PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.5
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.5
4.9.5 4.9.4 4.9.3 4.9.2 4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 All 203 releases
betterdocs / react-src / admin / quick-setup / fields / LayoutCustomizer.js

LayoutCustomizer.js in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.9.5, at react-src/admin/quick-setup/fields/LayoutCustomizer.js

147 lines 5.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import React from "react";
2 import { useState, useEffect } from "@wordpress/element";
3 import { IconCheck, IconArrowRight, Sparkle } from "../../sample-docs/icons";
4 import { setLayoutPick } from "./layoutStore";
5
6 /* Quick Setup "Customize" field — interactive layout picker.
7 Replaces the old Customizer.gif. Reads window.betterdocsCustomizeLayouts
8 (groups with preview thumbnails + current theme_mod values). Selections are
9 kept locally + in the shared store; they're persisted as theme_mods on
10 Finish (Launch), not on click. */
11
12 const cfg = () => (typeof window !== "undefined" && window.betterdocsCustomizeLayouts) || {};
13
14 function LayoutGroup({ group }) {
15 const [selected, setSelected] = useState(group.current);
16 const cols = group.cols || 4;
17
18 // Seed the shared store with the current value so Finish can save it.
19 useEffect(() => {
20 setLayoutPick(group.key, group.current);
21 }, []);
22
23 const choose = (value) => {
24 if (value === selected) return;
25 setSelected(value);
26 setLayoutPick(group.key, value);
27 };
28
29 return (
30 <section className="betterdocs-sd-layout-group">
31 <div className="betterdocs-sd-layout-group-head">
32 <h2 className="betterdocs-sd-layout-group-title">{group.title}</h2>
33 {group.hint && <span className="betterdocs-sd-layout-group-hint">{group.hint}</span>}
34 </div>
35 <div
36 className="betterdocs-sd-layout-grid"
37 style={{
38 gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))`,
39 maxWidth: cols <= 3 ? 760 : undefined,
40 }}
41 >
42 {group.options.map((opt) => (
43 <button
44 type="button"
45 key={opt.value}
46 className={`betterdocs-sd-layout-card${opt.value === selected ? " is-selected" : ""}`}
47 onClick={() => choose(opt.value)}
48 aria-pressed={opt.value === selected}
49 >
50 <span className="betterdocs-sd-layout-thumb">
51 <img src={opt.image} alt={opt.label} loading="lazy" />
52 {opt.value === selected && (
53 <span className="betterdocs-sd-layout-check">
54 <IconCheck size={14} sw={3} />
55 </span>
56 )}
57 </span>
58 <span className="betterdocs-sd-layout-name">{opt.label}</span>
59 </button>
60 ))}
61 </div>
62 </section>
63 );
64 }
65
66 /* Block (FSE) themes don't read the Customizer theme_mods the picker writes, so the
67 layout options are dead controls there. In their place, hand off to the Site Editor:
68 copy on the left, preview on the right, one primary action. Built from the same card
69 language as the Create Content step's AI banner (.betterdocs-sd-promo) so the wizard
70 reads as one flow. Classic themes never see this. */
71 function FseHandoff({ fse }) {
72 const features = Array.isArray(fse.features) ? fse.features : [];
73
74 return (
75 <div className="betterdocs-sd-fse">
76 <div className="betterdocs-sd-fse-body">
77 {/* The wizard's existing pill (same one the AI badge uses) — no bespoke variant. */}
78 {fse.eyebrow && (
79 <span className="betterdocs-sd-badge">
80 <Sparkle size={14} /> {fse.eyebrow}
81 </span>
82 )}
83 <h2 className="betterdocs-sd-fse-title">{fse.title}</h2>
84 <p className="betterdocs-sd-fse-text">{fse.description}</p>
85
86 {features.length > 0 && (
87 <ul className="betterdocs-sd-fse-list">
88 {features.map((item) => (
89 <li key={item}>
90 <span className="betterdocs-sd-fse-tick">
91 <IconCheck size={12} sw={3} />
92 </span>
93 {item}
94 </li>
95 ))}
96 </ul>
97 )}
98
99 {fse.url && (
100 <div className="betterdocs-sd-fse-action">
101 {/* New tab: the wizard is mid-flow, and navigating away would lose it. */}
102 <a
103 className="betterdocs-sd-fse-button"
104 href={fse.url}
105 target="_blank"
106 rel="noopener noreferrer"
107 >
108 {fse.button} <IconArrowRight size={15} />
109 </a>
110 {fse.note && <span className="betterdocs-sd-fse-note">{fse.note}</span>}
111 </div>
112 )}
113 </div>
114
115 {fse.image && (
116 <div className="betterdocs-sd-fse-preview">
117 <img src={fse.image} alt="" loading="lazy" />
118 </div>
119 )}
120 </div>
121 );
122 }
123
124 function LayoutCustomizer(props) {
125 const { groups, is_fse: isFse, fse } = cfg();
126
127 // The FSE hand-off is its own card, so it does NOT go inside .betterdocs-sd-layouts
128 // (that's the picker's frame — nesting produced a box inside a box).
129 if (isFse && fse) {
130 return <FseHandoff fse={fse} />;
131 }
132
133 if (!groups) {
134 return null;
135 }
136
137 return (
138 <div className={`betterdocs-sd-layouts ${props?.classes || ""}`}>
139 {Object.keys(groups).map((key) => (
140 <LayoutGroup key={key} group={groups[key]} />
141 ))}
142 </div>
143 );
144 }
145
146 export default LayoutCustomizer;
147