@pixerate/editor-react 0.16.0 → 0.17.0

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.
Files changed (46) hide show
  1. package/README.md +2 -2
  2. package/dist/canvas/index.cjs +406 -142
  3. package/dist/canvas/index.cjs.map +1 -1
  4. package/dist/canvas/index.d.cts +66 -3
  5. package/dist/canvas/index.d.ts +66 -3
  6. package/dist/canvas/index.js +277 -0
  7. package/dist/canvas/index.js.map +1 -1
  8. package/dist/chunk-I3E4KSA2.js +515 -0
  9. package/dist/chunk-I3E4KSA2.js.map +1 -0
  10. package/dist/chunk-JR5ZBDR7.js +2012 -0
  11. package/dist/chunk-JR5ZBDR7.js.map +1 -0
  12. package/dist/{chunk-XTFR3IMK.js → chunk-ZU54BMDC.js} +473 -160
  13. package/dist/chunk-ZU54BMDC.js.map +1 -0
  14. package/dist/generation/index.cjs +290 -0
  15. package/dist/generation/index.cjs.map +1 -1
  16. package/dist/generation/index.d.cts +66 -2
  17. package/dist/generation/index.d.ts +66 -2
  18. package/dist/generation/index.js +7 -1
  19. package/dist/image-editor/index.cjs +490 -187
  20. package/dist/image-editor/index.cjs.map +1 -1
  21. package/dist/image-editor/index.d.cts +34 -2
  22. package/dist/image-editor/index.d.ts +34 -2
  23. package/dist/image-editor/index.js +5 -1
  24. package/dist/index.cjs +2372 -731
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +88 -6
  27. package/dist/index.d.ts +88 -6
  28. package/dist/index.js +334 -96
  29. package/dist/index.js.map +1 -1
  30. package/dist/spreadsheet/index.cjs +943 -112
  31. package/dist/spreadsheet/index.cjs.map +1 -1
  32. package/dist/spreadsheet/index.d.cts +119 -7
  33. package/dist/spreadsheet/index.d.ts +119 -7
  34. package/dist/spreadsheet/index.js +11 -1
  35. package/dist/tokens/index.cjs +395 -0
  36. package/dist/tokens/index.cjs.map +1 -0
  37. package/dist/tokens/index.d.cts +54 -0
  38. package/dist/tokens/index.d.ts +54 -0
  39. package/dist/tokens/index.js +375 -0
  40. package/dist/tokens/index.js.map +1 -0
  41. package/package.json +10 -2
  42. package/dist/chunk-7SPRQZ6H.js +0 -216
  43. package/dist/chunk-7SPRQZ6H.js.map +0 -1
  44. package/dist/chunk-FYIPXHAE.js +0 -1169
  45. package/dist/chunk-FYIPXHAE.js.map +0 -1
  46. package/dist/chunk-XTFR3IMK.js.map +0 -1
