@rogieking/figui3 8.9.44 → 8.9.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,263 @@
1
+ # React + FigUI3
2
+
3
+ FigUI3 is web components. Use the tags in JSX. There is no React wrapper package.
4
+
5
+ Shared by `figui3`, `fig-editor`, and `fig-lab`. Per-tag recipes: [components.md](components.md).
6
+
7
+ ## Bootstrap
8
+
9
+ Register CSS + JS before the first render. Dynamic `import()` keeps bundlers from tree-shaking registration.
10
+
11
+ ```tsx
12
+ import { createRoot } from "react-dom/client";
13
+ import "@rogieking/figui3/fig.css";
14
+
15
+ const bootstrap = async () => {
16
+ await import("@rogieking/figui3/fig.js");
17
+ createRoot(document.getElementById("app")!).render(<App />);
18
+ };
19
+
20
+ bootstrap();
21
+ ```
22
+
23
+ Add editor and/or lab the same way, before render:
24
+
25
+ ```tsx
26
+ import "@rogieking/figui3/fig-editor.css";
27
+ await import("@rogieking/figui3/fig-editor.js");
28
+
29
+ import "@rogieking/figui3/fig-lab.css";
30
+ await import("@rogieking/figui3/fig-lab.js");
31
+ ```
32
+
33
+ SSR (Next/Remix/Astro): import the JS only on the client.
34
+
35
+ ## JSX types
36
+
37
+ ```ts
38
+ import "react";
39
+
40
+ type FigAttrs = React.DetailedHTMLProps<
41
+ React.HTMLAttributes<HTMLElement>,
42
+ HTMLElement
43
+ > & { [key: string]: unknown };
44
+
45
+ type FigTag =
46
+ | "fig-3d-rotate"
47
+ | "fig-ai-context"
48
+ | "fig-ai-prompt"
49
+ | "fig-attachment"
50
+ | "fig-attachments"
51
+ | "fig-avatar"
52
+ | "fig-button"
53
+ | "fig-button-combo"
54
+ | "fig-canvas-control"
55
+ | "fig-card"
56
+ | "fig-chat-message"
57
+ | "fig-checkbox"
58
+ | "fig-chit"
59
+ | "fig-choice"
60
+ | "fig-chooser"
61
+ | "fig-color-tip"
62
+ | "fig-combo-input"
63
+ | "fig-content"
64
+ | "fig-dropdown"
65
+ | "fig-easing-curve"
66
+ | "fig-field"
67
+ | "fig-fill-picker"
68
+ | "fig-footer"
69
+ | "fig-group"
70
+ | "fig-handle"
71
+ | "fig-header"
72
+ | "fig-icon"
73
+ | "fig-image"
74
+ | "fig-input-angle"
75
+ | "fig-input-color"
76
+ | "fig-input-combo"
77
+ | "fig-input-file"
78
+ | "fig-input-fill"
79
+ | "fig-input-gradient"
80
+ | "fig-input-number"
81
+ | "fig-input-palette"
82
+ | "fig-input-text"
83
+ | "fig-input-wheel"
84
+ | "fig-interpolation-swatch"
85
+ | "fig-joystick"
86
+ | "fig-layer"
87
+ | "fig-media"
88
+ | "fig-media-controls"
89
+ | "fig-menu"
90
+ | "fig-menu-item"
91
+ | "fig-menu-separator"
92
+ | "fig-options"
93
+ | "fig-origin-grid"
94
+ | "fig-preview"
95
+ | "fig-radio"
96
+ | "fig-reorder"
97
+ | "fig-segment"
98
+ | "fig-segmented-control"
99
+ | "fig-select"
100
+ | "fig-select-option"
101
+ | "fig-select-options"
102
+ | "fig-separator"
103
+ | "fig-shimmer"
104
+ | "fig-skeleton"
105
+ | "fig-slider"
106
+ | "fig-spinner"
107
+ | "fig-swatch"
108
+ | "fig-switch"
109
+ | "fig-tab"
110
+ | "fig-tab-content"
111
+ | "fig-tabs"
112
+ | "fig-tooltip"
113
+ | "fig-truncate"
114
+ | "fig-video"
115
+ | "propskit-color"
116
+ | "propskit-color-point"
117
+ | "propskit-fill"
118
+ | "propskit-gradient"
119
+ | "propskit-group"
120
+ | "propskit-number"
121
+ | "propskit-oscillator"
122
+ | "propskit-point-point"
123
+ | "propskit-point-radius"
124
+ | "propskit-point-radius-angle"
125
+ | "propskit-position"
126
+ | "propskit-select"
127
+ | "propskit-slider"
128
+ | "propskit-switch"
129
+ | "propskit-text"
130
+ | "propskit-wheel";
131
+
132
+ declare module "react" {
133
+ namespace JSX {
134
+ interface IntrinsicElements
135
+ extends Record<FigTag, FigAttrs> {
136
+ dialog: React.DetailedHTMLProps<
137
+ React.DialogHTMLAttributes<HTMLDialogElement>,
138
+ HTMLDialogElement
139
+ > & { is?: string; [key: string]: unknown };
140
+ }
141
+ }
142
+ }
143
+ ```
144
+
145
+ ## Host JSX
146
+
147
+ Render the custom elements as tags. Children are React nodes (`key` on lists). `className` works on `fig-*`, `propskit-*`, and `<dialog is="fig-…">`.
148
+
149
+ ```tsx
150
+ <fig-field direction="horizontal">
151
+ <label>Opacity</label>
152
+ <fig-slider
153
+ value={String(opacity)}
154
+ min="0"
155
+ max="100"
156
+ text="true"
157
+ units="%"
158
+ full
159
+ onInput={onInput}
160
+ onChange={onChange}
161
+ />
162
+ </fig-field>
163
+ ```
164
+
165
+ ## Attributes
166
+
167
+ - Strings: `text="true"`, `variant="ghost"`, `value={String(n)}`.
168
+ - Presence: `icon`, `full`, `compact`, `disabled` as boolean props; unset with `undefined`.
169
+ - Checked: `checked={on ? "true" : undefined}` — never `checked={false}`.
170
+ - Native dialog open: `open={open ? true : undefined}`.
171
+ - Optional flags / `data-*`: omit with `undefined`.
172
+
173
+ Passing `value` / `checked` as JSX props on re-render is OK. Components do not emit `input` from programmatic writes and ignore value writes during drag.
174
+
175
+ ## Events
176
+
177
+ Use React handlers:
178
+
179
+ - `onClick` — `fig-button` and other clickable hosts
180
+ - `onInput` — live (`fig-switch`, `fig-slider`, number/color/text)
181
+ - `onChange` — commit (`fig-options`, `fig-dropdown`, often paired with `onInput`)
182
+
183
+ Read order:
184
+
185
+ 1. `event.detail` (`.checked`, `.value`, or the detail payload)
186
+ 2. `event.currentTarget.value` / `.checked`
187
+ 3. `event.target.value`
188
+
189
+ ```tsx
190
+ function readValue(event: Event) {
191
+ const custom = event as CustomEvent<{ checked?: boolean; value?: unknown }>;
192
+ const host = event.currentTarget as HTMLElement & {
193
+ value?: unknown;
194
+ checked?: boolean;
195
+ };
196
+ if (typeof custom.detail?.checked === "boolean") return custom.detail.checked;
197
+ if (custom.detail?.value !== undefined) return custom.detail.value;
198
+ if (custom.detail !== undefined && custom.detail !== null) return custom.detail;
199
+ return host.value ?? host.checked;
200
+ }
201
+ ```
202
+
203
+ Native `addEventListener` only when React does not map the event: dialog `close` / `cancel`, delegated host clicks, imperative APIs.
204
+
205
+ Common events: `input`, `change`, `loaded`, `optionhover`, `reorder`, `remove`, `modeready`, `webcamstream`, `close`.
206
+
207
+ ## Customized built-ins
208
+
209
+ Do not use `<fig-dialog>`, `<fig-popup>`, or `<fig-toast>`. Use `<dialog is="…">`.
210
+
211
+ ```tsx
212
+ const buttonRef = useRef<HTMLElement>(null);
213
+ const popupRef = useRef<HTMLDialogElement>(null);
214
+
215
+ useEffect(() => {
216
+ const popup = popupRef.current;
217
+ if (!popup) return;
218
+ (popup as HTMLDialogElement & { anchor?: Element | null }).anchor =
219
+ buttonRef.current;
220
+ const onClose = () => setOpen(false);
221
+ popup.addEventListener("close", onClose);
222
+ return () => popup.removeEventListener("close", onClose);
223
+ }, []);
224
+
225
+ <dialog
226
+ ref={popupRef}
227
+ is="fig-popup"
228
+ variant="popover"
229
+ position="bottom"
230
+ offset="0 8"
231
+ className="preferences-popup"
232
+ open={open ? true : undefined}
233
+ >
234
+ <fig-header>Preferences</fig-header>
235
+ <fig-content>…</fig-content>
236
+ </dialog>
237
+ ```
238
+
239
+ Set `anchor` as an element on the ref after mount (selector strings also work).
240
+
241
+ ## Imperative refs
242
+
243
+ `useRef<HTMLElement>(null)` on hosts. Tooltips:
244
+
245
+ ```tsx
246
+ tooltip.text = "Copied";
247
+ tooltip.showPopup?.();
248
+ tooltip.hidePopup?.();
249
+ ```
250
+
251
+ Toasts: `toast.showToast()`. Lab reset: `host.resetToDefault()`.
252
+
253
+ ## Light DOM
254
+
255
+ React owns child nodes. Components slot; they do not relocate children.
256
+
257
+ - `fig-menu` items; trigger gets `slot="trigger"`
258
+ - `fig-select-options slot="panel"`
259
+ - `fig-tabs` / `fig-chooser` children
260
+ - media `slot="overlay"`
261
+ - fill custom `slot="mode-*"`
262
+
263
+ Do not `innerHTML` a tree React rendered. Do not reparent nodes React created. Children may arrive after `connectedCallback`; hosts already watch for that.
@@ -1,7 +1,6 @@
1
1
  # FigUI3 core API reference
