@lattice-php/lattice 0.18.0 → 0.20.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.
- package/README.md +1 -1
- package/dist/core/index.js +3 -3
- package/dist/core/nodes.d.ts +5 -0
- package/dist/core/nodes.js +8 -1
- package/dist/core/nodes.js.map +1 -1
- package/dist/core/renderer.js +1 -3
- package/dist/core/renderer.js.map +1 -1
- package/dist/core/types.d.ts +16 -4
- package/dist/create-app.d.ts +31 -3
- package/dist/create-app.js +48 -10
- package/dist/create-app.js.map +1 -1
- package/dist/effects/registry.d.ts +3 -3
- package/dist/effects/registry.js +4 -4
- package/dist/effects/registry.js.map +1 -1
- package/dist/events/event-names.d.ts +0 -3
- package/dist/events/event-names.js +0 -3
- package/dist/events/event-names.js.map +1 -1
- package/dist/form/components/base/field.d.ts +2 -2
- package/dist/form/components/base/field.js +2 -2
- package/dist/form/components/base/field.js.map +1 -1
- package/dist/form/components/fields/color-picker-field.d.ts +2 -0
- package/dist/form/components/fields/color-picker-field.js +57 -0
- package/dist/form/components/fields/color-picker-field.js.map +1 -0
- package/dist/form/components/fields/date-input.js +1 -1
- package/dist/form/components/fields/date-picker-field.js +1 -1
- package/dist/form/components/fields/date-time-input.js +1 -1
- package/dist/form/components/fields/file-upload.js +7 -12
- package/dist/form/components/fields/file-upload.js.map +1 -1
- package/dist/form/components/fields/number-input.js +1 -1
- package/dist/form/components/fields/rich-editor-field.js +36 -275
- package/dist/form/components/fields/rich-editor-field.js.map +1 -1
- package/dist/form/components/fields/row-action-menu.js +2 -2
- package/dist/form/components/fields/row-action-menu.js.map +1 -1
- package/dist/form/components/fields/row-actions.js +3 -1
- package/dist/form/components/fields/row-actions.js.map +1 -1
- package/dist/form/components/fields/row-item.js +2 -1
- package/dist/form/components/fields/row-item.js.map +1 -1
- package/dist/form/components/fields/select.js +59 -16
- package/dist/form/components/fields/select.js.map +1 -1
- package/dist/form/components/fields/table-rows.js +5 -4
- package/dist/form/components/fields/table-rows.js.map +1 -1
- package/dist/form/components/fields/text-input.js +8 -1
- package/dist/form/components/fields/text-input.js.map +1 -1
- package/dist/form/components/fields/time-input.js +1 -1
- package/dist/form/components/index.d.ts +1 -0
- package/dist/form/components/index.js +2 -1
- package/dist/form/embed.d.ts +1 -0
- package/dist/form/embed.js +2 -1
- package/dist/form/hooks/use-form-resolver.js.map +1 -1
- package/dist/form/index.js +2 -1
- package/dist/form/lib/conditions.d.ts +4 -10
- package/dist/form/lib/conditions.js.map +1 -1
- package/dist/form/lib/field-props.d.ts +4 -18
- package/dist/form/lib/field-props.js.map +1 -1
- package/dist/form/plugin.js +2 -0
- package/dist/form/plugin.js.map +1 -1
- package/dist/form/rich-editor/builtins.d.ts +6 -0
- package/dist/form/rich-editor/builtins.js +400 -0
- package/dist/form/rich-editor/builtins.js.map +1 -0
- package/dist/form/rich-editor/index.d.ts +7 -0
- package/dist/form/rich-editor/index.js +3 -0
- package/dist/form/rich-editor/registry.d.ts +67 -0
- package/dist/form/rich-editor/registry.js +79 -0
- package/dist/form/rich-editor/registry.js.map +1 -0
- package/dist/form/rich-editor/toolbar-button.d.ts +12 -0
- package/dist/form/rich-editor/toolbar-button.js +28 -0
- package/dist/form/rich-editor/toolbar-button.js.map +1 -0
- package/dist/form/types.d.ts +1 -6
- package/dist/format/date-time.d.ts +3 -2
- package/dist/format/date-time.js.map +1 -1
- package/dist/i18n/backend.js +1 -1
- package/dist/i18n/date-time.d.ts +3 -2
- package/dist/i18n/date-time.js.map +1 -1
- package/dist/i18n/index.js +3 -2
- package/dist/i18n/instance.js +1 -1
- package/dist/i18n/page-props.d.ts +3 -2
- package/dist/i18n/page-props.js +1 -15
- package/dist/i18n/page-props.js.map +1 -1
- package/dist/i18n/shared-props.d.ts +7 -0
- package/dist/i18n/shared-props.js +25 -0
- package/dist/i18n/shared-props.js.map +1 -0
- package/dist/icons/icon-renderer.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.js +7 -6
- package/dist/lattice.css +119 -57
- package/dist/layout/components/breadcrumbs.js +3 -1
- package/dist/layout/components/breadcrumbs.js.map +1 -1
- package/dist/layout/components/callouts.js +1 -1
- package/dist/lib/color.d.ts +9 -0
- package/dist/lib/color.js +48 -0
- package/dist/lib/color.js.map +1 -0
- package/dist/notifications/api.d.ts +6 -6
- package/dist/notifications/api.js.map +1 -1
- package/dist/notifications/types.d.ts +2 -10
- package/dist/provider-base.js +1 -1
- package/dist/realtime/build-effects.js +5 -5
- package/dist/realtime/build-effects.js.map +1 -1
- package/dist/realtime/listeners.d.ts +2 -2
- package/dist/realtime/listeners.js.map +1 -1
- package/dist/realtime/subscriptions.d.ts +2 -2
- package/dist/realtime/subscriptions.js.map +1 -1
- package/dist/registry.js.map +1 -1
- package/dist/table/components/bulk-bar.js +5 -5
- package/dist/table/components/bulk-bar.js.map +1 -1
- package/dist/table/components/cells/badge-cell.js +4 -1
- package/dist/table/components/cells/badge-cell.js.map +1 -1
- package/dist/table/components/cells/boolean-cell.js +3 -1
- package/dist/table/components/cells/boolean-cell.js.map +1 -1
- package/dist/table/components/cells/icon-cell.js +5 -2
- package/dist/table/components/cells/icon-cell.js.map +1 -1
- package/dist/table/components/cells/image-cell.js +8 -9
- package/dist/table/components/cells/image-cell.js.map +1 -1
- package/dist/table/components/cells/money-cell.js +9 -1
- package/dist/table/components/cells/money-cell.js.map +1 -1
- package/dist/table/components/cells/number-cell.js +9 -1
- package/dist/table/components/cells/number-cell.js.map +1 -1
- package/dist/table/components/cells/text-cell.js +4 -1
- package/dist/table/components/cells/text-cell.js.map +1 -1
- package/dist/table/components/column-filter-control.js +25 -32
- package/dist/table/components/column-filter-control.js.map +1 -1
- package/dist/table/components/column-header.d.ts +3 -3
- package/dist/table/components/column-header.js +5 -3
- package/dist/table/components/column-header.js.map +1 -1
- package/dist/table/components/column-visibility-menu.js +6 -11
- package/dist/table/components/column-visibility-menu.js.map +1 -1
- package/dist/table/components/filter-bar.d.ts +2 -2
- package/dist/table/components/filter-bar.js +13 -22
- package/dist/table/components/filter-bar.js.map +1 -1
- package/dist/table/components/filter-controls.d.ts +2 -1
- package/dist/table/components/filter-controls.js +36 -35
- package/dist/table/components/filter-controls.js.map +1 -1
- package/dist/table/components/filter-value-input.d.ts +0 -1
- package/dist/table/components/filter-value-input.js +21 -20
- package/dist/table/components/filter-value-input.js.map +1 -1
- package/dist/table/components/pagination.js +16 -20
- package/dist/table/components/pagination.js.map +1 -1
- package/dist/table/components/sort-bar.d.ts +3 -3
- package/dist/table/components/sort-bar.js +11 -12
- package/dist/table/components/sort-bar.js.map +1 -1
- package/dist/table/components/table-search.d.ts +9 -0
- package/dist/table/components/table-search.js +75 -0
- package/dist/table/components/table-search.js.map +1 -0
- package/dist/table/components/table.js +72 -19
- package/dist/table/components/table.js.map +1 -1
- package/dist/table/hooks/use-expanded-rows.d.ts +8 -0
- package/dist/table/hooks/use-expanded-rows.js +24 -0
- package/dist/table/hooks/use-expanded-rows.js.map +1 -0
- package/dist/table/hooks/use-table.d.ts +5 -3
- package/dist/table/hooks/use-table.js +56 -44
- package/dist/table/hooks/use-table.js.map +1 -1
- package/dist/table/lib/payload.d.ts +3 -2
- package/dist/table/lib/payload.js +16 -10
- package/dist/table/lib/payload.js.map +1 -1
- package/dist/table/lib/query.d.ts +5 -5
- package/dist/table/lib/query.js +20 -18
- package/dist/table/lib/query.js.map +1 -1
- package/dist/table/types.d.ts +3 -26
- package/dist/toast/callout.js +1 -3
- package/dist/toast/callout.js.map +1 -1
- package/dist/toast/toast.d.ts +1 -5
- package/dist/toast/toast.js +2 -7
- package/dist/toast/toast.js.map +1 -1
- package/dist/toast/toaster.js +2 -2
- package/dist/toast/toaster.js.map +1 -1
- package/dist/types/generated.d.ts +263 -188
- package/dist/ui/affix-group.d.ts +2 -1
- package/dist/ui/affix-group.js +9 -7
- package/dist/ui/affix-group.js.map +1 -1
- package/dist/ui/avatar.d.ts +3 -0
- package/dist/ui/avatar.js +52 -0
- package/dist/ui/avatar.js.map +1 -0
- package/dist/ui/button.d.ts +3 -1
- package/dist/ui/button.js +10 -3
- package/dist/ui/button.js.map +1 -1
- package/dist/ui/chart-view.js +161 -11
- package/dist/ui/chart-view.js.map +1 -1
- package/dist/ui/click-behavior.d.ts +1 -1
- package/dist/ui/click-behavior.js.map +1 -1
- package/dist/ui/collapsible.js +1 -1
- package/dist/ui/color-picker.d.ts +8 -0
- package/dist/ui/color-picker.js +59 -0
- package/dist/ui/color-picker.js.map +1 -0
- package/dist/ui/combobox.d.ts +11 -2
- package/dist/ui/combobox.js +51 -8
- package/dist/ui/combobox.js.map +1 -1
- package/dist/ui/copyable-text.d.ts +7 -0
- package/dist/ui/copyable-text.js +21 -13
- package/dist/ui/copyable-text.js.map +1 -1
- package/dist/ui/fragment.js +1 -1
- package/dist/ui/fragment.js.map +1 -1
- package/dist/ui/heading.js +39 -21
- package/dist/ui/heading.js.map +1 -1
- package/dist/ui/icon-button.d.ts +22 -0
- package/dist/ui/icon-button.js +51 -0
- package/dist/ui/icon-button.js.map +1 -0
- package/dist/ui/icon.js +10 -11
- package/dist/ui/icon.js.map +1 -1
- package/dist/ui/image-preview.d.ts +12 -0
- package/dist/ui/image-preview.js +65 -0
- package/dist/ui/image-preview.js.map +1 -0
- package/dist/ui/image.d.ts +3 -0
- package/dist/ui/image.js +21 -0
- package/dist/ui/image.js.map +1 -0
- package/dist/ui/index.d.ts +2 -1
- package/dist/ui/index.js +4 -3
- package/dist/ui/info-tooltip.js +1 -1
- package/dist/ui/info-tooltip.js.map +1 -1
- package/dist/ui/input.d.ts +3 -1
- package/dist/ui/input.js +2 -2
- package/dist/ui/input.js.map +1 -1
- package/dist/ui/modal.js +14 -2
- package/dist/ui/modal.js.map +1 -1
- package/dist/ui/native-select.d.ts +10 -0
- package/dist/ui/native-select.js +23 -0
- package/dist/ui/native-select.js.map +1 -0
- package/dist/ui/plugin.js +11 -1
- package/dist/ui/plugin.js.map +1 -1
- package/dist/ui/progress.d.ts +3 -0
- package/dist/ui/progress.js +114 -0
- package/dist/ui/progress.js.map +1 -0
- package/dist/ui/section.js +1 -1
- package/dist/ui/separator.d.ts +3 -0
- package/dist/ui/separator.js +15 -0
- package/dist/ui/separator.js.map +1 -0
- package/dist/ui/text.js +5 -10
- package/dist/ui/text.js.map +1 -1
- package/dist/ui/tooltip.js +1 -1
- package/dist/ui/tree-context.d.ts +33 -0
- package/dist/ui/tree-context.js +137 -0
- package/dist/ui/tree-context.js.map +1 -0
- package/dist/ui/tree.d.ts +19 -0
- package/dist/ui/tree.js +187 -0
- package/dist/ui/tree.js.map +1 -0
- package/dist-standalone/chunks/chart-view-wJmnVu7L.js +36 -0
- package/dist-standalone/chunks/date-picker-field-VbH9xrQO.js +1 -0
- package/dist-standalone/chunks/dist-PaT1gYi_.js +2 -0
- package/dist-standalone/chunks/jsx-runtime-Drll_-DD.js +1 -0
- package/dist-standalone/chunks/locale-CXqr7LPR.js +1 -0
- package/dist-standalone/chunks/notifications-echo-DsfNc4Ge.js +1 -0
- package/dist-standalone/chunks/page-props-AIa5UR6n.js +1 -0
- package/dist-standalone/chunks/react-B8IZ02wI.js +1 -0
- package/dist-standalone/chunks/rich-editor-field-DLQTyt36.js +154 -0
- package/dist-standalone/chunks/subscriptions-DY2tj9Im.js +1 -0
- package/dist-standalone/chunks/test-id-Dh3L6Gfh.js +1 -0
- package/dist-standalone/chunks/use-effect-dispatcher-feEs0GCP.js +97 -0
- package/dist-standalone/chunks/with-selector-BwDoCrJJ.js +1 -0
- package/dist-standalone/lattice.css +3 -0
- package/dist-standalone/lattice.js +66 -0
- package/dist-standalone/manifest.json +20 -0
- package/dist-standalone/sprite.svg +1 -0
- package/package.json +11 -2
- package/resources/icons/code-xml.svg +17 -0
- package/resources/icons/heading.svg +17 -0
- package/dist/action/hooks/use-click-behavior.d.ts +0 -3
- package/dist/action/hooks/use-click-behavior.js +0 -3
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { Input } from "./input.js";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { HexColorPicker } from "react-colorful";
|
|
6
|
+
//#region resources/js/ui/color-picker.tsx
|
|
7
|
+
function normalizeHex(input) {
|
|
8
|
+
const raw = input.trim().replace(/^#/, "").toLowerCase();
|
|
9
|
+
if (/^[0-9a-f]{6}$/.test(raw)) return `#${raw}`;
|
|
10
|
+
if (/^[0-9a-f]{3}$/.test(raw)) return `#${raw[0]}${raw[0]}${raw[1]}${raw[1]}${raw[2]}${raw[2]}`;
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
function ColorPicker({ value, onChange, palette, hexLabel = "Hex color", paletteLabel = "Color palette" }) {
|
|
14
|
+
const [draft, setDraft] = useState(null);
|
|
15
|
+
const color = normalizeHex(value) ?? "#6b7280";
|
|
16
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
17
|
+
className: "flex w-56 flex-col gap-3",
|
|
18
|
+
"data-slot": "color-picker",
|
|
19
|
+
children: [
|
|
20
|
+
/* @__PURE__ */ jsx(HexColorPicker, {
|
|
21
|
+
className: "!w-full",
|
|
22
|
+
color,
|
|
23
|
+
onChange
|
|
24
|
+
}),
|
|
25
|
+
palette.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
26
|
+
"aria-label": paletteLabel,
|
|
27
|
+
className: "flex flex-wrap gap-1.5",
|
|
28
|
+
role: "listbox",
|
|
29
|
+
children: palette.map((swatch) => {
|
|
30
|
+
const normalized = normalizeHex(swatch) ?? swatch;
|
|
31
|
+
return /* @__PURE__ */ jsx("button", {
|
|
32
|
+
"aria-label": normalized,
|
|
33
|
+
"aria-selected": normalized === color,
|
|
34
|
+
className: cn("size-6 shrink-0 rounded-full border border-lt-border", normalized === color && "ring-2 ring-lt-ring ring-offset-1"),
|
|
35
|
+
onClick: () => onChange(normalized),
|
|
36
|
+
role: "option",
|
|
37
|
+
style: { background: swatch },
|
|
38
|
+
type: "button"
|
|
39
|
+
}, swatch);
|
|
40
|
+
})
|
|
41
|
+
}),
|
|
42
|
+
/* @__PURE__ */ jsx(Input, {
|
|
43
|
+
"aria-label": hexLabel,
|
|
44
|
+
onBlur: () => setDraft(null),
|
|
45
|
+
onChange: (event) => {
|
|
46
|
+
const text = event.target.value;
|
|
47
|
+
const hex = normalizeHex(text);
|
|
48
|
+
setDraft(text);
|
|
49
|
+
if (hex !== null) onChange(hex);
|
|
50
|
+
},
|
|
51
|
+
value: draft ?? color
|
|
52
|
+
})
|
|
53
|
+
]
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
export { ColorPicker, normalizeHex };
|
|
58
|
+
|
|
59
|
+
//# sourceMappingURL=color-picker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color-picker.js","names":[],"sources":["../../resources/js/ui/color-picker.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { HexColorPicker } from \"react-colorful\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { Input } from \"./input\";\n\nexport function normalizeHex(input: string): string | null {\n const raw = input.trim().replace(/^#/, \"\").toLowerCase();\n\n if (/^[0-9a-f]{6}$/.test(raw)) {\n return `#${raw}`;\n }\n\n if (/^[0-9a-f]{3}$/.test(raw)) {\n return `#${raw[0]}${raw[0]}${raw[1]}${raw[1]}${raw[2]}${raw[2]}`;\n }\n\n return null;\n}\n\nexport function ColorPicker({\n value,\n onChange,\n palette,\n hexLabel = \"Hex color\",\n paletteLabel = \"Color palette\",\n}: {\n value: string;\n onChange: (hex: string) => void;\n palette: string[];\n hexLabel?: string;\n paletteLabel?: string;\n}) {\n const [draft, setDraft] = useState<string | null>(null);\n const color = normalizeHex(value) ?? \"#6b7280\";\n\n return (\n <div className=\"flex w-56 flex-col gap-3\" data-slot=\"color-picker\">\n <HexColorPicker className=\"!w-full\" color={color} onChange={onChange} />\n {palette.length > 0 && (\n <div aria-label={paletteLabel} className=\"flex flex-wrap gap-1.5\" role=\"listbox\">\n {palette.map((swatch) => {\n const normalized = normalizeHex(swatch) ?? swatch;\n\n return (\n <button\n aria-label={normalized}\n aria-selected={normalized === color}\n className={cn(\n \"size-6 shrink-0 rounded-full border border-lt-border\",\n normalized === color && \"ring-2 ring-lt-ring ring-offset-1\",\n )}\n key={swatch}\n onClick={() => onChange(normalized)}\n role=\"option\"\n style={{ background: swatch }}\n type=\"button\"\n />\n );\n })}\n </div>\n )}\n <Input\n aria-label={hexLabel}\n onBlur={() => setDraft(null)}\n onChange={(event) => {\n const text = event.target.value;\n const hex = normalizeHex(text);\n setDraft(text);\n\n if (hex !== null) {\n onChange(hex);\n }\n }}\n value={draft ?? color}\n />\n </div>\n );\n}\n"],"mappings":";;;;;;AAKA,SAAgB,aAAa,OAA8B;CACzD,MAAM,MAAM,MAAM,KAAK,EAAE,QAAQ,MAAM,EAAE,EAAE,YAAY;CAEvD,IAAI,gBAAgB,KAAK,GAAG,GAC1B,OAAO,IAAI;CAGb,IAAI,gBAAgB,KAAK,GAAG,GAC1B,OAAO,IAAI,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI;CAG9D,OAAO;AACT;AAEA,SAAgB,YAAY,EAC1B,OACA,UACA,SACA,WAAW,aACX,eAAe,mBAOd;CACD,MAAM,CAAC,OAAO,YAAY,SAAwB,IAAI;CACtD,MAAM,QAAQ,aAAa,KAAK,KAAK;CAErC,OACE,qBAAC,OAAD;EAAK,WAAU;EAA2B,aAAU;YAApD;GACE,oBAAC,gBAAD;IAAgB,WAAU;IAAiB;IAAiB;GAAW,CAAA;GACtE,QAAQ,SAAS,KAChB,oBAAC,OAAD;IAAK,cAAY;IAAc,WAAU;IAAyB,MAAK;cACpE,QAAQ,KAAK,WAAW;KACvB,MAAM,aAAa,aAAa,MAAM,KAAK;KAE3C,OACE,oBAAC,UAAD;MACE,cAAY;MACZ,iBAAe,eAAe;MAC9B,WAAW,GACT,wDACA,eAAe,SAAS,mCAC1B;MAEA,eAAe,SAAS,UAAU;MAClC,MAAK;MACL,OAAO,EAAE,YAAY,OAAO;MAC5B,MAAK;KACN,GALM,MAKN;IAEL,CAAC;GACE,CAAA;GAEP,oBAAC,OAAD;IACE,cAAY;IACZ,cAAc,SAAS,IAAI;IAC3B,WAAW,UAAU;KACnB,MAAM,OAAO,MAAM,OAAO;KAC1B,MAAM,MAAM,aAAa,IAAI;KAC7B,SAAS,IAAI;KAEb,IAAI,QAAQ,MACV,SAAS,GAAG;IAEhB;IACA,OAAO,SAAS;GACjB,CAAA;EACE;;AAET"}
|
package/dist/ui/combobox.d.ts
CHANGED
|
@@ -7,18 +7,27 @@ import * as React from "react";
|
|
|
7
7
|
* consumer also owns option fetching. Pass `onSearch` for remote search (the
|
|
8
8
|
* combobox debounces the query and renders `options` as given); omit it to
|
|
9
9
|
* filter the provided `options` locally by label. The combobox closes itself
|
|
10
|
-
* after a single-select.
|
|
10
|
+
* after a single-select. Pass `renderOption` to render rich option rows; the
|
|
11
|
+
* option's label stays the accessible name. `onSelect` toggles selection
|
|
12
|
+
* (used by dropdown-row clicks); tag-entry commits (Enter/comma/paste) that
|
|
13
|
+
* match an existing option call `onCommit` instead, falling back to
|
|
14
|
+
* `onSelect` when it is not provided, so consumers can make tag entry
|
|
15
|
+
* additive instead of toggling.
|
|
11
16
|
*/
|
|
12
|
-
declare function Combobox({ contentClassName, emptyLabel, loading, multiple, onSearch, onSelect, open, onOpenChange, options, searchLabel, searchPlaceholder, selected, showSearch, testId, trigger, triggerClassName, triggerProps, }: {
|
|
17
|
+
declare function Combobox({ contentClassName, creatable, emptyLabel, loading, multiple, onCommit, onCreate, onSearch, onSelect, open, onOpenChange, options, renderOption, searchLabel, searchPlaceholder, selected, showSearch, testId, trigger, triggerClassName, triggerProps, }: {
|
|
13
18
|
contentClassName?: string;
|
|
19
|
+
creatable?: boolean;
|
|
14
20
|
emptyLabel?: string;
|
|
15
21
|
loading?: boolean;
|
|
16
22
|
multiple?: boolean;
|
|
23
|
+
onCommit?: (value: string) => void;
|
|
24
|
+
onCreate?: (label: string) => void;
|
|
17
25
|
onSearch?: (query: string) => void;
|
|
18
26
|
onSelect: (value: string) => void;
|
|
19
27
|
open: boolean;
|
|
20
28
|
onOpenChange: (open: boolean) => void;
|
|
21
29
|
options: Option[];
|
|
30
|
+
renderOption?: (option: Option) => React.ReactNode;
|
|
22
31
|
searchLabel?: string;
|
|
23
32
|
searchPlaceholder?: string;
|
|
24
33
|
selected: string[];
|
package/dist/ui/combobox.js
CHANGED
|
@@ -13,11 +13,27 @@ var SEARCH_DEBOUNCE_MS = 250;
|
|
|
13
13
|
* consumer also owns option fetching. Pass `onSearch` for remote search (the
|
|
14
14
|
* combobox debounces the query and renders `options` as given); omit it to
|
|
15
15
|
* filter the provided `options` locally by label. The combobox closes itself
|
|
16
|
-
* after a single-select.
|
|
16
|
+
* after a single-select. Pass `renderOption` to render rich option rows; the
|
|
17
|
+
* option's label stays the accessible name. `onSelect` toggles selection
|
|
18
|
+
* (used by dropdown-row clicks); tag-entry commits (Enter/comma/paste) that
|
|
19
|
+
* match an existing option call `onCommit` instead, falling back to
|
|
20
|
+
* `onSelect` when it is not provided, so consumers can make tag entry
|
|
21
|
+
* additive instead of toggling.
|
|
17
22
|
*/
|
|
18
|
-
function Combobox({ contentClassName, emptyLabel, loading = false, multiple = false, onSearch, onSelect, open, onOpenChange, options, searchLabel, searchPlaceholder, selected, showSearch = true, testId, trigger, triggerClassName, triggerProps }) {
|
|
23
|
+
function Combobox({ contentClassName, creatable = false, emptyLabel, loading = false, multiple = false, onCommit, onCreate, onSearch, onSelect, open, onOpenChange, options, renderOption, searchLabel, searchPlaceholder, selected, showSearch = true, testId, trigger, triggerClassName, triggerProps }) {
|
|
19
24
|
const { t } = useT("lattice");
|
|
20
25
|
const [query, setQuery] = useState("");
|
|
26
|
+
function commitCreate(raw) {
|
|
27
|
+
const tokens = raw.split(",").map((token) => token.trim()).filter(Boolean);
|
|
28
|
+
for (const token of tokens) {
|
|
29
|
+
const match = options.find((option) => option.label.toLowerCase() === token.toLowerCase());
|
|
30
|
+
if (match) (onCommit ?? onSelect)(match.value);
|
|
31
|
+
else onCreate?.(token);
|
|
32
|
+
}
|
|
33
|
+
if (multiple) setQuery("");
|
|
34
|
+
else close();
|
|
35
|
+
}
|
|
36
|
+
const exactMatch = options.some((option) => option.label.toLowerCase() === query.trim().toLowerCase());
|
|
21
37
|
useEffect(() => {
|
|
22
38
|
if (!onSearch || !open) return;
|
|
23
39
|
const timer = window.setTimeout(() => onSearch(query), SEARCH_DEBOUNCE_MS);
|
|
@@ -53,11 +69,24 @@ function Combobox({ contentClassName, emptyLabel, loading = false, multiple = fa
|
|
|
53
69
|
children: [showSearch && /* @__PURE__ */ jsxs("div", {
|
|
54
70
|
className: "flex items-center gap-2 border-b border-lt-border px-3 py-2",
|
|
55
71
|
children: [/* @__PURE__ */ jsx("input", {
|
|
56
|
-
"aria-label": searchLabel ?? t("form.
|
|
72
|
+
"aria-label": searchLabel ?? t("form.search-options", "Search options"),
|
|
57
73
|
"data-slot": "combobox-search",
|
|
58
74
|
"data-test": testId ? `${testId}-search` : void 0,
|
|
59
75
|
className: "w-full bg-transparent text-sm outline-none placeholder:text-lt-muted-fg",
|
|
60
|
-
onChange: (event) =>
|
|
76
|
+
onChange: (event) => {
|
|
77
|
+
const next = event.target.value;
|
|
78
|
+
if (creatable && next.includes(",")) {
|
|
79
|
+
commitCreate(next);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
setQuery(next);
|
|
83
|
+
},
|
|
84
|
+
onKeyDown: (event) => {
|
|
85
|
+
if (creatable && event.key === "Enter" && query.trim() !== "") {
|
|
86
|
+
event.preventDefault();
|
|
87
|
+
commitCreate(query);
|
|
88
|
+
}
|
|
89
|
+
},
|
|
61
90
|
placeholder: searchPlaceholder,
|
|
62
91
|
value: query
|
|
63
92
|
}), loading && /* @__PURE__ */ jsx(Icon, {
|
|
@@ -65,15 +94,26 @@ function Combobox({ contentClassName, emptyLabel, loading = false, multiple = fa
|
|
|
65
94
|
"aria-hidden": "true",
|
|
66
95
|
className: "size-lt-icon-md shrink-0 animate-spin text-lt-muted-fg"
|
|
67
96
|
})]
|
|
68
|
-
}), /* @__PURE__ */
|
|
97
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
69
98
|
className: "max-h-60 overflow-y-auto p-1",
|
|
70
99
|
role: "listbox",
|
|
71
|
-
children:
|
|
100
|
+
children: [creatable && query.trim() !== "" && !exactMatch && /* @__PURE__ */ jsxs("button", {
|
|
101
|
+
className: "flex w-full items-center gap-2 rounded-lt-sm px-3 py-1.5 text-left text-sm transition-colors hover:bg-lt-accent hover:text-lt-accent-fg",
|
|
102
|
+
"data-test": testId ? `${testId}-create` : void 0,
|
|
103
|
+
onClick: () => commitCreate(query),
|
|
104
|
+
type: "button",
|
|
105
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
106
|
+
name: "plus",
|
|
107
|
+
"aria-hidden": "true",
|
|
108
|
+
className: "size-lt-icon-md shrink-0"
|
|
109
|
+
}), t("form.create-option", "Create \"{{label}}\"", { label: query.trim() })]
|
|
110
|
+
}), visibleOptions.length === 0 && !(creatable && query.trim() !== "" && !exactMatch) ? /* @__PURE__ */ jsx("p", {
|
|
72
111
|
className: "px-3 py-2 text-sm text-lt-muted-fg",
|
|
73
112
|
children: emptyLabel
|
|
74
113
|
}) : visibleOptions.map((option) => {
|
|
75
114
|
const isSelected = selected.includes(option.value);
|
|
76
115
|
return /* @__PURE__ */ jsxs("button", {
|
|
116
|
+
"aria-label": renderOption ? option.label : void 0,
|
|
77
117
|
"aria-selected": isSelected,
|
|
78
118
|
className: cn("flex w-full items-center justify-between gap-2 rounded-lt-sm px-3 py-1.5 text-left text-sm transition-colors hover:bg-lt-accent hover:text-lt-accent-fg", isSelected && "bg-lt-accent/60"),
|
|
79
119
|
"data-slot": "combobox-option",
|
|
@@ -82,13 +122,16 @@ function Combobox({ contentClassName, emptyLabel, loading = false, multiple = fa
|
|
|
82
122
|
onClick: () => choose(option.value),
|
|
83
123
|
role: "option",
|
|
84
124
|
type: "button",
|
|
85
|
-
children: [
|
|
125
|
+
children: [renderOption ? /* @__PURE__ */ jsx("span", {
|
|
126
|
+
className: "flex min-w-0 flex-1 items-center gap-2",
|
|
127
|
+
children: renderOption(option)
|
|
128
|
+
}) : option.label, isSelected && /* @__PURE__ */ jsx(Icon, {
|
|
86
129
|
name: "check",
|
|
87
130
|
"aria-hidden": "true",
|
|
88
131
|
className: "size-lt-icon-md shrink-0"
|
|
89
132
|
})]
|
|
90
133
|
}, option.value);
|
|
91
|
-
})
|
|
134
|
+
})]
|
|
92
135
|
})]
|
|
93
136
|
})]
|
|
94
137
|
});
|
package/dist/ui/combobox.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combobox.js","names":[],"sources":["../../resources/js/ui/combobox.tsx"],"sourcesContent":["import { Icon } from \"@lattice-php/lattice/icons\";\nimport * as React from \"react\";\nimport { useEffect, useState } from \"react\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport type { Option } from \"@lattice-php/lattice/core/types\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"./popover\";\n\nconst SEARCH_DEBOUNCE_MS = 250;\n\n/**\n * A popover select list with an optional search box and single/multi selection.\n *\n * Selection state is controlled by the consumer (`selected` + `onSelect`); the\n * consumer also owns option fetching. Pass `onSearch` for remote search (the\n * combobox debounces the query and renders `options` as given); omit it to\n * filter the provided `options` locally by label. The combobox closes itself\n * after a single-select.\n */\nfunction Combobox({\n contentClassName,\n emptyLabel,\n loading = false,\n multiple = false,\n onSearch,\n onSelect,\n open,\n onOpenChange,\n options,\n searchLabel,\n searchPlaceholder,\n selected,\n showSearch = true,\n testId,\n trigger,\n triggerClassName,\n triggerProps,\n}: {\n contentClassName?: string;\n emptyLabel?: string;\n loading?: boolean;\n multiple?: boolean;\n onSearch?: (query: string) => void;\n onSelect: (value: string) => void;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n options: Option[];\n searchLabel?: string;\n searchPlaceholder?: string;\n selected: string[];\n showSearch?: boolean;\n testId?: string;\n trigger: React.ReactNode;\n triggerClassName?: string;\n triggerProps?: React.ComponentProps<\"button\"> & { \"data-test\"?: string };\n}) {\n const { t } = useT(\"lattice\");\n const [query, setQuery] = useState(\"\");\n\n useEffect(() => {\n if (!onSearch || !open) {\n return;\n }\n\n const timer = window.setTimeout(() => onSearch(query), SEARCH_DEBOUNCE_MS);\n\n return () => window.clearTimeout(timer);\n }, [query, onSearch, open]);\n\n const visibleOptions = onSearch\n ? options\n : options.filter((option) => option.label.toLowerCase().includes(query.toLowerCase()));\n\n function close(): void {\n setQuery(\"\");\n onOpenChange(false);\n }\n\n function choose(value: string): void {\n onSelect(value);\n\n if (!multiple) {\n close();\n }\n }\n\n return (\n <Popover open={open} onOpenChange={(next) => (next ? onOpenChange(true) : close())}>\n <PopoverTrigger asChild>\n <button type=\"button\" className={triggerClassName} {...triggerProps}>\n {trigger}\n </button>\n </PopoverTrigger>\n\n <PopoverContent\n align=\"start\"\n className={cn(\n \"w-[var(--radix-popover-trigger-width)] overflow-hidden p-0\",\n contentClassName,\n )}\n >\n {showSearch && (\n <div className=\"flex items-center gap-2 border-b border-lt-border px-3 py-2\">\n <input\n aria-label={searchLabel ?? t(\"form.searchOptions\", \"Search options\")}\n data-slot=\"combobox-search\"\n data-test={testId ? `${testId}-search` : undefined}\n className=\"w-full bg-transparent text-sm outline-none placeholder:text-lt-muted-fg\"\n onChange={(event) => setQuery(event.target.value)}\n placeholder={searchPlaceholder}\n value={query}\n />\n {loading && (\n <Icon\n name=\"loader-2\"\n aria-hidden=\"true\"\n className=\"size-lt-icon-md shrink-0 animate-spin text-lt-muted-fg\"\n />\n )}\n </div>\n )}\n\n <div className=\"max-h-60 overflow-y-auto p-1\" role=\"listbox\">\n {visibleOptions.length === 0 ? (\n <p className=\"px-3 py-2 text-sm text-lt-muted-fg\">{emptyLabel}</p>\n ) : (\n visibleOptions.map((option) => {\n const isSelected = selected.includes(option.value);\n\n return (\n <button\n aria-selected={isSelected}\n className={cn(\n \"flex w-full items-center justify-between gap-2 rounded-lt-sm px-3 py-1.5 text-left text-sm transition-colors hover:bg-lt-accent hover:text-lt-accent-fg\",\n isSelected && \"bg-lt-accent/60\",\n )}\n data-slot=\"combobox-option\"\n data-test={testId ? `${testId}-option-${option.value}` : undefined}\n data-value={option.value}\n key={option.value}\n onClick={() => choose(option.value)}\n role=\"option\"\n type=\"button\"\n >\n {option.label}\n {isSelected && (\n <Icon name=\"check\" aria-hidden=\"true\" className=\"size-lt-icon-md shrink-0\" />\n )}\n </button>\n );\n })\n )}\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n\nexport { Combobox };\n"],"mappings":";;;;;;;AAQA,IAAM,qBAAqB;;;;;;;;;;AAW3B,SAAS,SAAS,EAChB,kBACA,YACA,UAAU,OACV,WAAW,OACX,UACA,UACA,MACA,cACA,SACA,aACA,mBACA,UACA,aAAa,MACb,QACA,SACA,kBACA,gBAmBC;CACD,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,CAAC,OAAO,YAAY,SAAS,EAAE;CAErC,gBAAgB;EACd,IAAI,CAAC,YAAY,CAAC,MAChB;EAGF,MAAM,QAAQ,OAAO,iBAAiB,SAAS,KAAK,GAAG,kBAAkB;EAEzE,aAAa,OAAO,aAAa,KAAK;CACxC,GAAG;EAAC;EAAO;EAAU;CAAI,CAAC;CAE1B,MAAM,iBAAiB,WACnB,UACA,QAAQ,QAAQ,WAAW,OAAO,MAAM,YAAY,EAAE,SAAS,MAAM,YAAY,CAAC,CAAC;CAEvF,SAAS,QAAc;EACrB,SAAS,EAAE;EACX,aAAa,KAAK;CACpB;CAEA,SAAS,OAAO,OAAqB;EACnC,SAAS,KAAK;EAEd,IAAI,CAAC,UACH,MAAM;CAEV;CAEA,OACE,qBAAC,SAAD;EAAe;EAAM,eAAe,SAAU,OAAO,aAAa,IAAI,IAAI,MAAM;YAAhF,CACE,oBAAC,gBAAD;GAAgB,SAAA;aACd,oBAAC,UAAD;IAAQ,MAAK;IAAS,WAAW;IAAkB,GAAI;cACpD;GACK,CAAA;EACM,CAAA,GAEhB,qBAAC,gBAAD;GACE,OAAM;GACN,WAAW,GACT,8DACA,gBACF;aALF,CAOG,cACC,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,SAAD;KACE,cAAY,eAAe,EAAE,sBAAsB,gBAAgB;KACnE,aAAU;KACV,aAAW,SAAS,GAAG,OAAO,WAAW,KAAA;KACzC,WAAU;KACV,WAAW,UAAU,SAAS,MAAM,OAAO,KAAK;KAChD,aAAa;KACb,OAAO;IACR,CAAA,GACA,WACC,oBAAC,MAAD;KACE,MAAK;KACL,eAAY;KACZ,WAAU;IACX,CAAA,CAEA;OAGP,oBAAC,OAAD;IAAK,WAAU;IAA+B,MAAK;cAChD,eAAe,WAAW,IACzB,oBAAC,KAAD;KAAG,WAAU;eAAsC;IAAc,CAAA,IAEjE,eAAe,KAAK,WAAW;KAC7B,MAAM,aAAa,SAAS,SAAS,OAAO,KAAK;KAEjD,OACE,qBAAC,UAAD;MACE,iBAAe;MACf,WAAW,GACT,2JACA,cAAc,iBAChB;MACA,aAAU;MACV,aAAW,SAAS,GAAG,OAAO,UAAU,OAAO,UAAU,KAAA;MACzD,cAAY,OAAO;MAEnB,eAAe,OAAO,OAAO,KAAK;MAClC,MAAK;MACL,MAAK;gBAZP,CAcG,OAAO,OACP,cACC,oBAAC,MAAD;OAAM,MAAK;OAAQ,eAAY;OAAO,WAAU;MAA4B,CAAA,CAExE;QATD,OAAO,KASN;IAEZ,CAAC;GAEA,CAAA,CACS;IACT;;AAEb"}
|
|
1
|
+
{"version":3,"file":"combobox.js","names":[],"sources":["../../resources/js/ui/combobox.tsx"],"sourcesContent":["import { Icon } from \"@lattice-php/lattice/icons\";\nimport * as React from \"react\";\nimport { useEffect, useState } from \"react\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport type { Option } from \"@lattice-php/lattice/core/types\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"./popover\";\n\nconst SEARCH_DEBOUNCE_MS = 250;\n\n/**\n * A popover select list with an optional search box and single/multi selection.\n *\n * Selection state is controlled by the consumer (`selected` + `onSelect`); the\n * consumer also owns option fetching. Pass `onSearch` for remote search (the\n * combobox debounces the query and renders `options` as given); omit it to\n * filter the provided `options` locally by label. The combobox closes itself\n * after a single-select. Pass `renderOption` to render rich option rows; the\n * option's label stays the accessible name. `onSelect` toggles selection\n * (used by dropdown-row clicks); tag-entry commits (Enter/comma/paste) that\n * match an existing option call `onCommit` instead, falling back to\n * `onSelect` when it is not provided, so consumers can make tag entry\n * additive instead of toggling.\n */\nfunction Combobox({\n contentClassName,\n creatable = false,\n emptyLabel,\n loading = false,\n multiple = false,\n onCommit,\n onCreate,\n onSearch,\n onSelect,\n open,\n onOpenChange,\n options,\n renderOption,\n searchLabel,\n searchPlaceholder,\n selected,\n showSearch = true,\n testId,\n trigger,\n triggerClassName,\n triggerProps,\n}: {\n contentClassName?: string;\n creatable?: boolean;\n emptyLabel?: string;\n loading?: boolean;\n multiple?: boolean;\n onCommit?: (value: string) => void;\n onCreate?: (label: string) => void;\n onSearch?: (query: string) => void;\n onSelect: (value: string) => void;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n options: Option[];\n renderOption?: (option: Option) => React.ReactNode;\n searchLabel?: string;\n searchPlaceholder?: string;\n selected: string[];\n showSearch?: boolean;\n testId?: string;\n trigger: React.ReactNode;\n triggerClassName?: string;\n triggerProps?: React.ComponentProps<\"button\"> & { \"data-test\"?: string };\n}) {\n const { t } = useT(\"lattice\");\n const [query, setQuery] = useState(\"\");\n\n function commitCreate(raw: string): void {\n const tokens = raw\n .split(\",\")\n .map((token) => token.trim())\n .filter(Boolean);\n\n for (const token of tokens) {\n const match = options.find((option) => option.label.toLowerCase() === token.toLowerCase());\n\n if (match) {\n (onCommit ?? onSelect)(match.value);\n } else {\n onCreate?.(token);\n }\n }\n\n if (multiple) {\n setQuery(\"\");\n } else {\n close();\n }\n }\n\n const exactMatch = options.some(\n (option) => option.label.toLowerCase() === query.trim().toLowerCase(),\n );\n\n useEffect(() => {\n if (!onSearch || !open) {\n return;\n }\n\n const timer = window.setTimeout(() => onSearch(query), SEARCH_DEBOUNCE_MS);\n\n return () => window.clearTimeout(timer);\n }, [query, onSearch, open]);\n\n const visibleOptions = onSearch\n ? options\n : options.filter((option) => option.label.toLowerCase().includes(query.toLowerCase()));\n\n function close(): void {\n setQuery(\"\");\n onOpenChange(false);\n }\n\n function choose(value: string): void {\n onSelect(value);\n\n if (!multiple) {\n close();\n }\n }\n\n return (\n <Popover open={open} onOpenChange={(next) => (next ? onOpenChange(true) : close())}>\n <PopoverTrigger asChild>\n <button type=\"button\" className={triggerClassName} {...triggerProps}>\n {trigger}\n </button>\n </PopoverTrigger>\n\n <PopoverContent\n align=\"start\"\n className={cn(\n \"w-[var(--radix-popover-trigger-width)] overflow-hidden p-0\",\n contentClassName,\n )}\n >\n {showSearch && (\n <div className=\"flex items-center gap-2 border-b border-lt-border px-3 py-2\">\n <input\n aria-label={searchLabel ?? t(\"form.search-options\", \"Search options\")}\n data-slot=\"combobox-search\"\n data-test={testId ? `${testId}-search` : undefined}\n className=\"w-full bg-transparent text-sm outline-none placeholder:text-lt-muted-fg\"\n onChange={(event) => {\n const next = event.target.value;\n\n if (creatable && next.includes(\",\")) {\n commitCreate(next);\n\n return;\n }\n\n setQuery(next);\n }}\n onKeyDown={(event) => {\n if (creatable && event.key === \"Enter\" && query.trim() !== \"\") {\n event.preventDefault();\n commitCreate(query);\n }\n }}\n placeholder={searchPlaceholder}\n value={query}\n />\n {loading && (\n <Icon\n name=\"loader-2\"\n aria-hidden=\"true\"\n className=\"size-lt-icon-md shrink-0 animate-spin text-lt-muted-fg\"\n />\n )}\n </div>\n )}\n\n <div className=\"max-h-60 overflow-y-auto p-1\" role=\"listbox\">\n {creatable && query.trim() !== \"\" && !exactMatch && (\n <button\n className=\"flex w-full items-center gap-2 rounded-lt-sm px-3 py-1.5 text-left text-sm transition-colors hover:bg-lt-accent hover:text-lt-accent-fg\"\n data-test={testId ? `${testId}-create` : undefined}\n onClick={() => commitCreate(query)}\n type=\"button\"\n >\n <Icon name=\"plus\" aria-hidden=\"true\" className=\"size-lt-icon-md shrink-0\" />\n {t(\"form.create-option\", 'Create \"{{label}}\"', { label: query.trim() })}\n </button>\n )}\n\n {visibleOptions.length === 0 && !(creatable && query.trim() !== \"\" && !exactMatch) ? (\n <p className=\"px-3 py-2 text-sm text-lt-muted-fg\">{emptyLabel}</p>\n ) : (\n visibleOptions.map((option) => {\n const isSelected = selected.includes(option.value);\n\n return (\n <button\n aria-label={renderOption ? option.label : undefined}\n aria-selected={isSelected}\n className={cn(\n \"flex w-full items-center justify-between gap-2 rounded-lt-sm px-3 py-1.5 text-left text-sm transition-colors hover:bg-lt-accent hover:text-lt-accent-fg\",\n isSelected && \"bg-lt-accent/60\",\n )}\n data-slot=\"combobox-option\"\n data-test={testId ? `${testId}-option-${option.value}` : undefined}\n data-value={option.value}\n key={option.value}\n onClick={() => choose(option.value)}\n role=\"option\"\n type=\"button\"\n >\n {renderOption ? (\n <span className=\"flex min-w-0 flex-1 items-center gap-2\">\n {renderOption(option)}\n </span>\n ) : (\n option.label\n )}\n {isSelected && (\n <Icon name=\"check\" aria-hidden=\"true\" className=\"size-lt-icon-md shrink-0\" />\n )}\n </button>\n );\n })\n )}\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n\nexport { Combobox };\n"],"mappings":";;;;;;;AAQA,IAAM,qBAAqB;;;;;;;;;;;;;;;AAgB3B,SAAS,SAAS,EAChB,kBACA,YAAY,OACZ,YACA,UAAU,OACV,WAAW,OACX,UACA,UACA,UACA,UACA,MACA,cACA,SACA,cACA,aACA,mBACA,UACA,aAAa,MACb,QACA,SACA,kBACA,gBAuBC;CACD,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,CAAC,OAAO,YAAY,SAAS,EAAE;CAErC,SAAS,aAAa,KAAmB;EACvC,MAAM,SAAS,IACZ,MAAM,GAAG,EACT,KAAK,UAAU,MAAM,KAAK,CAAC,EAC3B,OAAO,OAAO;EAEjB,KAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,QAAQ,QAAQ,MAAM,WAAW,OAAO,MAAM,YAAY,MAAM,MAAM,YAAY,CAAC;GAEzF,IAAI,OACF,CAAC,YAAY,UAAU,MAAM,KAAK;QAElC,WAAW,KAAK;EAEpB;EAEA,IAAI,UACF,SAAS,EAAE;OAEX,MAAM;CAEV;CAEA,MAAM,aAAa,QAAQ,MACxB,WAAW,OAAO,MAAM,YAAY,MAAM,MAAM,KAAK,EAAE,YAAY,CACtE;CAEA,gBAAgB;EACd,IAAI,CAAC,YAAY,CAAC,MAChB;EAGF,MAAM,QAAQ,OAAO,iBAAiB,SAAS,KAAK,GAAG,kBAAkB;EAEzE,aAAa,OAAO,aAAa,KAAK;CACxC,GAAG;EAAC;EAAO;EAAU;CAAI,CAAC;CAE1B,MAAM,iBAAiB,WACnB,UACA,QAAQ,QAAQ,WAAW,OAAO,MAAM,YAAY,EAAE,SAAS,MAAM,YAAY,CAAC,CAAC;CAEvF,SAAS,QAAc;EACrB,SAAS,EAAE;EACX,aAAa,KAAK;CACpB;CAEA,SAAS,OAAO,OAAqB;EACnC,SAAS,KAAK;EAEd,IAAI,CAAC,UACH,MAAM;CAEV;CAEA,OACE,qBAAC,SAAD;EAAe;EAAM,eAAe,SAAU,OAAO,aAAa,IAAI,IAAI,MAAM;YAAhF,CACE,oBAAC,gBAAD;GAAgB,SAAA;aACd,oBAAC,UAAD;IAAQ,MAAK;IAAS,WAAW;IAAkB,GAAI;cACpD;GACK,CAAA;EACM,CAAA,GAEhB,qBAAC,gBAAD;GACE,OAAM;GACN,WAAW,GACT,8DACA,gBACF;aALF,CAOG,cACC,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,SAAD;KACE,cAAY,eAAe,EAAE,uBAAuB,gBAAgB;KACpE,aAAU;KACV,aAAW,SAAS,GAAG,OAAO,WAAW,KAAA;KACzC,WAAU;KACV,WAAW,UAAU;MACnB,MAAM,OAAO,MAAM,OAAO;MAE1B,IAAI,aAAa,KAAK,SAAS,GAAG,GAAG;OACnC,aAAa,IAAI;OAEjB;MACF;MAEA,SAAS,IAAI;KACf;KACA,YAAY,UAAU;MACpB,IAAI,aAAa,MAAM,QAAQ,WAAW,MAAM,KAAK,MAAM,IAAI;OAC7D,MAAM,eAAe;OACrB,aAAa,KAAK;MACpB;KACF;KACA,aAAa;KACb,OAAO;IACR,CAAA,GACA,WACC,oBAAC,MAAD;KACE,MAAK;KACL,eAAY;KACZ,WAAU;IACX,CAAA,CAEA;OAGP,qBAAC,OAAD;IAAK,WAAU;IAA+B,MAAK;cAAnD,CACG,aAAa,MAAM,KAAK,MAAM,MAAM,CAAC,cACpC,qBAAC,UAAD;KACE,WAAU;KACV,aAAW,SAAS,GAAG,OAAO,WAAW,KAAA;KACzC,eAAe,aAAa,KAAK;KACjC,MAAK;eAJP,CAME,oBAAC,MAAD;MAAM,MAAK;MAAO,eAAY;MAAO,WAAU;KAA4B,CAAA,GAC1E,EAAE,sBAAsB,wBAAsB,EAAE,OAAO,MAAM,KAAK,EAAE,CAAC,CAChE;QAGT,eAAe,WAAW,KAAK,EAAE,aAAa,MAAM,KAAK,MAAM,MAAM,CAAC,cACrE,oBAAC,KAAD;KAAG,WAAU;eAAsC;IAAc,CAAA,IAEjE,eAAe,KAAK,WAAW;KAC7B,MAAM,aAAa,SAAS,SAAS,OAAO,KAAK;KAEjD,OACE,qBAAC,UAAD;MACE,cAAY,eAAe,OAAO,QAAQ,KAAA;MAC1C,iBAAe;MACf,WAAW,GACT,2JACA,cAAc,iBAChB;MACA,aAAU;MACV,aAAW,SAAS,GAAG,OAAO,UAAU,OAAO,UAAU,KAAA;MACzD,cAAY,OAAO;MAEnB,eAAe,OAAO,OAAO,KAAK;MAClC,MAAK;MACL,MAAK;gBAbP,CAeG,eACC,oBAAC,QAAD;OAAM,WAAU;iBACb,aAAa,MAAM;MAChB,CAAA,IAEN,OAAO,OAER,cACC,oBAAC,MAAD;OAAM,MAAK;OAAQ,eAAY;OAAO,WAAU;MAA4B,CAAA,CAExE;QAfD,OAAO,KAeN;IAEZ,CAAC,CAEA;KACS;IACT;;AAEb"}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
export declare function copyToClipboard(text: string): Promise<boolean>;
|
|
3
|
+
interface CopyButtonProps {
|
|
4
|
+
value: string;
|
|
5
|
+
label: string;
|
|
6
|
+
testId?: string;
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function CopyButton({ value, label, testId, className }: CopyButtonProps): ReactNode;
|
|
3
10
|
interface CopyableTextProps {
|
|
4
11
|
value: string;
|
|
5
12
|
label: string;
|
package/dist/ui/copyable-text.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
1
2
|
import { Icon } from "../icons/sprite.js";
|
|
2
3
|
import { useT } from "../i18n/instance.js";
|
|
3
4
|
import { useEffect, useState } from "react";
|
|
@@ -12,7 +13,7 @@ async function copyToClipboard(text) {
|
|
|
12
13
|
return false;
|
|
13
14
|
}
|
|
14
15
|
}
|
|
15
|
-
function
|
|
16
|
+
function CopyButton({ value, label, testId, className }) {
|
|
16
17
|
const { t } = useT("lattice");
|
|
17
18
|
const [copied, setCopied] = useState(false);
|
|
18
19
|
useEffect(() => {
|
|
@@ -24,23 +25,30 @@ function CopyableText({ value, label, testId, children }) {
|
|
|
24
25
|
copyToClipboard(value);
|
|
25
26
|
setCopied(true);
|
|
26
27
|
}
|
|
27
|
-
const ariaLabel = copied ? t("common.
|
|
28
|
+
const ariaLabel = copied ? t("common.copied-value", "Copied {{label}}", { label }) : t("common.copy-value", "Copy {{label}}", { label });
|
|
29
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
30
|
+
type: "button",
|
|
31
|
+
"data-test": testId,
|
|
32
|
+
className: cn("inline-flex items-center gap-1 rounded-lt-sm border border-lt-border px-2 py-1 text-xs", className),
|
|
33
|
+
"aria-label": ariaLabel,
|
|
34
|
+
onClick: handleCopy,
|
|
35
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
36
|
+
name: copied ? "check" : "copy",
|
|
37
|
+
className: "size-lt-icon-xs"
|
|
38
|
+
}), copied ? t("common.copied", "Copied") : t("common.copy", "Copy")]
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function CopyableText({ value, label, testId, children }) {
|
|
28
42
|
return /* @__PURE__ */ jsxs("div", {
|
|
29
43
|
className: "inline-flex items-center gap-2",
|
|
30
|
-
children: [children ?? value, /* @__PURE__ */
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"aria-label": ariaLabel,
|
|
35
|
-
onClick: handleCopy,
|
|
36
|
-
children: [/* @__PURE__ */ jsx(Icon, {
|
|
37
|
-
name: copied ? "check" : "copy",
|
|
38
|
-
className: "size-lt-icon-xs"
|
|
39
|
-
}), copied ? t("common.copied", "Copied") : t("common.copy", "Copy")]
|
|
44
|
+
children: [children ?? value, /* @__PURE__ */ jsx(CopyButton, {
|
|
45
|
+
value,
|
|
46
|
+
label,
|
|
47
|
+
testId
|
|
40
48
|
})]
|
|
41
49
|
});
|
|
42
50
|
}
|
|
43
51
|
//#endregion
|
|
44
|
-
export { CopyableText, copyToClipboard };
|
|
52
|
+
export { CopyButton, CopyableText, copyToClipboard };
|
|
45
53
|
|
|
46
54
|
//# sourceMappingURL=copyable-text.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copyable-text.js","names":[],"sources":["../../resources/js/ui/copyable-text.tsx"],"sourcesContent":["import { useT } from \"@lattice-php/lattice/i18n\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { type ReactNode, useEffect, useState } from \"react\";\n\nexport async function copyToClipboard(text: string): Promise<boolean> {\n if (!navigator?.clipboard) {\n return false;\n }\n\n try {\n await navigator.clipboard.writeText(text);\n\n return true;\n } catch {\n return false;\n }\n}\n\ninterface
|
|
1
|
+
{"version":3,"file":"copyable-text.js","names":[],"sources":["../../resources/js/ui/copyable-text.tsx"],"sourcesContent":["import { useT } from \"@lattice-php/lattice/i18n\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { type ReactNode, useEffect, useState } from \"react\";\n\nexport async function copyToClipboard(text: string): Promise<boolean> {\n if (!navigator?.clipboard) {\n return false;\n }\n\n try {\n await navigator.clipboard.writeText(text);\n\n return true;\n } catch {\n return false;\n }\n}\n\ninterface CopyButtonProps {\n value: string;\n label: string;\n testId?: string;\n className?: string;\n}\n\nexport function CopyButton({ value, label, testId, className }: CopyButtonProps): ReactNode {\n const { t } = useT(\"lattice\");\n const [copied, setCopied] = useState(false);\n\n useEffect(() => {\n if (!copied) {\n return;\n }\n\n const timeout = window.setTimeout(() => setCopied(false), 1500);\n\n return () => window.clearTimeout(timeout);\n }, [copied]);\n\n function handleCopy(): void {\n void copyToClipboard(value);\n setCopied(true);\n }\n\n const ariaLabel = copied\n ? t(\"common.copied-value\", \"Copied {{label}}\", { label })\n : t(\"common.copy-value\", \"Copy {{label}}\", { label });\n\n return (\n <button\n type=\"button\"\n data-test={testId}\n className={cn(\n \"inline-flex items-center gap-1 rounded-lt-sm border border-lt-border px-2 py-1 text-xs\",\n className,\n )}\n aria-label={ariaLabel}\n onClick={handleCopy}\n >\n <Icon name={copied ? \"check\" : \"copy\"} className=\"size-lt-icon-xs\" />\n {copied ? t(\"common.copied\", \"Copied\") : t(\"common.copy\", \"Copy\")}\n </button>\n );\n}\n\ninterface CopyableTextProps {\n value: string;\n label: string;\n testId?: string;\n children?: ReactNode;\n}\n\nexport function CopyableText({ value, label, testId, children }: CopyableTextProps): ReactNode {\n return (\n <div className=\"inline-flex items-center gap-2\">\n {children ?? value}\n <CopyButton value={value} label={label} testId={testId} />\n </div>\n );\n}\n"],"mappings":";;;;;;AAKA,eAAsB,gBAAgB,MAAgC;CACpE,IAAI,CAAC,WAAW,WACd,OAAO;CAGT,IAAI;EACF,MAAM,UAAU,UAAU,UAAU,IAAI;EAExC,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AASA,SAAgB,WAAW,EAAE,OAAO,OAAO,QAAQ,aAAyC;CAC1F,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,CAAC,QAAQ,aAAa,SAAS,KAAK;CAE1C,gBAAgB;EACd,IAAI,CAAC,QACH;EAGF,MAAM,UAAU,OAAO,iBAAiB,UAAU,KAAK,GAAG,IAAI;EAE9D,aAAa,OAAO,aAAa,OAAO;CAC1C,GAAG,CAAC,MAAM,CAAC;CAEX,SAAS,aAAmB;EAC1B,gBAAqB,KAAK;EAC1B,UAAU,IAAI;CAChB;CAEA,MAAM,YAAY,SACd,EAAE,uBAAuB,oBAAoB,EAAE,MAAM,CAAC,IACtD,EAAE,qBAAqB,kBAAkB,EAAE,MAAM,CAAC;CAEtD,OACE,qBAAC,UAAD;EACE,MAAK;EACL,aAAW;EACX,WAAW,GACT,0FACA,SACF;EACA,cAAY;EACZ,SAAS;YARX,CAUE,oBAAC,MAAD;GAAM,MAAM,SAAS,UAAU;GAAQ,WAAU;EAAmB,CAAA,GACnE,SAAS,EAAE,iBAAiB,QAAQ,IAAI,EAAE,eAAe,MAAM,CAC1D;;AAEZ;AASA,SAAgB,aAAa,EAAE,OAAO,OAAO,QAAQ,YAA0C;CAC7F,OACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACG,YAAY,OACb,oBAAC,YAAD;GAAmB;GAAc;GAAe;EAAS,CAAA,CACtD;;AAET"}
|
package/dist/ui/fragment.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { LATTICE_EVENT } from "../events/event-names.js";
|
|
2
|
+
import { toNodes } from "../core/nodes.js";
|
|
2
3
|
import { Renderer } from "../core/renderer.js";
|
|
3
4
|
import { apiJson } from "../core/api.js";
|
|
4
5
|
import { Skeleton } from "./skeleton.js";
|
|
5
|
-
import { toNodes } from "../core/nodes.js";
|
|
6
6
|
import { useCallback, useEffect, useState } from "react";
|
|
7
7
|
import { jsx } from "react/jsx-runtime";
|
|
8
8
|
//#region resources/js/ui/fragment.tsx
|
package/dist/ui/fragment.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fragment.js","names":[],"sources":["../../resources/js/ui/fragment.tsx"],"sourcesContent":["import { useCallback, useEffect, useState } from \"react\";\nimport { apiJson } from \"@lattice-php/lattice/core/api\";\nimport { Skeleton } from \"@lattice-php/lattice/ui/skeleton\";\nimport { Renderer } from \"@lattice-php/lattice/core/renderer\";\nimport type { RendererComponent
|
|
1
|
+
{"version":3,"file":"fragment.js","names":[],"sources":["../../resources/js/ui/fragment.tsx"],"sourcesContent":["import { useCallback, useEffect, useState } from \"react\";\nimport { apiJson } from \"@lattice-php/lattice/core/api\";\nimport { Skeleton } from \"@lattice-php/lattice/ui/skeleton\";\nimport { Renderer } from \"@lattice-php/lattice/core/renderer\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport type { FragmentResponse } from \"@lattice-php/lattice/types/generated\";\nimport { toNodes } from \"@lattice-php/lattice/core/nodes\";\nimport { LATTICE_EVENT, type ReloadComponentEvent } from \"@lattice-php/lattice/events/event-names\";\n\nconst fragmentSizeHeights = {\n lg: 320,\n md: 64,\n sm: 40,\n xl: 384,\n xs: 32,\n \"2xl\": 448,\n \"3xl\": 512,\n \"4xl\": 576,\n} as const;\n\nconst FragmentComponent: RendererComponent<\"fragment\"> = ({ node }) => {\n const endpoint = node.props.endpoint ?? \"\";\n const isLazy = node.props.lazy === true;\n const componentRef = node.props.ref ?? \"\";\n const placeholderHeight = fragmentSizeHeights[node.props.size];\n const [components, setComponents] = useState(() => node.schema ?? []);\n const [hasLoaded, setHasLoaded] = useState(!isLazy);\n const [processing, setProcessing] = useState(isLazy && endpoint !== \"\");\n\n const load = useCallback(async (): Promise<void> => {\n if (!endpoint) {\n return;\n }\n\n setProcessing(true);\n\n try {\n const result = await apiJson<FragmentResponse>(endpoint, { ref: componentRef });\n\n setComponents(toNodes(result.schema));\n setHasLoaded(true);\n } finally {\n setProcessing(false);\n }\n }, [endpoint, componentRef]);\n\n useEffect(() => {\n if (!isLazy || hasLoaded) {\n return;\n }\n\n void load();\n }, [hasLoaded, isLazy, load]);\n\n useEffect(() => {\n function reload(event: Event): void {\n const detail = (event as ReloadComponentEvent).detail;\n\n if (detail?.component !== node.id) {\n return;\n }\n\n void load();\n }\n\n window.addEventListener(LATTICE_EVENT.reloadComponent, reload);\n\n return () => window.removeEventListener(LATTICE_EVENT.reloadComponent, reload);\n }, [load, node.id]);\n\n useEffect(() => {\n function reloadOnLocaleChange(): void {\n if (!hasLoaded) {\n return;\n }\n\n void load();\n }\n\n window.addEventListener(LATTICE_EVENT.localeChange, reloadOnLocaleChange);\n\n return () => window.removeEventListener(LATTICE_EVENT.localeChange, reloadOnLocaleChange);\n }, [hasLoaded, load]);\n\n return (\n <div\n data-lattice-fragment={node.id}\n style={processing && components.length === 0 ? { minHeight: placeholderHeight } : undefined}\n >\n {processing && components.length === 0 ? (\n <Skeleton className=\"w-full\" style={{ height: placeholderHeight }} />\n ) : (\n <Renderer nodes={components} />\n )}\n </div>\n );\n};\n\nexport default FragmentComponent;\n"],"mappings":";;;;;;;;AASA,IAAM,sBAAsB;CAC1B,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,OAAO;CACP,OAAO;CACP,OAAO;AACT;AAEA,IAAM,qBAAoD,EAAE,WAAW;CACrE,MAAM,WAAW,KAAK,MAAM,YAAY;CACxC,MAAM,SAAS,KAAK,MAAM,SAAS;CACnC,MAAM,eAAe,KAAK,MAAM,OAAO;CACvC,MAAM,oBAAoB,oBAAoB,KAAK,MAAM;CACzD,MAAM,CAAC,YAAY,iBAAiB,eAAe,KAAK,UAAU,CAAC,CAAC;CACpE,MAAM,CAAC,WAAW,gBAAgB,SAAS,CAAC,MAAM;CAClD,MAAM,CAAC,YAAY,iBAAiB,SAAS,UAAU,aAAa,EAAE;CAEtE,MAAM,OAAO,YAAY,YAA2B;EAClD,IAAI,CAAC,UACH;EAGF,cAAc,IAAI;EAElB,IAAI;GAGF,cAAc,SAAQ,MAFD,QAA0B,UAAU,EAAE,KAAK,aAAa,CAAC,GAEjD,MAAM,CAAC;GACpC,aAAa,IAAI;EACnB,UAAU;GACR,cAAc,KAAK;EACrB;CACF,GAAG,CAAC,UAAU,YAAY,CAAC;CAE3B,gBAAgB;EACd,IAAI,CAAC,UAAU,WACb;EAGF,KAAU;CACZ,GAAG;EAAC;EAAW;EAAQ;CAAI,CAAC;CAE5B,gBAAgB;EACd,SAAS,OAAO,OAAoB;GAGlC,IAFgB,MAA+B,QAEnC,cAAc,KAAK,IAC7B;GAGF,KAAU;EACZ;EAEA,OAAO,iBAAiB,cAAc,iBAAiB,MAAM;EAE7D,aAAa,OAAO,oBAAoB,cAAc,iBAAiB,MAAM;CAC/E,GAAG,CAAC,MAAM,KAAK,EAAE,CAAC;CAElB,gBAAgB;EACd,SAAS,uBAA6B;GACpC,IAAI,CAAC,WACH;GAGF,KAAU;EACZ;EAEA,OAAO,iBAAiB,cAAc,cAAc,oBAAoB;EAExE,aAAa,OAAO,oBAAoB,cAAc,cAAc,oBAAoB;CAC1F,GAAG,CAAC,WAAW,IAAI,CAAC;CAEpB,OACE,oBAAC,OAAD;EACE,yBAAuB,KAAK;EAC5B,OAAO,cAAc,WAAW,WAAW,IAAI,EAAE,WAAW,kBAAkB,IAAI,KAAA;YAEjF,cAAc,WAAW,WAAW,IACnC,oBAAC,UAAD;GAAU,WAAU;GAAS,OAAO,EAAE,QAAQ,kBAAkB;EAAI,CAAA,IAEpE,oBAAC,UAAD,EAAU,OAAO,WAAa,CAAA;CAE7B,CAAA;AAET"}
|
package/dist/ui/heading.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { cn } from "../lib/utils.js";
|
|
2
2
|
import { InfoTooltip } from "./info-tooltip.js";
|
|
3
|
+
import { CopyableText } from "./copyable-text.js";
|
|
3
4
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
5
|
//#region resources/js/ui/heading.tsx
|
|
5
6
|
var HeadingComponent = ({ node }) => {
|
|
@@ -7,32 +8,49 @@ var HeadingComponent = ({ node }) => {
|
|
|
7
8
|
const level = Math.min(Math.max(node.props.level, 1), 6);
|
|
8
9
|
const className = cn("max-w-3xl font-semibold tracking-normal text-balance text-lt-fg", level === 1 && "text-2xl font-bold leading-tight", level === 2 && "text-xl", level > 2 && "text-base");
|
|
9
10
|
const content = /* @__PURE__ */ jsxs(Fragment, { children: [text, /* @__PURE__ */ jsx(InfoTooltip, { content: tooltip })] });
|
|
11
|
+
let heading;
|
|
10
12
|
switch (level) {
|
|
11
|
-
case 1:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
case
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
13
|
+
case 1:
|
|
14
|
+
heading = /* @__PURE__ */ jsx("h1", {
|
|
15
|
+
className,
|
|
16
|
+
children: content
|
|
17
|
+
});
|
|
18
|
+
break;
|
|
19
|
+
case 2:
|
|
20
|
+
heading = /* @__PURE__ */ jsx("h2", {
|
|
21
|
+
className,
|
|
22
|
+
children: content
|
|
23
|
+
});
|
|
24
|
+
break;
|
|
25
|
+
case 3:
|
|
26
|
+
heading = /* @__PURE__ */ jsx("h3", {
|
|
27
|
+
className,
|
|
28
|
+
children: content
|
|
29
|
+
});
|
|
30
|
+
break;
|
|
31
|
+
case 4:
|
|
32
|
+
heading = /* @__PURE__ */ jsx("h4", {
|
|
33
|
+
className,
|
|
34
|
+
children: content
|
|
35
|
+
});
|
|
36
|
+
break;
|
|
37
|
+
case 5:
|
|
38
|
+
heading = /* @__PURE__ */ jsx("h5", {
|
|
39
|
+
className,
|
|
40
|
+
children: content
|
|
41
|
+
});
|
|
42
|
+
break;
|
|
43
|
+
default: heading = /* @__PURE__ */ jsx("h6", {
|
|
32
44
|
className,
|
|
33
45
|
children: content
|
|
34
46
|
});
|
|
35
47
|
}
|
|
48
|
+
if (!node.props.copyable) return heading;
|
|
49
|
+
return /* @__PURE__ */ jsx(CopyableText, {
|
|
50
|
+
value: text,
|
|
51
|
+
label: text,
|
|
52
|
+
children: heading
|
|
53
|
+
});
|
|
36
54
|
};
|
|
37
55
|
//#endregion
|
|
38
56
|
export { HeadingComponent as default };
|
package/dist/ui/heading.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"heading.js","names":[],"sources":["../../resources/js/ui/heading.tsx"],"sourcesContent":["import type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { InfoTooltip } from \"./info-tooltip\";\n\nconst HeadingComponent: RendererComponent<\"heading\"> = ({ node }) => {\n const { text, tooltip } = node.props;\n const level = Math.min(Math.max(node.props.level, 1), 6);\n const className = cn(\n \"max-w-3xl font-semibold tracking-normal text-balance text-lt-fg\",\n level === 1 && \"text-2xl font-bold leading-tight\",\n level === 2 && \"text-xl\",\n level > 2 && \"text-base\",\n );\n\n const content = (\n <>\n {text}\n <InfoTooltip content={tooltip} />\n </>\n );\n\n switch (level) {\n case 1:\n
|
|
1
|
+
{"version":3,"file":"heading.js","names":[],"sources":["../../resources/js/ui/heading.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { CopyableText } from \"./copyable-text\";\nimport { InfoTooltip } from \"./info-tooltip\";\n\nconst HeadingComponent: RendererComponent<\"heading\"> = ({ node }) => {\n const { text, tooltip } = node.props;\n const level = Math.min(Math.max(node.props.level, 1), 6);\n const className = cn(\n \"max-w-3xl font-semibold tracking-normal text-balance text-lt-fg\",\n level === 1 && \"text-2xl font-bold leading-tight\",\n level === 2 && \"text-xl\",\n level > 2 && \"text-base\",\n );\n\n const content = (\n <>\n {text}\n <InfoTooltip content={tooltip} />\n </>\n );\n\n let heading: ReactNode;\n\n switch (level) {\n case 1:\n heading = <h1 className={className}>{content}</h1>;\n break;\n case 2:\n heading = <h2 className={className}>{content}</h2>;\n break;\n case 3:\n heading = <h3 className={className}>{content}</h3>;\n break;\n case 4:\n heading = <h4 className={className}>{content}</h4>;\n break;\n case 5:\n heading = <h5 className={className}>{content}</h5>;\n break;\n default:\n heading = <h6 className={className}>{content}</h6>;\n }\n\n if (!node.props.copyable) {\n return heading;\n }\n\n return (\n <CopyableText value={text} label={text}>\n {heading}\n </CopyableText>\n );\n};\n\nexport default HeadingComponent;\n"],"mappings":";;;;;AAMA,IAAM,oBAAkD,EAAE,WAAW;CACnE,MAAM,EAAE,MAAM,YAAY,KAAK;CAC/B,MAAM,QAAQ,KAAK,IAAI,KAAK,IAAI,KAAK,MAAM,OAAO,CAAC,GAAG,CAAC;CACvD,MAAM,YAAY,GAChB,mEACA,UAAU,KAAK,oCACf,UAAU,KAAK,WACf,QAAQ,KAAK,WACf;CAEA,MAAM,UACJ,qBAAA,UAAA,EAAA,UAAA,CACG,MACD,oBAAC,aAAD,EAAa,SAAS,QAAU,CAAA,CAChC,EAAA,CAAA;CAGJ,IAAI;CAEJ,QAAQ,OAAR;EACE,KAAK;GACH,UAAU,oBAAC,MAAD;IAAe;cAAY;GAAY,CAAA;GACjD;EACF,KAAK;GACH,UAAU,oBAAC,MAAD;IAAe;cAAY;GAAY,CAAA;GACjD;EACF,KAAK;GACH,UAAU,oBAAC,MAAD;IAAe;cAAY;GAAY,CAAA;GACjD;EACF,KAAK;GACH,UAAU,oBAAC,MAAD;IAAe;cAAY;GAAY,CAAA;GACjD;EACF,KAAK;GACH,UAAU,oBAAC,MAAD;IAAe;cAAY;GAAY,CAAA;GACjD;EACF,SACE,UAAU,oBAAC,MAAD;GAAe;aAAY;EAAY,CAAA;CACrD;CAEA,IAAI,CAAC,KAAK,MAAM,UACd,OAAO;CAGT,OACE,oBAAC,cAAD;EAAc,OAAO;EAAM,OAAO;YAC/B;CACW,CAAA;AAElB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { VariantProps } from 'class-variance-authority';
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
/**
|
|
4
|
+
* A compact, resizable icon button — the shared affordance behind toolbar
|
|
5
|
+
* triggers, popover triggers, and inline clear/remove buttons. `ghost` matches
|
|
6
|
+
* `Button variant="ghost"` (accent hover) and reacts to `aria-pressed` (toggle)
|
|
7
|
+
* and `data-[state=open]` (a popover trigger). `segmented` joins onto the right
|
|
8
|
+
* of an adjacent input. Size defaults to `sm`; pass `size` to resize.
|
|
9
|
+
*/
|
|
10
|
+
declare const iconButtonVariants: (props?: ({
|
|
11
|
+
variant?: "ghost" | "segmented" | null | undefined;
|
|
12
|
+
size?: "xs" | "sm" | "md" | null | undefined;
|
|
13
|
+
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
14
|
+
export declare function IconButton({ icon, label, active, size, variant, className, children, ref, ...props }: Omit<React.ComponentProps<"button">, "aria-pressed"> & VariantProps<typeof iconButtonVariants> & {
|
|
15
|
+
/** Icon glyph name; omit and pass `children` for custom content. */
|
|
16
|
+
icon?: string;
|
|
17
|
+
/** Accessible name (also used when the button shows only an icon). */
|
|
18
|
+
label: string;
|
|
19
|
+
/** Toggle state — sets `aria-pressed` and the pressed styling. */
|
|
20
|
+
active?: boolean;
|
|
21
|
+
}): React.JSX.Element;
|
|
22
|
+
export { iconButtonVariants };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { Icon } from "../icons/sprite.js";
|
|
3
|
+
import "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { cva } from "class-variance-authority";
|
|
6
|
+
//#region resources/js/ui/icon-button.tsx
|
|
7
|
+
/**
|
|
8
|
+
* A compact, resizable icon button — the shared affordance behind toolbar
|
|
9
|
+
* triggers, popover triggers, and inline clear/remove buttons. `ghost` matches
|
|
10
|
+
* `Button variant="ghost"` (accent hover) and reacts to `aria-pressed` (toggle)
|
|
11
|
+
* and `data-[state=open]` (a popover trigger). `segmented` joins onto the right
|
|
12
|
+
* of an adjacent input. Size defaults to `sm`; pass `size` to resize.
|
|
13
|
+
*/
|
|
14
|
+
var iconButtonVariants = cva(cn("relative inline-flex shrink-0 items-center justify-center rounded-lt-sm transition-colors", "outline-none focus-visible:ring-2 focus-visible:ring-lt-ring/50", "disabled:pointer-events-none disabled:opacity-50"), {
|
|
15
|
+
variants: {
|
|
16
|
+
variant: {
|
|
17
|
+
ghost: cn("text-lt-muted-fg hover:bg-lt-accent hover:text-lt-accent-fg", "data-[state=open]:bg-lt-accent data-[state=open]:text-lt-accent-fg", "aria-pressed:bg-lt-accent aria-pressed:text-lt-accent-fg"),
|
|
18
|
+
segmented: cn("-ml-px rounded-l-none border border-lt-input text-lt-muted-fg hover:bg-lt-accent hover:text-lt-accent-fg", "data-[state=open]:z-10 data-[state=open]:border-lt-primary data-[state=open]:text-lt-fg")
|
|
19
|
+
},
|
|
20
|
+
size: {
|
|
21
|
+
xs: "size-5 [&_svg]:size-lt-icon-sm",
|
|
22
|
+
sm: "size-7 [&_svg]:size-lt-icon-md",
|
|
23
|
+
md: "size-lt-control-md [&_svg]:size-lt-icon-md"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
defaultVariants: {
|
|
27
|
+
variant: "ghost",
|
|
28
|
+
size: "sm"
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
function IconButton({ icon, label, active, size, variant, className, children, ref, ...props }) {
|
|
32
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
33
|
+
ref,
|
|
34
|
+
type: "button",
|
|
35
|
+
"aria-label": label,
|
|
36
|
+
"aria-pressed": active,
|
|
37
|
+
className: cn(iconButtonVariants({
|
|
38
|
+
variant,
|
|
39
|
+
size
|
|
40
|
+
}), className),
|
|
41
|
+
...props,
|
|
42
|
+
children: [icon ? /* @__PURE__ */ jsx(Icon, {
|
|
43
|
+
name: icon,
|
|
44
|
+
"aria-hidden": "true"
|
|
45
|
+
}) : null, children]
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
export { IconButton, iconButtonVariants };
|
|
50
|
+
|
|
51
|
+
//# sourceMappingURL=icon-button.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icon-button.js","names":[],"sources":["../../resources/js/ui/icon-button.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\n\n/**\n * A compact, resizable icon button — the shared affordance behind toolbar\n * triggers, popover triggers, and inline clear/remove buttons. `ghost` matches\n * `Button variant=\"ghost\"` (accent hover) and reacts to `aria-pressed` (toggle)\n * and `data-[state=open]` (a popover trigger). `segmented` joins onto the right\n * of an adjacent input. Size defaults to `sm`; pass `size` to resize.\n */\nconst iconButtonVariants = cva(\n cn(\n \"relative inline-flex shrink-0 items-center justify-center rounded-lt-sm transition-colors\",\n \"outline-none focus-visible:ring-2 focus-visible:ring-lt-ring/50\",\n \"disabled:pointer-events-none disabled:opacity-50\",\n ),\n {\n variants: {\n variant: {\n ghost: cn(\n \"text-lt-muted-fg hover:bg-lt-accent hover:text-lt-accent-fg\",\n \"data-[state=open]:bg-lt-accent data-[state=open]:text-lt-accent-fg\",\n \"aria-pressed:bg-lt-accent aria-pressed:text-lt-accent-fg\",\n ),\n segmented: cn(\n \"-ml-px rounded-l-none border border-lt-input text-lt-muted-fg hover:bg-lt-accent hover:text-lt-accent-fg\",\n \"data-[state=open]:z-10 data-[state=open]:border-lt-primary data-[state=open]:text-lt-fg\",\n ),\n },\n size: {\n xs: \"size-5 [&_svg]:size-lt-icon-sm\",\n sm: \"size-7 [&_svg]:size-lt-icon-md\",\n md: \"size-lt-control-md [&_svg]:size-lt-icon-md\",\n },\n },\n defaultVariants: { variant: \"ghost\", size: \"sm\" },\n },\n);\n\nexport function IconButton({\n icon,\n label,\n active,\n size,\n variant,\n className,\n children,\n ref,\n ...props\n}: Omit<React.ComponentProps<\"button\">, \"aria-pressed\"> &\n VariantProps<typeof iconButtonVariants> & {\n /** Icon glyph name; omit and pass `children` for custom content. */\n icon?: string;\n /** Accessible name (also used when the button shows only an icon). */\n label: string;\n /** Toggle state — sets `aria-pressed` and the pressed styling. */\n active?: boolean;\n }) {\n return (\n <button\n ref={ref}\n type=\"button\"\n aria-label={label}\n aria-pressed={active}\n className={cn(iconButtonVariants({ variant, size }), className)}\n {...props}\n >\n {icon ? <Icon name={icon} aria-hidden=\"true\" /> : null}\n {children}\n </button>\n );\n}\n\nexport { iconButtonVariants };\n"],"mappings":";;;;;;;;;;;;;AAYA,IAAM,qBAAqB,IACzB,GACE,6FACA,mEACA,kDACF,GACA;CACE,UAAU;EACR,SAAS;GACP,OAAO,GACL,+DACA,sEACA,0DACF;GACA,WAAW,GACT,4GACA,yFACF;EACF;EACA,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;EACN;CACF;CACA,iBAAiB;EAAE,SAAS;EAAS,MAAM;CAAK;AAClD,CACF;AAEA,SAAgB,WAAW,EACzB,MACA,OACA,QACA,MACA,SACA,WACA,UACA,KACA,GAAG,SASA;CACH,OACE,qBAAC,UAAD;EACO;EACL,MAAK;EACL,cAAY;EACZ,gBAAc;EACd,WAAW,GAAG,mBAAmB;GAAE;GAAS;EAAK,CAAC,GAAG,SAAS;EAC9D,GAAI;YANN,CAQG,OAAO,oBAAC,MAAD;GAAM,MAAM;GAAM,eAAY;EAAQ,CAAA,IAAI,MACjD,QACK;;AAEZ"}
|
package/dist/ui/icon.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { cn } from "../lib/utils.js";
|
|
2
2
|
import { IconRenderer } from "../icons/icon-renderer.js";
|
|
3
|
+
import { coerceColor, colorValue } from "../lib/color.js";
|
|
3
4
|
import { jsx } from "react/jsx-runtime";
|
|
4
5
|
//#region resources/js/ui/icon.tsx
|
|
5
6
|
var sizeClass = {
|
|
@@ -12,20 +13,18 @@ var sizeClass = {
|
|
|
12
13
|
"3xl": "size-lt-icon-3xl",
|
|
13
14
|
"4xl": "size-lt-icon-4xl"
|
|
14
15
|
};
|
|
15
|
-
var colorClass = {
|
|
16
|
-
default: "text-lt-fg",
|
|
17
|
-
muted: "text-lt-muted-fg",
|
|
18
|
-
primary: "text-lt-primary",
|
|
19
|
-
success: "text-lt-success",
|
|
20
|
-
info: "text-lt-info",
|
|
21
|
-
warning: "text-lt-warning",
|
|
22
|
-
danger: "text-lt-danger"
|
|
23
|
-
};
|
|
24
16
|
var IconComponent = ({ node }) => {
|
|
25
17
|
const { name, size, color, class: className } = node.props;
|
|
26
|
-
|
|
18
|
+
const icon = /* @__PURE__ */ jsx(IconRenderer, {
|
|
27
19
|
icon: name,
|
|
28
|
-
className: cn(sizeClass[size],
|
|
20
|
+
className: cn(sizeClass[size], className)
|
|
21
|
+
});
|
|
22
|
+
const coerced = coerceColor(color);
|
|
23
|
+
if (!coerced) return icon;
|
|
24
|
+
return /* @__PURE__ */ jsx("span", {
|
|
25
|
+
className: "contents",
|
|
26
|
+
style: { color: colorValue(coerced) },
|
|
27
|
+
children: icon
|
|
29
28
|
});
|
|
30
29
|
};
|
|
31
30
|
//#endregion
|
package/dist/ui/icon.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"icon.js","names":[],"sources":["../../resources/js/ui/icon.tsx"],"sourcesContent":["import type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { IconRenderer } from \"@lattice-php/lattice/icons\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport type {
|
|
1
|
+
{"version":3,"file":"icon.js","names":[],"sources":["../../resources/js/ui/icon.tsx"],"sourcesContent":["import type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { IconRenderer } from \"@lattice-php/lattice/icons\";\nimport { coerceColor, colorValue } from \"@lattice-php/lattice/lib/color\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport type { Size } from \"@lattice-php/lattice/types/generated\";\n\nconst sizeClass: Record<Size, string> = {\n xs: \"size-lt-icon-xs\",\n sm: \"size-lt-icon-sm\",\n md: \"size-lt-icon-md\",\n lg: \"size-lt-icon-lg\",\n xl: \"size-lt-icon-xl\",\n \"2xl\": \"size-lt-icon-2xl\",\n \"3xl\": \"size-lt-icon-3xl\",\n \"4xl\": \"size-lt-icon-4xl\",\n};\n\nconst IconComponent: RendererComponent<\"icon\"> = ({ node }) => {\n const { name, size, color, class: className } = node.props;\n const icon = <IconRenderer icon={name} className={cn(sizeClass[size], className)} />;\n const coerced = coerceColor(color);\n\n if (!coerced) {\n return icon;\n }\n\n return (\n <span className=\"contents\" style={{ color: colorValue(coerced) }}>\n {icon}\n </span>\n );\n};\n\nexport default IconComponent;\n"],"mappings":";;;;;AAMA,IAAM,YAAkC;CACtC,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,OAAO;CACP,OAAO;CACP,OAAO;AACT;AAEA,IAAM,iBAA4C,EAAE,WAAW;CAC7D,MAAM,EAAE,MAAM,MAAM,OAAO,OAAO,cAAc,KAAK;CACrD,MAAM,OAAO,oBAAC,cAAD;EAAc,MAAM;EAAM,WAAW,GAAG,UAAU,OAAO,SAAS;CAAI,CAAA;CACnF,MAAM,UAAU,YAAY,KAAK;CAEjC,IAAI,CAAC,SACH,OAAO;CAGT,OACE,oBAAC,QAAD;EAAM,WAAU;EAAW,OAAO,EAAE,OAAO,WAAW,OAAO,EAAE;YAC5D;CACG,CAAA;AAEV"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
interface PreviewableImageProps {
|
|
3
|
+
src: string;
|
|
4
|
+
alt: string;
|
|
5
|
+
previewable: boolean;
|
|
6
|
+
width?: number;
|
|
7
|
+
height?: number;
|
|
8
|
+
className?: string;
|
|
9
|
+
testId?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function PreviewableImage({ src, alt, previewable, width, height, className, testId, }: PreviewableImageProps): ReactNode;
|
|
12
|
+
export {};
|