@@ -0,0 +1,395 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/tokens/index.ts
21
+ var tokens_exports = {};
22
+ __export(tokens_exports, {
23
+ TokenSwatch: () => TokenSwatch,
24
+ TokensPanel: () => TokensPanel,
25
+ useTokenState: () => useTokenState,
26
+ useTokenStore: () => useTokenStore,
27
+ useTokens: () => useTokens
28
+ });
29
+ module.exports = __toCommonJS(tokens_exports);
30
+
31
+ // src/tokens/useTokens.ts
32
+ var import_react = require("react");
33
+ var import_tokens = require("@pixerate/editor/tokens");
34
+ function useTokenStore(options) {
35
+ const ref = (0, import_react.useRef)(null);
36
+ if (!ref.current) ref.current = new import_tokens.TokenStore(options);
37
+ return ref.current;
38
+ }
39
+ function useTokenState(store) {
40
+ return (0, import_react.useSyncExternalStore)(store.subscribe, store.getState, store.getState);
41
+ }
42
+ function useTokens(store, options = {}) {
43
+ const state = useTokenState(store);
44
+ const modesKey = JSON.stringify(options.modes ?? {});
45
+ const cssKey = JSON.stringify(options.css ?? {}, (_k, v) => typeof v === "function" ? String(v) : v);
46
+ const cssVariables = (0, import_react.useMemo)(
47
+ () => (0, import_tokens.getTokenCssVariables)(state, options.modes, options.css),
48
+ // eslint-disable-next-line react-hooks/exhaustive-deps
49
+ [state, modesKey, cssKey]
50
+ );
51
+ const css = (0, import_react.useMemo)(() => (0, import_tokens.exportTokensCss)(state, options.css), [state, cssKey]);
52
+ const undo = (0, import_react.useCallback)(() => void store.undo(), [store]);
53
+ const redo = (0, import_react.useCallback)(() => void store.redo(), [store]);
54
+ return { store, state, cssVariables, css, canUndo: store.canUndo(), canRedo: store.canRedo(), undo, redo };
55
+ }
56
+
57
+ // src/tokens/TokensPanel.tsx
58
+ var import_react2 = require("react");
59
+ var import_tokens2 = require("@pixerate/editor/tokens");
60
+ var import_jsx_runtime = require("react/jsx-runtime");
61
+ var input = "w-full min-w-0 rounded border border-border bg-background px-1.5 py-0.5 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-primary";
62
+ var button = "rounded border border-border px-2 py-0.5 text-xs hover:bg-muted disabled:opacity-40";
63
+ function CommitInput({
64
+ value,
65
+ onCommit,
66
+ ...rest
67
+ }) {
68
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
69
+ "input",
70
+ {
71
+ defaultValue: value,
72
+ className: input,
73
+ onBlur: (e) => {
74
+ if (e.currentTarget.value !== value) onCommit(e.currentTarget.value);
75
+ },
76
+ onKeyDown: (e) => {
77
+ if (e.key === "Enter") e.currentTarget.blur();
78
+ if (e.key === "Escape") {
79
+ e.currentTarget.value = value;
80
+ e.currentTarget.blur();
81
+ }
82
+ },
83
+ ...rest
84
+ },
85
+ value
86
+ );
87
+ }
88
+ function TokenSwatch({ color, className = "" }) {
89
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
90
+ "span",
91
+ {
92
+ "data-token-swatch": color,
93
+ className: `inline-block h-4 w-4 shrink-0 rounded border border-border ${className}`,
94
+ style: { background: `linear-gradient(${color}, ${color}), repeating-conic-gradient(#ccc 0 25%, #fff 0 50%) 0 0 / 8px 8px` },
95
+ "aria-hidden": "true"
96
+ }
97
+ );
98
+ }
99
+ function LiteralEditor({
100
+ variable,
101
+ value,
102
+ onSet
103
+ }) {
104
+ switch (variable.type) {
105
+ case "color":
106
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "flex items-center gap-1", children: [
107
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "relative", children: [
108
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TokenSwatch, { color: String(value) }),
109
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
110
+ "input",
111
+ {
112
+ type: "color",
113
+ "aria-label": `${variable.name} colour`,
114
+ className: "absolute inset-0 h-full w-full cursor-pointer opacity-0",
115
+ value: String(value).slice(0, 7),
116
+ onChange: (e) => onSet(e.currentTarget.value + String(value).slice(7))
117
+ }
118
+ )
119
+ ] }),
120
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommitInput, { value: String(value), "aria-label": `${variable.name} value`, onCommit: onSet })
121
+ ] });
122
+ case "number":
123
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommitInput, { value: String(value), inputMode: "decimal", "aria-label": `${variable.name} value`, onCommit: onSet });
124
+ case "string":
125
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommitInput, { value: String(value), "aria-label": `${variable.name} value`, onCommit: onSet });
126
+ case "boolean":
127
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
128
+ "input",
129
+ {
130
+ type: "checkbox",
131
+ "aria-label": `${variable.name} value`,
132
+ checked: value === true,
133
+ onChange: (e) => onSet(e.currentTarget.checked)
134
+ }
135
+ );
136
+ }
137
+ }
138
+ function ValueCell({
139
+ state,
140
+ variable,
141
+ mode,
142
+ onSet
143
+ }) {
144
+ const value = variable.valuesByMode[mode.id];
145
+ const alias = (0, import_tokens2.isTokenAlias)(value) ? value.alias : "";
146
+ const resolved = (0, import_tokens2.resolveTokenInMode)(state, variable.id, mode.id);
147
+ const candidates = state.variables.filter((v) => v.type === variable.type && v.id !== variable.id);
148
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "px-1 py-1 align-middle", "data-variable-id": variable.id, "data-mode-id": mode.id, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-1", children: [
149
+ alias ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "flex min-w-0 flex-1 items-center gap-1 text-xs", "data-token-alias": alias, children: [
150
+ variable.type === "color" && resolved !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TokenSwatch, { color: String(resolved) }) : null,
151
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "truncate", children: [
152
+ "\u2192 ",
153
+ (0, import_tokens2.getTokenVariable)(state, alias)?.name ?? alias
154
+ ] })
155
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "min-w-0 flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LiteralEditor, { variable, value, onSet }) }),
156
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
157
+ "select",
158
+ {
159
+ "aria-label": `${variable.name} alias in ${mode.name}`,
160
+ className: "w-5 shrink-0 rounded border border-border bg-background text-xs",
161
+ title: "Alias",
162
+ value: alias,
163
+ onChange: (e) => {
164
+ const target = e.currentTarget.value;
165
+ onSet(target ? { alias: target } : resolved ?? void 0);
166
+ },
167
+ children: [
168
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: "Value" }),
169
+ candidates.map((v) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("option", { value: v.id, children: [
170
+ "\u2192 ",
171
+ v.name
172
+ ] }, v.id))
173
+ ]
174
+ }
175
+ )
176
+ ] }) });
177
+ }
178
+ function TokensPanel({ store, collectionId, onCollectionChange, onError, className = "" }) {
179
+ const state = useTokenState(store);
180
+ const [ownSelection, setOwnSelection] = (0, import_react2.useState)(void 0);
181
+ const [error, setError] = (0, import_react2.useState)(null);
182
+ const [newCollection, setNewCollection] = (0, import_react2.useState)("");
183
+ const [newMode, setNewMode] = (0, import_react2.useState)("");
184
+ const [newName, setNewName] = (0, import_react2.useState)("");
185
+ const [newType, setNewType] = (0, import_react2.useState)("color");
186
+ const selectedId = collectionId ?? ownSelection;
187
+ const collection = state.collections.find((c) => c.id === selectedId) ?? state.collections[0];
188
+ const select = (id) => {
189
+ setOwnSelection(id);
190
+ onCollectionChange?.(id);
191
+ };
192
+ const run = (fn) => {
193
+ try {
194
+ fn();
195
+ setError(null);
196
+ } catch (e) {
197
+ const err = e;
198
+ setError(err instanceof import_tokens2.TokenError ? err.message : String(err));
199
+ onError?.(err);
200
+ }
201
+ };
202
+ const variables = collection ? (0, import_tokens2.getCollectionVariables)(state, collection.id) : [];
203
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-tokens-panel": "", className: `flex flex-col gap-2 text-xs text-foreground ${className}`, children: [
204
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
205
+ state.collections.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
206
+ "select",
207
+ {
208
+ "aria-label": "Collection",
209
+ className: "rounded border border-border bg-background px-1.5 py-0.5 text-xs",
210
+ value: collection?.id ?? "",
211
+ onChange: (e) => select(e.currentTarget.value),
212
+ children: state.collections.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: c.id, children: c.name }, c.id))
213
+ }
214
+ ) : null,
215
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
216
+ "form",
217
+ {
218
+ className: "flex items-center gap-1",
219
+ onSubmit: (e) => {
220
+ e.preventDefault();
221
+ run(() => {
222
+ const created = store.createCollection(newCollection, ["Light", "Dark"]);
223
+ setNewCollection("");
224
+ select(created.id);
225
+ });
226
+ },
227
+ children: [
228
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
229
+ "input",
230
+ {
231
+ className: input,
232
+ placeholder: "New collection",
233
+ "aria-label": "New collection name",
234
+ value: newCollection,
235
+ onChange: (e) => setNewCollection(e.currentTarget.value)
236
+ }
237
+ ),
238
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "submit", className: button, disabled: !newCollection.trim(), children: "Add" })
239
+ ]
240
+ }
241
+ ),
242
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "ml-auto flex gap-1", children: [
243
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: button, disabled: !store.canUndo(), onClick: () => store.undo(), "aria-label": "Undo token edit", children: "Undo" }),
244
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: button, disabled: !store.canRedo(), onClick: () => store.redo(), "aria-label": "Redo token edit", children: "Redo" })
245
+ ] })
246
+ ] }),
247
+ error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { role: "alert", className: "rounded border border-red-500/40 bg-red-500/10 px-2 py-1 text-red-500", children: error }) : null,
248
+ collection ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
249
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-1", children: [
250
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommitInput, { value: collection.name, "aria-label": "Collection name", onCommit: (name) => run(() => store.renameCollection(collection.id, name)) }),
251
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: button, onClick: () => run(() => store.deleteCollection(collection.id)), "aria-label": `Delete collection ${collection.name}`, children: "Delete" })
252
+ ] }),
253
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "overflow-x-auto", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { className: "w-full border-collapse", children: [
254
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { className: "border-b border-border text-left text-muted-foreground", children: [
255
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: "px-1 py-1 font-medium", children: "Name" }),
256
+ collection.modes.map((mode, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: "px-1 py-1 font-medium", "data-mode-id": mode.id, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-1", children: [
257
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
258
+ CommitInput,
259
+ {
260
+ value: mode.name,
261
+ "aria-label": `Mode ${mode.name} name`,
262
+ onCommit: (name) => run(() => store.renameMode(collection.id, mode.id, name))
263
+ }
264
+ ),
265
+ index === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { title: "Default mode", className: "text-[10px] uppercase", children: "default" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
266
+ "button",
267
+ {
268
+ type: "button",
269
+ className: "px-1 hover:text-foreground",
270
+ title: "Make default",
271
+ "aria-label": `Make ${mode.name} the default mode`,
272
+ onClick: () => run(() => store.setDefaultMode(collection.id, mode.id)),
273
+ children: "\u2605"
274
+ }
275
+ ),
276
+ collection.modes.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
277
+ "button",
278
+ {
279
+ type: "button",
280
+ className: "px-1 hover:text-foreground",
281
+ "aria-label": `Remove mode ${mode.name}`,
282
+ onClick: () => run(() => store.removeMode(collection.id, mode.id)),
283
+ children: "\xD7"
284
+ }
285
+ ) : null
286
+ ] }) }, mode.id)),
287
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: "px-1 py-1", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
288
+ "form",
289
+ {
290
+ className: "flex items-center gap-1",
291
+ onSubmit: (e) => {
292
+ e.preventDefault();
293
+ run(() => {
294
+ store.addMode(collection.id, newMode);
295
+ setNewMode("");
296
+ });
297
+ },
298
+ children: [
299
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
300
+ "input",
301
+ {
302
+ className: input,
303
+ placeholder: "Mode",
304
+ "aria-label": "New mode name",
305
+ value: newMode,
306
+ onChange: (e) => setNewMode(e.currentTarget.value)
307
+ }
308
+ ),
309
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "submit", className: button, disabled: !newMode.trim(), "aria-label": "Add mode", children: "+" })
310
+ ]
311
+ }
312
+ ) })
313
+ ] }) }),
314
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: variables.map((variable) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { className: "border-b border-border/50", "data-token-row": variable.id, children: [
315
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "px-1 py-1", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-1", children: [
316
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "w-4 text-center text-muted-foreground", title: variable.type, children: variable.type === "color" ? "\u25D0" : variable.type === "number" ? "#" : variable.type === "string" ? "T" : "\u2298" }),
317
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
318
+ CommitInput,
319
+ {
320
+ value: variable.name,
321
+ "aria-label": `Variable ${variable.name} name`,
322
+ onCommit: (name) => run(() => store.renameVariable(variable.id, name))
323
+ }
324
+ )
325
+ ] }) }),
326
+ collection.modes.map((mode) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
327
+ ValueCell,
328
+ {
329
+ state,
330
+ variable,
331
+ mode,
332
+ onSet: (value) => run(() => store.setValue(variable.id, value, mode.id))
333
+ },
334
+ mode.id
335
+ )),
336
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "px-1 py-1 text-right", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
337
+ "button",
338
+ {
339
+ type: "button",
340
+ className: "px-1 text-muted-foreground hover:text-foreground",
341
+ "aria-label": `Delete variable ${variable.name}`,
342
+ onClick: () => run(() => store.deleteVariables(variable.id)),
343
+ children: "\xD7"
344
+ }
345
+ ) })
346
+ ] }, variable.id)) })
347
+ ] }) }),
348
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
349
+ "form",
350
+ {
351
+ className: "flex items-center gap-1",
352
+ onSubmit: (e) => {
353
+ e.preventDefault();
354
+ run(() => {
355
+ store.createVariable({ collectionId: collection.id, name: newName, type: newType });
356
+ setNewName("");
357
+ });
358
+ },
359
+ children: [
360
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
361
+ "input",
362
+ {
363
+ className: input,
364
+ placeholder: "color/bg",
365
+ "aria-label": "New variable name",
366
+ value: newName,
367
+ onChange: (e) => setNewName(e.currentTarget.value)
368
+ }
369
+ ),
370
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
371
+ "select",
372
+ {
373
+ "aria-label": "New variable type",
374
+ className: "rounded border border-border bg-background px-1 py-0.5 text-xs",
375
+ value: newType,
376
+ onChange: (e) => setNewType(e.currentTarget.value),
377
+ children: import_tokens2.TOKEN_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: t, children: t }, t))
378
+ }
379
+ ),
380
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "submit", className: button, disabled: !newName.trim(), children: "Add variable" })
381
+ ]
382
+ }
383
+ )
384
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-muted-foreground", children: "No collections yet." })
385
+ ] });
386
+ }
387
+ // Annotate the CommonJS export names for ESM import in node:
388
+ 0 && (module.exports = {
389
+ TokenSwatch,
390
+ TokensPanel,
391
+ useTokenState,
392
+ useTokenStore,
393
+ useTokens
394
+ });
395
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/tokens/index.ts","../../src/tokens/useTokens.ts","../../src/tokens/TokensPanel.tsx"],"sourcesContent":["export * from './useTokens';\nexport * from './TokensPanel';\n","import { useCallback, useMemo, useRef, useSyncExternalStore } from 'react';\nimport {\n TokenStore,\n exportTokensCss,\n getTokenCssVariables,\n type TokenCssOptions,\n type TokenModeContext,\n type TokenState,\n type TokenStoreOptions,\n} from '@pixerate/editor/tokens';\n\n/** A `TokenStore` created once for the component's lifetime. */\nexport function useTokenStore(options?: TokenStoreOptions): TokenStore {\n const ref = useRef<TokenStore | null>(null);\n if (!ref.current) ref.current = new TokenStore(options);\n return ref.current;\n}\n\n/** The store's current state (re-renders on every change). */\nexport function useTokenState(store: TokenStore): TokenState {\n return useSyncExternalStore(store.subscribe, store.getState, store.getState);\n}\n\nexport interface UseTokensOptions {\n /** Modes for `cssVariables` (collection id → mode id; default modes otherwise). */\n modes?: TokenModeContext;\n /** Options for `css` and `cssVariables` (prefix, attribute, …). */\n css?: TokenCssOptions;\n}\n\nexport interface UseTokensResult {\n store: TokenStore;\n state: TokenState;\n /** Resolved custom properties for `modes`, e.g. `<div style={cssVariables}>`. */\n cssVariables: Record<string, string>;\n /** A stylesheet with every mode (`:root { … } :root[data-mode=\"dark\"] { … }`). */\n css: string;\n canUndo: boolean;\n canRedo: boolean;\n undo: () => void;\n redo: () => void;\n}\n\n/**\n * Subscribes to a token store and exposes its state plus CSS custom properties for app\n * theming: apply `cssVariables` as an inline style, or inject `css` and switch modes with the\n * `data-mode` attribute.\n */\nexport function useTokens(store: TokenStore, options: UseTokensOptions = {}): UseTokensResult {\n const state = useTokenState(store);\n const modesKey = JSON.stringify(options.modes ?? {});\n const cssKey = JSON.stringify(options.css ?? {}, (_k, v) => (typeof v === 'function' ? String(v) : v));\n const cssVariables = useMemo(\n () => getTokenCssVariables(state, options.modes, options.css),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [state, modesKey, cssKey],\n );\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const css = useMemo(() => exportTokensCss(state, options.css), [state, cssKey]);\n const undo = useCallback(() => void store.undo(), [store]);\n const redo = useCallback(() => void store.redo(), [store]);\n return { store, state, cssVariables, css, canUndo: store.canUndo(), canRedo: store.canRedo(), undo, redo };\n}\n","import React, { useState } from 'react';\nimport {\n TOKEN_TYPES,\n TokenError,\n getCollectionVariables,\n getTokenVariable,\n isTokenAlias,\n resolveTokenInMode,\n type TokenCollection,\n type TokenLiteral,\n type TokenMode,\n type TokenState,\n type TokenStore,\n type TokenType,\n type TokenVariable,\n} from '@pixerate/editor/tokens';\nimport { useTokenState } from './useTokens';\n\nexport interface TokensPanelProps {\n store: TokenStore;\n /** Selected collection (controlled); defaults to the first. */\n collectionId?: string;\n onCollectionChange?: (collectionId: string) => void;\n /** Called with refused edits (also shown in the panel). */\n onError?: (error: Error) => void;\n className?: string;\n}\n\nconst input =\n 'w-full min-w-0 rounded border border-border bg-background px-1.5 py-0.5 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-primary';\nconst button = 'rounded border border-border px-2 py-0.5 text-xs hover:bg-muted disabled:opacity-40';\n\n/** Commits on blur or Enter; resets when `value` changes from outside. */\nfunction CommitInput({\n value,\n onCommit,\n ...rest\n}: { value: string; onCommit: (value: string) => void } & Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange'>) {\n return (\n <input\n key={value}\n defaultValue={value}\n className={input}\n onBlur={(e) => {\n if (e.currentTarget.value !== value) onCommit(e.currentTarget.value);\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter') e.currentTarget.blur();\n if (e.key === 'Escape') {\n e.currentTarget.value = value;\n e.currentTarget.blur();\n }\n }}\n {...rest}\n />\n );\n}\n\n/** A colour swatch (checkered under translucent colours). */\nexport function TokenSwatch({ color, className = '' }: { color: string; className?: string }) {\n return (\n <span\n data-token-swatch={color}\n className={`inline-block h-4 w-4 shrink-0 rounded border border-border ${className}`}\n style={{ background: `linear-gradient(${color}, ${color}), repeating-conic-gradient(#ccc 0 25%, #fff 0 50%) 0 0 / 8px 8px` }}\n aria-hidden=\"true\"\n />\n );\n}\n\nfunction LiteralEditor({\n variable,\n value,\n onSet,\n}: {\n variable: TokenVariable;\n value: TokenLiteral;\n onSet: (value: unknown) => void;\n}) {\n switch (variable.type) {\n case 'color':\n return (\n <span className=\"flex items-center gap-1\">\n <label className=\"relative\">\n <TokenSwatch color={String(value)} />\n <input\n type=\"color\"\n aria-label={`${variable.name} colour`}\n className=\"absolute inset-0 h-full w-full cursor-pointer opacity-0\"\n value={String(value).slice(0, 7)}\n onChange={(e) => onSet(e.currentTarget.value + String(value).slice(7))}\n />\n </label>\n <CommitInput value={String(value)} aria-label={`${variable.name} value`} onCommit={onSet} />\n </span>\n );\n case 'number':\n return <CommitInput value={String(value)} inputMode=\"decimal\" aria-label={`${variable.name} value`} onCommit={onSet} />;\n case 'string':\n return <CommitInput value={String(value)} aria-label={`${variable.name} value`} onCommit={onSet} />;\n case 'boolean':\n return (\n <input\n type=\"checkbox\"\n aria-label={`${variable.name} value`}\n checked={value === true}\n onChange={(e) => onSet(e.currentTarget.checked)}\n />\n );\n }\n}\n\nfunction ValueCell({\n state,\n variable,\n mode,\n onSet,\n}: {\n state: TokenState;\n variable: TokenVariable;\n mode: TokenMode;\n onSet: (value: unknown) => void;\n}) {\n const value = variable.valuesByMode[mode.id];\n const alias = isTokenAlias(value) ? value.alias : '';\n const resolved = resolveTokenInMode(state, variable.id, mode.id);\n const candidates = state.variables.filter((v) => v.type === variable.type && v.id !== variable.id);\n return (\n <td className=\"px-1 py-1 align-middle\" data-variable-id={variable.id} data-mode-id={mode.id}>\n <div className=\"flex items-center gap-1\">\n {alias ? (\n <span className=\"flex min-w-0 flex-1 items-center gap-1 text-xs\" data-token-alias={alias}>\n {variable.type === 'color' && resolved !== undefined ? <TokenSwatch color={String(resolved)} /> : null}\n <span className=\"truncate\">→ {getTokenVariable(state, alias)?.name ?? alias}</span>\n </span>\n ) : (\n <span className=\"min-w-0 flex-1\">\n <LiteralEditor variable={variable} value={value as TokenLiteral} onSet={onSet} />\n </span>\n )}\n <select\n aria-label={`${variable.name} alias in ${mode.name}`}\n className=\"w-5 shrink-0 rounded border border-border bg-background text-xs\"\n title=\"Alias\"\n value={alias}\n onChange={(e) => {\n const target = e.currentTarget.value;\n // \"Value\" detaches the alias to what it resolves to.\n onSet(target ? { alias: target } : resolved ?? undefined);\n }}\n >\n <option value=\"\">Value</option>\n {candidates.map((v) => (\n <option key={v.id} value={v.id}>\n → {v.name}\n </option>\n ))}\n </select>\n </div>\n </td>\n );\n}\n\n/**\n * Figma-style variables panel: collections, modes as columns, typed values with colour\n * swatches, aliases (→), add / rename / delete, undo / redo. Edits go through the store, so\n * each is one undo step; refused edits (cycles, duplicate names) show as an alert.\n */\nexport function TokensPanel({ store, collectionId, onCollectionChange, onError, className = '' }: TokensPanelProps) {\n const state = useTokenState(store);\n const [ownSelection, setOwnSelection] = useState<string | undefined>(undefined);\n const [error, setError] = useState<string | null>(null);\n const [newCollection, setNewCollection] = useState('');\n const [newMode, setNewMode] = useState('');\n const [newName, setNewName] = useState('');\n const [newType, setNewType] = useState<TokenType>('color');\n\n const selectedId = collectionId ?? ownSelection;\n const collection: TokenCollection | undefined = state.collections.find((c) => c.id === selectedId) ?? state.collections[0];\n const select = (id: string) => {\n setOwnSelection(id);\n onCollectionChange?.(id);\n };\n const run = (fn: () => void) => {\n try {\n fn();\n setError(null);\n } catch (e) {\n const err = e as Error;\n setError(err instanceof TokenError ? err.message : String(err));\n onError?.(err);\n }\n };\n const variables = collection ? getCollectionVariables(state, collection.id) : [];\n\n return (\n <div data-tokens-panel=\"\" className={`flex flex-col gap-2 text-xs text-foreground ${className}`}>\n <div className=\"flex flex-wrap items-center gap-2\">\n {state.collections.length > 0 ? (\n <select\n aria-label=\"Collection\"\n className=\"rounded border border-border bg-background px-1.5 py-0.5 text-xs\"\n value={collection?.id ?? ''}\n onChange={(e) => select(e.currentTarget.value)}\n >\n {state.collections.map((c) => (\n <option key={c.id} value={c.id}>\n {c.name}\n </option>\n ))}\n </select>\n ) : null}\n <form\n className=\"flex items-center gap-1\"\n onSubmit={(e) => {\n e.preventDefault();\n run(() => {\n const created = store.createCollection(newCollection, ['Light', 'Dark']);\n setNewCollection('');\n select(created.id);\n });\n }}\n >\n <input\n className={input}\n placeholder=\"New collection\"\n aria-label=\"New collection name\"\n value={newCollection}\n onChange={(e) => setNewCollection(e.currentTarget.value)}\n />\n <button type=\"submit\" className={button} disabled={!newCollection.trim()}>\n Add\n </button>\n </form>\n <span className=\"ml-auto flex gap-1\">\n <button type=\"button\" className={button} disabled={!store.canUndo()} onClick={() => store.undo()} aria-label=\"Undo token edit\">\n Undo\n </button>\n <button type=\"button\" className={button} disabled={!store.canRedo()} onClick={() => store.redo()} aria-label=\"Redo token edit\">\n Redo\n </button>\n </span>\n </div>\n\n {error ? (\n <div role=\"alert\" className=\"rounded border border-red-500/40 bg-red-500/10 px-2 py-1 text-red-500\">\n {error}\n </div>\n ) : null}\n\n {collection ? (\n <>\n <div className=\"flex items-center gap-1\">\n <CommitInput value={collection.name} aria-label=\"Collection name\" onCommit={(name) => run(() => store.renameCollection(collection.id, name))} />\n <button type=\"button\" className={button} onClick={() => run(() => store.deleteCollection(collection.id))} aria-label={`Delete collection ${collection.name}`}>\n Delete\n </button>\n </div>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full border-collapse\">\n <thead>\n <tr className=\"border-b border-border text-left text-muted-foreground\">\n <th className=\"px-1 py-1 font-medium\">Name</th>\n {collection.modes.map((mode, index) => (\n <th key={mode.id} className=\"px-1 py-1 font-medium\" data-mode-id={mode.id}>\n <div className=\"flex items-center gap-1\">\n <CommitInput\n value={mode.name}\n aria-label={`Mode ${mode.name} name`}\n onCommit={(name) => run(() => store.renameMode(collection.id, mode.id, name))}\n />\n {index === 0 ? (\n <span title=\"Default mode\" className=\"text-[10px] uppercase\">\n default\n </span>\n ) : (\n <button\n type=\"button\"\n className=\"px-1 hover:text-foreground\"\n title=\"Make default\"\n aria-label={`Make ${mode.name} the default mode`}\n onClick={() => run(() => store.setDefaultMode(collection.id, mode.id))}\n >\n ★\n </button>\n )}\n {collection.modes.length > 1 ? (\n <button\n type=\"button\"\n className=\"px-1 hover:text-foreground\"\n aria-label={`Remove mode ${mode.name}`}\n onClick={() => run(() => store.removeMode(collection.id, mode.id))}\n >\n ×\n </button>\n ) : null}\n </div>\n </th>\n ))}\n <th className=\"px-1 py-1\">\n <form\n className=\"flex items-center gap-1\"\n onSubmit={(e) => {\n e.preventDefault();\n run(() => {\n store.addMode(collection.id, newMode);\n setNewMode('');\n });\n }}\n >\n <input\n className={input}\n placeholder=\"Mode\"\n aria-label=\"New mode name\"\n value={newMode}\n onChange={(e) => setNewMode(e.currentTarget.value)}\n />\n <button type=\"submit\" className={button} disabled={!newMode.trim()} aria-label=\"Add mode\">\n +\n </button>\n </form>\n </th>\n </tr>\n </thead>\n <tbody>\n {variables.map((variable) => (\n <tr key={variable.id} className=\"border-b border-border/50\" data-token-row={variable.id}>\n <td className=\"px-1 py-1\">\n <div className=\"flex items-center gap-1\">\n <span className=\"w-4 text-center text-muted-foreground\" title={variable.type}>\n {variable.type === 'color' ? '◐' : variable.type === 'number' ? '#' : variable.type === 'string' ? 'T' : '⊘'}\n </span>\n <CommitInput\n value={variable.name}\n aria-label={`Variable ${variable.name} name`}\n onCommit={(name) => run(() => store.renameVariable(variable.id, name))}\n />\n </div>\n </td>\n {collection.modes.map((mode) => (\n <ValueCell\n key={mode.id}\n state={state}\n variable={variable}\n mode={mode}\n onSet={(value) => run(() => store.setValue(variable.id, value, mode.id))}\n />\n ))}\n <td className=\"px-1 py-1 text-right\">\n <button\n type=\"button\"\n className=\"px-1 text-muted-foreground hover:text-foreground\"\n aria-label={`Delete variable ${variable.name}`}\n onClick={() => run(() => store.deleteVariables(variable.id))}\n >\n ×\n </button>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n <form\n className=\"flex items-center gap-1\"\n onSubmit={(e) => {\n e.preventDefault();\n run(() => {\n store.createVariable({ collectionId: collection.id, name: newName, type: newType });\n setNewName('');\n });\n }}\n >\n <input\n className={input}\n placeholder=\"color/bg\"\n aria-label=\"New variable name\"\n value={newName}\n onChange={(e) => setNewName(e.currentTarget.value)}\n />\n <select\n aria-label=\"New variable type\"\n className=\"rounded border border-border bg-background px-1 py-0.5 text-xs\"\n value={newType}\n onChange={(e) => setNewType(e.currentTarget.value as TokenType)}\n >\n {TOKEN_TYPES.map((t) => (\n <option key={t} value={t}>\n {t}\n </option>\n ))}\n </select>\n <button type=\"submit\" className={button} disabled={!newName.trim()}>\n Add variable\n </button>\n </form>\n </>\n ) : (\n <p className=\"text-muted-foreground\">No collections yet.</p>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","import_tokens"]}
@@ -0,0 +1,54 @@
1
+ import { TokenModeContext, TokenCssOptions, TokenStore, TokenState, TokenStoreOptions } from '@pixerate/editor/tokens';
2
+ import React__default from 'react';
3
+
4
+ /** A `TokenStore` created once for the component's lifetime. */
5
+ declare function useTokenStore(options?: TokenStoreOptions): TokenStore;
6
+ /** The store's current state (re-renders on every change). */
7
+ declare function useTokenState(store: TokenStore): TokenState;
8
+ interface UseTokensOptions {
9
+ /** Modes for `cssVariables` (collection id → mode id; default modes otherwise). */
10
+ modes?: TokenModeContext;
11
+ /** Options for `css` and `cssVariables` (prefix, attribute, …). */
12
+ css?: TokenCssOptions;
13
+ }
14
+ interface UseTokensResult {
15
+ store: TokenStore;
16
+ state: TokenState;
17
+ /** Resolved custom properties for `modes`, e.g. `<div style={cssVariables}>`. */
18
+ cssVariables: Record<string, string>;
19
+ /** A stylesheet with every mode (`:root { … } :root[data-mode="dark"] { … }`). */
20
+ css: string;
21
+ canUndo: boolean;
22
+ canRedo: boolean;
23
+ undo: () => void;
24
+ redo: () => void;
25
+ }
26
+ /**
27
+ * Subscribes to a token store and exposes its state plus CSS custom properties for app
28
+ * theming: apply `cssVariables` as an inline style, or inject `css` and switch modes with the
29
+ * `data-mode` attribute.
30
+ */
31
+ declare function useTokens(store: TokenStore, options?: UseTokensOptions): UseTokensResult;
32
+
33
+ interface TokensPanelProps {
34
+ store: TokenStore;
35
+ /** Selected collection (controlled); defaults to the first. */
36
+ collectionId?: string;
37
+ onCollectionChange?: (collectionId: string) => void;
38
+ /** Called with refused edits (also shown in the panel). */
39
+ onError?: (error: Error) => void;
40
+ className?: string;
41
+ }
42
+ /** A colour swatch (checkered under translucent colours). */
43
+ declare function TokenSwatch({ color, className }: {
44
+ color: string;
45
+ className?: string;
46
+ }): React__default.JSX.Element;
47
+ /**
48
+ * Figma-style variables panel: collections, modes as columns, typed values with colour
49
+ * swatches, aliases (→), add / rename / delete, undo / redo. Edits go through the store, so
50
+ * each is one undo step; refused edits (cycles, duplicate names) show as an alert.
51
+ */
52
+ declare function TokensPanel({ store, collectionId, onCollectionChange, onError, className }: TokensPanelProps): React__default.JSX.Element;
53
+
54
+ export { TokenSwatch, TokensPanel, type TokensPanelProps, type UseTokensOptions, type UseTokensResult, useTokenState, useTokenStore, useTokens };
@@ -0,0 +1,54 @@
1
+ import { TokenModeContext, TokenCssOptions, TokenStore, TokenState, TokenStoreOptions } from '@pixerate/editor/tokens';
2
+ import React__default from 'react';
3
+
4
+ /** A `TokenStore` created once for the component's lifetime. */
5
+ declare function useTokenStore(options?: TokenStoreOptions): TokenStore;
6
+ /** The store's current state (re-renders on every change). */
7
+ declare function useTokenState(store: TokenStore): TokenState;
8
+ interface UseTokensOptions {
9
+ /** Modes for `cssVariables` (collection id → mode id; default modes otherwise). */
10
+ modes?: TokenModeContext;
11
+ /** Options for `css` and `cssVariables` (prefix, attribute, …). */
12
+ css?: TokenCssOptions;
13
+ }
14
+ interface UseTokensResult {
15
+ store: TokenStore;
16
+ state: TokenState;
17
+ /** Resolved custom properties for `modes`, e.g. `<div style={cssVariables}>`. */
18
+ cssVariables: Record<string, string>;
19
+ /** A stylesheet with every mode (`:root { … } :root[data-mode="dark"] { … }`). */
20
+ css: string;
21
+ canUndo: boolean;
22
+ canRedo: boolean;
23
+ undo: () => void;
24
+ redo: () => void;
25
+ }
26
+ /**
27
+ * Subscribes to a token store and exposes its state plus CSS custom properties for app
28
+ * theming: apply `cssVariables` as an inline style, or inject `css` and switch modes with the
29
+ * `data-mode` attribute.
30
+ */
31
+ declare function useTokens(store: TokenStore, options?: UseTokensOptions): UseTokensResult;
32
+
33
+ interface TokensPanelProps {
34
+ store: TokenStore;
35
+ /** Selected collection (controlled); defaults to the first. */
36
+ collectionId?: string;
37
+ onCollectionChange?: (collectionId: string) => void;
38
+ /** Called with refused edits (also shown in the panel). */
39
+ onError?: (error: Error) => void;
40
+ className?: string;
41
+ }
42
+ /** A colour swatch (checkered under translucent colours). */
43
+ declare function TokenSwatch({ color, className }: {
44
+ color: string;
45
+ className?: string;
46
+ }): React__default.JSX.Element;
47
+ /**
48
+ * Figma-style variables panel: collections, modes as columns, typed values with colour
49
+ * swatches, aliases (→), add / rename / delete, undo / redo. Edits go through the store, so
50
+ * each is one undo step; refused edits (cycles, duplicate names) show as an alert.
51
+ */
52
+ declare function TokensPanel({ store, collectionId, onCollectionChange, onError, className }: TokensPanelProps): React__default.JSX.Element;
53
+
54
+ export { TokenSwatch, TokensPanel, type TokensPanelProps, type UseTokensOptions, type UseTokensResult, useTokenState, useTokenStore, useTokens };