2
2
 
3
- Source of truth for live attrs: `playground/src/lib/attributeRules.ts` and `README.md`.
4
- Playground: `/figui3#{section}`.
3
+ Public API: `README.md`. React recipes: [components.md](components.md).
5
4
 
6
5
  ## Option string formats
7
6
 
@@ -84,8 +83,8 @@ import "@rogieking/figui3/fig-layer.css";
84
83
  import "@rogieking/figui3/fig-layer.js";
85
84
  ```
86
85
 
87
- Attrs: `open`, `visible`, `disabled`. Markup: child `.fig-layer-row` plus nested `fig-layer`. Events: `openchange`, `visibilitychange`. Playground: `/figui3#layer` (behind Full editor).
86
+ Attrs: `open`, `visible`, `disabled`. Markup: child `.fig-layer-row` plus nested `fig-layer`. Events: `openchange`, `visibilitychange`.
88
87
 
89
88
  ## Native elements
90
89
 
91
- `/figui3` also demos styled native `button`, `select`, `input`, `textarea`, `checkbox`, `.switch`, `radio`, `color`, `progress`, `fieldset`, `details`, `hr` under group “Native elements”. Use these when a custom element is unnecessary.
90
+ Styled native `button`, `select`, `input`, `textarea`, `checkbox`, `.switch`, `radio`, `color`, `progress`, `fieldset`, `details`, `hr` when a custom element is unnecessary.
@@ -1,10 +1,9 @@
1
1
  ---
2
2
  name: propkit
3
3
  description: >-
4
- Guides Figma-style property panel composition in the /propskit playground using
5
- fig-field rows and FigUI3 controls. Use when building or editing
6
- playground/src/data/sections.ts, generating field prompts, or choosing between
7
- raw fig-* rows and propskit-* wrappers (fig-lab).
4
+ Guides Figma-style property panel composition using fig-field rows and FigUI3
5
+ controls, including React JSX. Use when building labeled property panels or
6
+ choosing between raw fig-* rows and propskit-* wrappers (fig-lab).
8
7
  user-invocable: false
9
8
  ---
10
9
 
@@ -12,54 +11,47 @@ user-invocable: false
12
11
 
13
12
  Patterns for Figma property panels. Two layers:
14
13
 
15
- | Surface | Route | What to use |
16
- |---|---|---|
17
- | **PropsKit playground** | `/propskit` | Horizontal `fig-field` + core `fig-*` |
18
- | **Lab wrappers** | `/propskit/lab` | `propskit-*` (see `fig-lab` skill) |
14
+ | Surface | What to use |
15
+ |---|---|
16
+ | **Core rows** | Horizontal `fig-field` + core `fig-*` |
17
+ | **Lab wrappers** | `propskit-*` (see `fig-lab` skill) |
19
18
 
20
- Canonical `/propskit` examples: `playground/src/data/sections.ts`.
21
- Core control APIs: `figui3` skill. Select/fill picker: `fig-editor`. Labeled wrappers: `fig-lab`.
19
+ Core tags: `figui3` skill + [../figui3/components.md](../figui3/components.md). React: [../figui3/react.md](../figui3/react.md). Select/fill picker: `fig-editor`. Labeled wrappers: `fig-lab`.
22
20
 
23
21
  ## Principles
24
22
 
25
23
  1. Default to horizontal `fig-field` rows.
26
24
  2. One concise label per control.
27
25
  3. For new labeled property controls in lab, prefer `propskit-*` over duplicating field chrome.
28
- 4. In `/propskit` demos, keep composing from `fig-*` so examples stay core-only unless the section needs lab.
29
- 5. Panel width ~240px. Match existing section density.
30
-
31
- ## React bootstrap
32
-
33
- ```tsx
34
- import "@rogieking/figui3/fig.css";
35
-
36
- const bootstrap = async () => {
37
- await import("@rogieking/figui3/fig.js");
38
- createRoot(document.getElementById("app")!).render(<App />);
39
- };
40
- bootstrap();
41
- ```
42
-
43
- Add `fig-editor` when using `fig-select` / fill picker. Add `fig-lab` when using `propskit-*`.
44
-
45
- On `fig-*` and `<dialog is="fig-...">`, use `class` not `className`.
26
+ 4. Compose from `fig-*` unless the row needs lab wrappers.
27
+ 5. Panel width ~240px. Keep density tight.
46
28
 
47
29
  ## Field composition
48
30
 
49
- ```html
31
+ ```tsx
50
32
  <fig-field direction="horizontal">
51
33
  <label>Opacity</label>
52
- <fig-slider value="75" min="0" max="100" text="true" units="%" full></fig-slider>
34
+ <fig-slider
35
+ value={String(opacity)}
36
+ min="0"
37
+ max="100"
38
+ text="true"
39
+ units="%"
40
+ full
41
+ onInput={onInput}
42
+ onChange={onChange}
43
+ />
53
44
  </fig-field>
54
45
  ```
55
46
 
56
47
  - Put control attrs on the control, not a wrapper.
57
48
  - Use `full` when the control should stretch.
58
49
  - Do not mix unrelated controls in one row unless grouped on purpose.
50
+ - Add `fig-editor` when using `fig-select` / fill picker. Add `fig-lab` when using `propskit-*`.
59
51
 
60
52
  ## Control heuristics
61
53
 
62
- | Intent | `/propskit` (core) | Lab wrapper |
54
+ | Intent | Core | Lab wrapper |
63
55
  |---|---|---|
64
56
  | Boolean | `fig-switch` | `propskit-switch` |
65
57
  | Continuous number | `fig-slider` | `propskit-slider` |
@@ -100,7 +92,5 @@ Use a horizontal fig-field, with a fig-slider, min=0 max=100 text=true units=%.
100
92
  1. Identify intent (boolean, discrete, continuous, color/fill, media, motion).
101
93
  2. Pick core vs lab wrapper.
102
94
  3. Compose the row; set defaults explicitly.
103
- 4. Check `/propskit` or `/propskit/lab` for an existing example before inventing markup.
104
- 5. Verify `input`/`change` and keyboard.
105
-
106
- Primary files: `playground/src/data/sections.ts`, `playground/src/data/labSections.ts`, `fig.js`, `fig-lab.js`.
95
+ 4. Wire `onInput` (live) and `onChange` (commit).
96
+ 5. Verify keyboard.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "8.9.44",
3
+ "version": "8.9.45",
4
4
  "description": "A lightweight web components library for building Figma plugin and widget UIs with native look and feel",
5
5
  "author": "Rogie King",
6
6
  "license": "SEE LICENSE IN LICENSE",