@lattice-php/lattice 0.36.0 → 0.37.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 (90) hide show
  1. package/dist/chat/components/prompt-input.js +1 -0
  2. package/dist/chat/components/prompt-input.js.map +1 -1
  3. package/dist/form/components/base/field.d.ts +14 -4
  4. package/dist/form/components/base/field.js +33 -10
  5. package/dist/form/components/base/field.js.map +1 -1
  6. package/dist/form/components/fields/builder.js +4 -2
  7. package/dist/form/components/fields/builder.js.map +1 -1
  8. package/dist/form/components/fields/choice.js +6 -5
  9. package/dist/form/components/fields/choice.js.map +1 -1
  10. package/dist/form/components/fields/color-picker-field.js +2 -2
  11. package/dist/form/components/fields/color-picker-field.js.map +1 -1
  12. package/dist/form/components/fields/date-input.js +2 -1
  13. package/dist/form/components/fields/date-input.js.map +1 -1
  14. package/dist/form/components/fields/date-picker-field.d.ts +3 -1
  15. package/dist/form/components/fields/date-picker-field.js +2 -2
  16. package/dist/form/components/fields/date-picker-field.js.map +1 -1
  17. package/dist/form/components/fields/date-time-input.js +2 -1
  18. package/dist/form/components/fields/date-time-input.js.map +1 -1
  19. package/dist/form/components/fields/file-upload.js +4 -4
  20. package/dist/form/components/fields/file-upload.js.map +1 -1
  21. package/dist/form/components/fields/number-input.js +3 -3
  22. package/dist/form/components/fields/number-input.js.map +1 -1
  23. package/dist/form/components/fields/otp-input.js +2 -2
  24. package/dist/form/components/fields/otp-input.js.map +1 -1
  25. package/dist/form/components/fields/password-input.js +6 -6
  26. package/dist/form/components/fields/password-input.js.map +1 -1
  27. package/dist/form/components/fields/repeater.js +4 -2
  28. package/dist/form/components/fields/repeater.js.map +1 -1
  29. package/dist/form/components/fields/rich-editor-field.js +7 -5
  30. package/dist/form/components/fields/rich-editor-field.js.map +1 -1
  31. package/dist/form/components/fields/select.js +5 -5
  32. package/dist/form/components/fields/select.js.map +1 -1
  33. package/dist/form/components/fields/simple-field.d.ts +2 -1
  34. package/dist/form/components/fields/simple-field.js +2 -2
  35. package/dist/form/components/fields/simple-field.js.map +1 -1
  36. package/dist/form/components/fields/text-input.js +2 -2
  37. package/dist/form/components/fields/text-input.js.map +1 -1
  38. package/dist/form/components/fields/textarea.js +2 -2
  39. package/dist/form/components/fields/textarea.js.map +1 -1
  40. package/dist/form/components/fields/time-input.js +2 -2
  41. package/dist/form/components/fields/time-input.js.map +1 -1
  42. package/dist/form/components/fields/toggle.js +6 -6
  43. package/dist/form/components/fields/toggle.js.map +1 -1
  44. package/dist/form/index.d.ts +1 -0
  45. package/dist/form/index.js +2 -1
  46. package/dist/form/toolkit.d.ts +1 -1
  47. package/dist/lattice.css +16 -0
  48. package/dist/table/components/column-visibility-menu.js +1 -1
  49. package/dist/table/components/filter-controls.js +1 -1
  50. package/dist/table/components/filter-value-input.js +1 -1
  51. package/dist/table/components/table-search.js +1 -1
  52. package/dist/types/generated.d.ts +10 -2
  53. package/dist/ui/code-block-view.d.ts +3 -0
  54. package/dist/ui/code-block-view.js +115 -0
  55. package/dist/ui/code-block-view.js.map +1 -0
  56. package/dist/ui/code-block.d.ts +22 -0
  57. package/dist/ui/code-block.js +43 -0
  58. package/dist/ui/code-block.js.map +1 -0
  59. package/dist/ui/copyable-text.d.ts +3 -1
  60. package/dist/ui/copyable-text.js +19 -13
  61. package/dist/ui/copyable-text.js.map +1 -1
  62. package/dist/ui/index.d.ts +3 -0
  63. package/dist/ui/index.js +3 -1
  64. package/dist/ui/plugin.js +2 -0
  65. package/dist/ui/plugin.js.map +1 -1
  66. package/dist/ui/segmented-pills.d.ts +2 -1
  67. package/dist/ui/segmented-pills.js +3 -2
  68. package/dist/ui/segmented-pills.js.map +1 -1
  69. package/dist/vite.js.map +1 -1
  70. package/dist-standalone/chunks/{chart-view-CR1X2aGE.js → chart-view-CFV7BU_D.js} +17 -17
  71. package/dist-standalone/chunks/code-block-view-D464jtHN.js +34 -0
  72. package/dist-standalone/chunks/date-picker-field-DepKtR_m.js +1 -0
  73. package/dist-standalone/chunks/dist-BtOmkEqQ.js +1 -0
  74. package/dist-standalone/chunks/jsx-runtime-0vZSBttN.js +1 -0
  75. package/dist-standalone/chunks/{jsx-runtime-CN5ucv1d.js → react-dom-DJCfT20h.js} +1 -1
  76. package/dist-standalone/chunks/rich-editor-field-BZQIPOLc.js +158 -0
  77. package/dist-standalone/chunks/{subscriptions-BV4g0s0U.js → subscriptions-4IpkpPX3.js} +1 -1
  78. package/dist-standalone/chunks/{time-picker-kbhOHEPd.js → time-picker-DUjYyeeP.js} +1 -1
  79. package/dist-standalone/chunks/use-dependent-field-DvH7mUyx.js +41 -0
  80. package/dist-standalone/chunks/use-effect-dispatcher-BMWOfaFE.js +97 -0
  81. package/dist-standalone/chunks/w3c-keyname-BOAvb0qz.js +1 -0
  82. package/dist-standalone/lattice.css +1 -1
  83. package/dist-standalone/lattice.js +4 -4
  84. package/dist-standalone/manifest.json +1 -1
  85. package/package.json +9 -1
  86. package/dist-standalone/chunks/date-picker-field-B4Xzow-A.js +0 -1
  87. package/dist-standalone/chunks/dist-ci5ng714.js +0 -1
  88. package/dist-standalone/chunks/icon-button-D3AltHNz.js +0 -41
  89. package/dist-standalone/chunks/rich-editor-field-JM3jBEV1.js +0 -158
  90. package/dist-standalone/chunks/use-effect-dispatcher-CJXFkAv1.js +0 -97
@@ -1 +1 @@
1
- {"version":3,"file":"toggle.js","names":[],"sources":["../../../../resources/js/form/components/fields/toggle.tsx"],"sourcesContent":["import type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { testIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { FormFieldFrame } from \"@lattice-php/lattice/form/components/base/field\";\nimport { toBoolean } from \"@lattice-php/lattice/form/lib/conditions\";\nimport { useFormContext } from \"@lattice-php/lattice/form/hooks/context\";\nimport { fieldDomName } from \"@lattice-php/lattice/form/lib/field-dom-name\";\nimport { useFieldScope } from \"@lattice-php/lattice/form/hooks/field-scope\";\nimport { useDependentField } from \"@lattice-php/lattice/form/hooks/use-dependent-field\";\nimport { useFieldCommit } from \"@lattice-php/lattice/form/hooks/use-field-commit\";\nimport { useSeedDefault } from \"@lattice-php/lattice/form/hooks/use-seed-default\";\nimport { useFormValue } from \"@lattice-php/lattice/form/hooks/values\";\n\nexport const ToggleComponent: RendererComponent<\"field.toggle\"> = ({ node }) => {\n const { hidden, required, readOnly, disabled } = useDependentField(node);\n const props = node.props;\n const localName = props.name;\n const scope = useFieldScope();\n const { errors, fieldIdPrefix } = useFormContext();\n const name = fieldDomName(scope ? scope.scopedName(localName) : localName, fieldIdPrefix);\n const errorKey = scope ? scope.errorKey(localName) : localName;\n const globalValue = useFormValue(localName);\n const storedValue = scope ? scope.getValue(localName) : globalValue;\n const defaultChecked = toBoolean(props.value);\n const checked = storedValue !== undefined ? toBoolean(storedValue) : defaultChecked;\n const locked = readOnly || disabled;\n const { commit } = useFieldCommit();\n\n useSeedDefault(localName, defaultChecked);\n\n if (hidden) {\n return null;\n }\n\n return (\n <FormFieldFrame\n error={errors[errorKey]}\n helperText={props.helperText ?? undefined}\n tooltip={props.tooltip ?? undefined}\n label={props.label ?? \"\"}\n name={name}\n required={required}\n >\n <input disabled={locked} name={name} type=\"hidden\" value={checked ? \"1\" : \"0\"} />\n <button\n aria-checked={checked}\n aria-label={props.label ?? localName}\n autoFocus={props.autoFocus ?? false}\n className={cn(\n \"inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border border-transparent bg-lt-muted p-0.5 shadow-lt-xs transition-colors outline-none focus-visible:border-lt-ring focus-visible:ring-[length:var(--lt-ring-width)] focus-visible:ring-lt-ring/50 disabled:cursor-not-allowed disabled:bg-lt-disabled data-[state=checked]:bg-lt-primary disabled:data-[state=checked]:bg-lt-disabled\",\n )}\n data-state={checked ? \"checked\" : \"unchecked\"}\n data-test={testIdentity(localName)}\n disabled={locked}\n id={name}\n name={name}\n onClick={() => commit(localName, !checked)}\n role=\"switch\"\n tabIndex={props.tabIndex ?? undefined}\n type=\"button\"\n >\n <span\n className=\"size-5 rounded-full bg-lt-bg shadow-lt-sm transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0\"\n data-state={checked ? \"checked\" : \"unchecked\"}\n />\n </button>\n </FormFieldFrame>\n );\n};\n"],"mappings":";;;;;;;;;;;;;AAaA,IAAa,mBAAsD,EAAE,WAAW;CAC9E,MAAM,EAAE,QAAQ,UAAU,UAAU,aAAa,kBAAkB,IAAI;CACvE,MAAM,QAAQ,KAAK;CACnB,MAAM,YAAY,MAAM;CACxB,MAAM,QAAQ,cAAc;CAC5B,MAAM,EAAE,QAAQ,kBAAkB,eAAe;CACjD,MAAM,OAAO,aAAa,QAAQ,MAAM,WAAW,SAAS,IAAI,WAAW,aAAa;CACxF,MAAM,WAAW,QAAQ,MAAM,SAAS,SAAS,IAAI;CACrD,MAAM,cAAc,aAAa,SAAS;CAC1C,MAAM,cAAc,QAAQ,MAAM,SAAS,SAAS,IAAI;CACxD,MAAM,iBAAiB,UAAU,MAAM,KAAK;CAC5C,MAAM,UAAU,gBAAgB,KAAA,IAAY,UAAU,WAAW,IAAI;CACrE,MAAM,SAAS,YAAY;CAC3B,MAAM,EAAE,WAAW,eAAe;CAElC,eAAe,WAAW,cAAc;CAExC,IAAI,QACF,OAAO;CAGT,OACE,qBAAC,gBAAD;EACE,OAAO,OAAO;EACd,YAAY,MAAM,cAAc,KAAA;EAChC,SAAS,MAAM,WAAW,KAAA;EAC1B,OAAO,MAAM,SAAS;EAChB;EACI;EANZ,UAAA,CAQE,oBAAC,SAAD;GAAO,UAAU;GAAc;GAAM,MAAK;GAAS,OAAO,UAAU,MAAM;EAAM,CAAA,GAChF,oBAAC,UAAD;GACE,gBAAc;GACd,cAAY,MAAM,SAAS;GAC3B,WAAW,MAAM,aAAa;GAC9B,WAAW,GACT,gZACF;GACA,cAAY,UAAU,YAAY;GAClC,aAAW,aAAa,SAAS;GACjC,UAAU;GACV,IAAI;GACE;GACN,eAAe,OAAO,WAAW,CAAC,OAAO;GACzC,MAAK;GACL,UAAU,MAAM,YAAY,KAAA;GAC5B,MAAK;GAEL,UAAA,oBAAC,QAAD;IACE,WAAU;IACV,cAAY,UAAU,YAAY;GACnC,CAAA;EACK,CAAA,CACM;;AAEpB"}
1
+ {"version":3,"file":"toggle.js","names":[],"sources":["../../../../resources/js/form/components/fields/toggle.tsx"],"sourcesContent":["import type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { testIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { FormFieldFrame } from \"@lattice-php/lattice/form/components/base/field\";\nimport { toBoolean } from \"@lattice-php/lattice/form/lib/conditions\";\nimport { useFormContext } from \"@lattice-php/lattice/form/hooks/context\";\nimport { fieldDomName } from \"@lattice-php/lattice/form/lib/field-dom-name\";\nimport { useFieldScope } from \"@lattice-php/lattice/form/hooks/field-scope\";\nimport { useDependentField } from \"@lattice-php/lattice/form/hooks/use-dependent-field\";\nimport { useFieldCommit } from \"@lattice-php/lattice/form/hooks/use-field-commit\";\nimport { useSeedDefault } from \"@lattice-php/lattice/form/hooks/use-seed-default\";\nimport { useFormValue } from \"@lattice-php/lattice/form/hooks/values\";\n\nexport const ToggleComponent: RendererComponent<\"field.toggle\"> = ({ node }) => {\n const { hidden, required, readOnly, disabled } = useDependentField(node);\n const props = node.props;\n const localName = props.name;\n const scope = useFieldScope();\n const { errors, fieldIdPrefix } = useFormContext();\n const name = fieldDomName(scope ? scope.scopedName(localName) : localName, fieldIdPrefix);\n const errorKey = scope ? scope.errorKey(localName) : localName;\n const globalValue = useFormValue(localName);\n const storedValue = scope ? scope.getValue(localName) : globalValue;\n const defaultChecked = toBoolean(props.value);\n const checked = storedValue !== undefined ? toBoolean(storedValue) : defaultChecked;\n const locked = readOnly || disabled;\n const { commit } = useFieldCommit();\n\n useSeedDefault(localName, defaultChecked);\n\n if (hidden) {\n return null;\n }\n\n return (\n <FormFieldFrame\n error={errors[errorKey]}\n helperText={props.helperText ?? undefined}\n tooltip={props.tooltip ?? undefined}\n label={props.label ?? \"\"}\n id={name}\n required={required}\n >\n {(controlProps) => (\n <>\n <input disabled={locked} name={name} type=\"hidden\" value={checked ? \"1\" : \"0\"} />\n <button\n {...controlProps}\n aria-checked={checked}\n aria-label={props.label ?? localName}\n autoFocus={props.autoFocus ?? false}\n className={cn(\n \"inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border border-transparent bg-lt-muted p-0.5 shadow-lt-xs transition-colors outline-none focus-visible:border-lt-ring focus-visible:ring-[length:var(--lt-ring-width)] focus-visible:ring-lt-ring/50 disabled:cursor-not-allowed disabled:bg-lt-disabled data-[state=checked]:bg-lt-primary disabled:data-[state=checked]:bg-lt-disabled\",\n )}\n data-state={checked ? \"checked\" : \"unchecked\"}\n data-test={testIdentity(localName)}\n disabled={locked}\n name={name}\n onClick={() => commit(localName, !checked)}\n role=\"switch\"\n tabIndex={props.tabIndex ?? undefined}\n type=\"button\"\n >\n <span\n className=\"size-5 rounded-full bg-lt-bg shadow-lt-sm transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0\"\n data-state={checked ? \"checked\" : \"unchecked\"}\n />\n </button>\n </>\n )}\n </FormFieldFrame>\n );\n};\n"],"mappings":";;;;;;;;;;;;;AAaA,IAAa,mBAAsD,EAAE,WAAW;CAC9E,MAAM,EAAE,QAAQ,UAAU,UAAU,aAAa,kBAAkB,IAAI;CACvE,MAAM,QAAQ,KAAK;CACnB,MAAM,YAAY,MAAM;CACxB,MAAM,QAAQ,cAAc;CAC5B,MAAM,EAAE,QAAQ,kBAAkB,eAAe;CACjD,MAAM,OAAO,aAAa,QAAQ,MAAM,WAAW,SAAS,IAAI,WAAW,aAAa;CACxF,MAAM,WAAW,QAAQ,MAAM,SAAS,SAAS,IAAI;CACrD,MAAM,cAAc,aAAa,SAAS;CAC1C,MAAM,cAAc,QAAQ,MAAM,SAAS,SAAS,IAAI;CACxD,MAAM,iBAAiB,UAAU,MAAM,KAAK;CAC5C,MAAM,UAAU,gBAAgB,KAAA,IAAY,UAAU,WAAW,IAAI;CACrE,MAAM,SAAS,YAAY;CAC3B,MAAM,EAAE,WAAW,eAAe;CAElC,eAAe,WAAW,cAAc;CAExC,IAAI,QACF,OAAO;CAGT,OACE,oBAAC,gBAAD;EACE,OAAO,OAAO;EACd,YAAY,MAAM,cAAc,KAAA;EAChC,SAAS,MAAM,WAAW,KAAA;EAC1B,OAAO,MAAM,SAAS;EACtB,IAAI;EACM;EAER,WAAA,iBACA,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,SAAD;GAAO,UAAU;GAAc;GAAM,MAAK;GAAS,OAAO,UAAU,MAAM;EAAM,CAAA,GAChF,oBAAC,UAAD;GACE,GAAI;GACJ,gBAAc;GACd,cAAY,MAAM,SAAS;GAC3B,WAAW,MAAM,aAAa;GAC9B,WAAW,GACT,gZACF;GACA,cAAY,UAAU,YAAY;GAClC,aAAW,aAAa,SAAS;GACjC,UAAU;GACJ;GACN,eAAe,OAAO,WAAW,CAAC,OAAO;GACzC,MAAK;GACL,UAAU,MAAM,YAAY,KAAA;GAC5B,MAAK;GAEL,UAAA,oBAAC,QAAD;IACE,WAAU;IACV,cAAY,UAAU,YAAY;GACnC,CAAA;EACK,CAAA,CACR,EAAA,CAAA;CAEU,CAAA;AAEpB"}
@@ -1,4 +1,5 @@
1
1
  export * from './components/index.js';
2
+ export { FormFieldFrame, type FormFieldControlProps } from './components/base/field.js';
2
3
  export { FormValuesProvider } from './hooks/values.js';
3
4
  export { formComponents } from './plugin.js';
4
5
  export type * from './types.js';
@@ -1,4 +1,5 @@
1
1
  import { FormValuesProvider } from "./hooks/values.js";
2
+ import { FormFieldFrame } from "./components/base/field.js";
2
3
  import { BuilderComponent } from "./components/fields/builder.js";
3
4
  import { CheckboxComponent } from "./components/fields/checkbox.js";
4
5
  import { ChoiceComponent } from "./components/fields/choice.js";
@@ -19,4 +20,4 @@ import { TimeInputComponent } from "./components/fields/time-input.js";
19
20
  import { ToggleComponent } from "./components/fields/toggle.js";
20
21
  import { WizardComponent, WizardStepComponent } from "./components/wizard.js";
21
22
  import { formComponents } from "./plugin.js";
22
- export { BuilderComponent, CheckboxComponent, ChoiceComponent, ColorPickerFieldComponent, DateInputComponent, DateTimeInputComponent, FileUploadComponent, FormComponent, FormValuesProvider, HiddenInputComponent, NumberInputComponent, OtpInputComponent, PasswordInputComponent, RepeaterComponent, SelectComponent, TextInputComponent, TextareaComponent, TimeInputComponent, ToggleComponent, WizardComponent, WizardStepComponent, formComponents };
23
+ export { BuilderComponent, CheckboxComponent, ChoiceComponent, ColorPickerFieldComponent, DateInputComponent, DateTimeInputComponent, FileUploadComponent, FormComponent, FormFieldFrame, FormValuesProvider, HiddenInputComponent, NumberInputComponent, OtpInputComponent, PasswordInputComponent, RepeaterComponent, SelectComponent, TextInputComponent, TextareaComponent, TimeInputComponent, ToggleComponent, WizardComponent, WizardStepComponent, formComponents };
@@ -2,7 +2,7 @@
2
2
  * Entry point for building custom form fields outside the package. Form
3
3
  * modules not re-exported here are internal and may change without notice.
4
4
  */
5
- export { FormFieldFrame } from './components/base/field.js';
5
+ export { FormFieldFrame, type FormFieldControlProps } from './components/base/field.js';
6
6
  export { useFormContext } from './hooks/context.js';
7
7
  export { FieldScopeProvider, useFieldScope } from './hooks/field-scope.js';
8
8
  export { fieldProps, walkFields } from './lib/field-props.js';
package/dist/lattice.css CHANGED
@@ -97,6 +97,9 @@
97
97
  --lt-control-h-md: 2.25rem;
98
98
  --lt-control-h-lg: 2.5rem;
99
99
 
100
+ /* >= 1rem, or iOS Safari auto-zooms the page on focus */
101
+ --lt-input-font-size-mobile: 1rem;
102
+
100
103
  /* Table cell padding — table density lever */
101
104
  --lt-table-cell-x: calc(var(--spacing) * 4);
102
105
  --lt-table-cell-y: calc(var(--spacing) * 4);
@@ -588,6 +591,19 @@
588
591
  }
589
592
  }
590
593
 
594
+ /* iOS Safari auto-zooms when a focused control computes below 16px; unlayered
595
+ so it beats the text-size utilities on the components. */
596
+ @media (width < theme(--breakpoint-md)) {
597
+ [data-slot="input"],
598
+ [data-slot="textarea"],
599
+ [data-slot="native-select"],
600
+ [data-slot="combobox-search"],
601
+ [data-slot="prompt-input"],
602
+ .lattice-prose[contenteditable="true"] {
603
+ font-size: var(--lt-input-font-size-mobile);
604
+ }
605
+ }
606
+
591
607
  .lt-badge {
592
608
  display: inline-flex;
593
609
  align-items: center;
@@ -1,7 +1,7 @@
1
1
  import { useT } from "../../i18n/instance.js";
2
2
  import { Popover, PopoverContent, PopoverTrigger } from "../../ui/popover.js";
3
- import { Checkbox } from "../../ui/checkbox.js";
4
3
  import { IconButton } from "../../ui/icon-button.js";
4
+ import { Checkbox } from "../../ui/checkbox.js";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  //#region resources/js/table/components/column-visibility-menu.tsx
7
7
  function ColumnVisibilityMenu({ columns, hasHidden, isVisible, onReset, onToggle, processing, visibleColumnCount }) {
@@ -8,8 +8,8 @@ import { ResolvedNodesProvider } from "../../form/hooks/resolved-nodes.js";
8
8
  import { FormValuesProvider, useFormValues, useSetFormValue } from "../../form/hooks/values.js";
9
9
  import { FieldCommitOverrideProvider } from "../../form/hooks/use-field-commit.js";
10
10
  import { TableCellProvider } from "../../form/hooks/row-layout-context.js";
11
- import { Checkbox } from "../../ui/checkbox.js";
12
11
  import { IconButton } from "../../ui/icon-button.js";
12
+ import { Checkbox } from "../../ui/checkbox.js";
13
13
  import { filterValue, isActiveFilterValue } from "../lib/filter-values.js";
14
14
  import { isTruthy } from "../../lib/is-truthy.js";
15
15
  import { useCallback, useMemo, useRef } from "react";
@@ -1,8 +1,8 @@
1
1
  import { useT } from "../../i18n/instance.js";
2
2
  import { cn } from "../../lib/utils.js";
3
3
  import { Icon } from "../../icons/sprite.js";
4
- import { Input } from "../../ui/input.js";
5
4
  import { IconButton } from "../../ui/icon-button.js";
5
+ import { Input } from "../../ui/input.js";
6
6
  import { NativeSelect } from "../../ui/native-select.js";
7
7
  import { useEffect, useState } from "react";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -2,8 +2,8 @@ import { useT } from "../../i18n/instance.js";
2
2
  import { cn } from "../../lib/utils.js";
3
3
  import { Icon } from "../../icons/sprite.js";
4
4
  import { useDebouncedCallback } from "../../lib/use-debounced-callback.js";
5
- import { Input } from "../../ui/input.js";
6
5
  import { IconButton } from "../../ui/icon-button.js";
6
+ import { Input } from "../../ui/input.js";
7
7
  import { useEffect, useRef, useState } from "react";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
9
9
  //#region resources/js/table/components/table-search.tsx
@@ -322,6 +322,13 @@ export type Choice = {
322
322
  export type CloseModal = {
323
323
  readonly modal: string | null;
324
324
  };
325
+ export type CodeBlock = {
326
+ code: string;
327
+ copyable: boolean;
328
+ language: CodeBlockLanguage;
329
+ wrap: boolean;
330
+ };
331
+ export type CodeBlockLanguage = "text" | "json" | "javascript" | "shell" | "php";
325
332
  export type Collapsible = {
326
333
  collapsed: boolean;
327
334
  rememberState: boolean;
@@ -409,6 +416,7 @@ export type ComponentPropsMap = {
409
416
  "chat.box": ChatBox;
410
417
  "chat.part.text": TextPart;
411
418
  "chat.part.tool-call": ToolCallPart;
419
+ "code-block": CodeBlock;
412
420
  collapsible: Collapsible;
413
421
  dropdown: Dropdown;
414
422
  "field.builder": Builder;
@@ -841,7 +849,7 @@ export type MoneyColumn = {
841
849
  toggleable: boolean;
842
850
  width: ColumnWidth;
843
851
  };
844
- export type NodeType = "action" | "action.bulk" | "action.group" | "avatar" | "badge" | "breadcrumbs" | "button" | "callouts" | "card" | "chart" | "chat.box" | "chat.part.text" | "chat.part.tool-call" | "collapsible" | "dropdown" | "field.builder" | "field.checkbox" | "field.choice" | "field.color-picker" | "field.date-input" | "field.date-time-input" | "field.file-upload" | "field.hidden-input" | "field.number-input" | "field.otp" | "field.password-input" | "field.repeater" | "field.rich-editor" | "field.select" | "field.text-input" | "field.textarea" | "field.time-input" | "field.toggle" | "floating-panel" | "form" | "fragment" | "grid" | "heading" | "icon" | "image" | "link" | "menu" | "menu-item" | "modal" | "notifications" | "outlet" | "progress" | "raw-block" | "remote.data-list" | "section" | "segmented-control" | "separator" | "sidebar" | "stack" | "tab" | "table" | "tabs" | "text" | "tooltip" | "topbar" | "wizard" | "wizard-step";
852
+ export type NodeType = "action" | "action.bulk" | "action.group" | "avatar" | "badge" | "breadcrumbs" | "button" | "callouts" | "card" | "chart" | "chat.box" | "chat.part.text" | "chat.part.tool-call" | "code-block" | "collapsible" | "dropdown" | "field.builder" | "field.checkbox" | "field.choice" | "field.color-picker" | "field.date-input" | "field.date-time-input" | "field.file-upload" | "field.hidden-input" | "field.number-input" | "field.otp" | "field.password-input" | "field.repeater" | "field.rich-editor" | "field.select" | "field.text-input" | "field.textarea" | "field.time-input" | "field.toggle" | "floating-panel" | "form" | "fragment" | "grid" | "heading" | "icon" | "image" | "link" | "menu" | "menu-item" | "modal" | "notifications" | "outlet" | "progress" | "raw-block" | "remote.data-list" | "section" | "segmented-control" | "separator" | "sidebar" | "stack" | "tab" | "table" | "tabs" | "text" | "tooltip" | "topbar" | "wizard" | "wizard-step";
845
853
  export type NotificationItem = {
846
854
  readonly actions: Node[];
847
855
  readonly body: Translatable | string | null;
@@ -1390,7 +1398,7 @@ export type Translatable = {
1390
1398
  payload: Record<string, string>;
1391
1399
  replacements: Record<string, string | number | boolean>;
1392
1400
  };
1393
- export type UiNodeType = "avatar" | "badge" | "button" | "card" | "chart" | "collapsible" | "floating-panel" | "grid" | "heading" | "icon" | "image" | "link" | "modal" | "progress" | "raw-block" | "section" | "segmented-control" | "separator" | "stack" | "tab" | "tabs" | "text" | "tooltip";
1401
+ export type UiNodeType = "avatar" | "badge" | "button" | "card" | "chart" | "code-block" | "collapsible" | "floating-panel" | "grid" | "heading" | "icon" | "image" | "link" | "modal" | "progress" | "raw-block" | "section" | "segmented-control" | "separator" | "stack" | "tab" | "tabs" | "text" | "tooltip";
1394
1402
  export type UnreadCount = {
1395
1403
  readonly unreadCount: number;
1396
1404
  };
@@ -0,0 +1,3 @@
1
+ import { CodeBlockViewProps } from './code-block.js';
2
+ declare function CodeBlockView({ children, copyable, language, wrap }: CodeBlockViewProps): import("react").JSX.Element;
3
+ export default CodeBlockView;
@@ -0,0 +1,115 @@
1
+ import { useEffect, useRef } from "react";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { javascript } from "@codemirror/lang-javascript";
4
+ import { json } from "@codemirror/lang-json";
5
+ import { php } from "@codemirror/lang-php";
6
+ import { HighlightStyle, StreamLanguage, syntaxHighlighting } from "@codemirror/language";
7
+ import { shell } from "@codemirror/legacy-modes/mode/shell";
8
+ import { Compartment, EditorState } from "@codemirror/state";
9
+ import { EditorView } from "@codemirror/view";
10
+ import { tags } from "@lezer/highlight";
11
+ //#region resources/js/ui/code-block-view.tsx
12
+ var languages = {
13
+ text: [],
14
+ json: json(),
15
+ javascript: javascript(),
16
+ shell: StreamLanguage.define(shell),
17
+ php: php()
18
+ };
19
+ var highlightStyle = HighlightStyle.define([
20
+ {
21
+ tag: tags.comment,
22
+ color: "var(--lt-muted-fg)",
23
+ fontStyle: "italic"
24
+ },
25
+ {
26
+ tag: [
27
+ tags.keyword,
28
+ tags.operatorKeyword,
29
+ tags.controlKeyword
30
+ ],
31
+ color: "var(--lt-primary)"
32
+ },
33
+ {
34
+ tag: [tags.string, tags.special(tags.string)],
35
+ color: "var(--lt-success)"
36
+ },
37
+ {
38
+ tag: [
39
+ tags.number,
40
+ tags.bool,
41
+ tags.null
42
+ ],
43
+ color: "var(--lt-info)"
44
+ },
45
+ {
46
+ tag: [tags.function(tags.variableName), tags.labelName],
47
+ color: "var(--lt-accent-fg)"
48
+ },
49
+ {
50
+ tag: [tags.typeName, tags.className],
51
+ color: "var(--lt-danger)"
52
+ },
53
+ {
54
+ tag: tags.meta,
55
+ color: "var(--lt-muted-fg)"
56
+ }
57
+ ]);
58
+ var codeBlockTheme = EditorView.theme({
59
+ "&": {
60
+ backgroundColor: "transparent",
61
+ color: "var(--lt-fg)",
62
+ fontSize: "0.75rem"
63
+ },
64
+ ".cm-scroller": {
65
+ fontFamily: "var(--lt-font-mono)",
66
+ lineHeight: "1.5",
67
+ overflow: "auto"
68
+ },
69
+ ".cm-content": { padding: "0.75rem" },
70
+ ".cm-line": { padding: "0" }
71
+ });
72
+ var copyableTheme = EditorView.theme({ ".cm-content": { padding: "2.75rem 0.75rem 0.75rem" } });
73
+ function CodeBlockView({ children, copyable, language, wrap }) {
74
+ const container = useRef(null);
75
+ useEffect(() => {
76
+ if (!container.current) return;
77
+ const languageCompartment = new Compartment();
78
+ const customLanguage = typeof language === "function" ? language : null;
79
+ const initialLanguage = typeof language === "function" ? [] : languages[language];
80
+ const view = new EditorView({
81
+ doc: children,
82
+ parent: container.current,
83
+ extensions: [
84
+ EditorState.readOnly.of(true),
85
+ EditorView.editable.of(false),
86
+ EditorView.contentAttributes.of({ role: "code" }),
87
+ languageCompartment.of(initialLanguage),
88
+ syntaxHighlighting(highlightStyle),
89
+ codeBlockTheme,
90
+ copyable ? copyableTheme : [],
91
+ wrap ? EditorView.lineWrapping : []
92
+ ]
93
+ });
94
+ let active = true;
95
+ if (customLanguage) customLanguage().then((extension) => {
96
+ if (active) view.dispatch({ effects: languageCompartment.reconfigure(extension) });
97
+ }).catch((error) => {
98
+ if (active) console.warn("[lattice] CodeBlock language failed to load.", error);
99
+ });
100
+ return () => {
101
+ active = false;
102
+ view.destroy();
103
+ };
104
+ }, [
105
+ children,
106
+ copyable,
107
+ language,
108
+ wrap
109
+ ]);
110
+ return /* @__PURE__ */ jsx("div", { ref: container });
111
+ }
112
+ //#endregion
113
+ export { CodeBlockView as default };
114
+
115
+ //# sourceMappingURL=code-block-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-block-view.js","names":[],"sources":["../../resources/js/ui/code-block-view.tsx"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport { javascript } from \"@codemirror/lang-javascript\";\nimport { json } from \"@codemirror/lang-json\";\nimport { php } from \"@codemirror/lang-php\";\nimport { HighlightStyle, StreamLanguage, syntaxHighlighting } from \"@codemirror/language\";\nimport { shell } from \"@codemirror/legacy-modes/mode/shell\";\nimport { Compartment, EditorState, type Extension } from \"@codemirror/state\";\nimport { EditorView } from \"@codemirror/view\";\nimport { tags } from \"@lezer/highlight\";\nimport type { CodeBlockLanguage, CodeBlockViewProps } from \"./code-block\";\n\nconst languages: Record<CodeBlockLanguage, Extension> = {\n text: [],\n json: json(),\n javascript: javascript(),\n shell: StreamLanguage.define(shell),\n php: php(),\n};\n\nconst highlightStyle = HighlightStyle.define([\n { tag: tags.comment, color: \"var(--lt-muted-fg)\", fontStyle: \"italic\" },\n { tag: [tags.keyword, tags.operatorKeyword, tags.controlKeyword], color: \"var(--lt-primary)\" },\n { tag: [tags.string, tags.special(tags.string)], color: \"var(--lt-success)\" },\n { tag: [tags.number, tags.bool, tags.null], color: \"var(--lt-info)\" },\n { tag: [tags.function(tags.variableName), tags.labelName], color: \"var(--lt-accent-fg)\" },\n { tag: [tags.typeName, tags.className], color: \"var(--lt-danger)\" },\n { tag: tags.meta, color: \"var(--lt-muted-fg)\" },\n]);\n\nconst codeBlockTheme = EditorView.theme({\n \"&\": {\n backgroundColor: \"transparent\",\n color: \"var(--lt-fg)\",\n fontSize: \"0.75rem\",\n },\n \".cm-scroller\": {\n fontFamily: \"var(--lt-font-mono)\",\n lineHeight: \"1.5\",\n overflow: \"auto\",\n },\n \".cm-content\": { padding: \"0.75rem\" },\n \".cm-line\": { padding: \"0\" },\n});\n\nconst copyableTheme = EditorView.theme({\n \".cm-content\": { padding: \"2.75rem 0.75rem 0.75rem\" },\n});\n\nfunction CodeBlockView({ children, copyable, language, wrap }: CodeBlockViewProps) {\n const container = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!container.current) {\n return;\n }\n\n const languageCompartment = new Compartment();\n const customLanguage = typeof language === \"function\" ? language : null;\n const initialLanguage = typeof language === \"function\" ? [] : languages[language];\n const view = new EditorView({\n doc: children,\n parent: container.current,\n extensions: [\n EditorState.readOnly.of(true),\n EditorView.editable.of(false),\n EditorView.contentAttributes.of({ role: \"code\" }),\n languageCompartment.of(initialLanguage),\n syntaxHighlighting(highlightStyle),\n codeBlockTheme,\n copyable ? copyableTheme : [],\n wrap ? EditorView.lineWrapping : [],\n ],\n });\n let active = true;\n\n if (customLanguage) {\n void customLanguage()\n .then((extension) => {\n if (active) {\n view.dispatch({ effects: languageCompartment.reconfigure(extension) });\n }\n })\n .catch((error: unknown) => {\n if (active) {\n console.warn(\"[lattice] CodeBlock language failed to load.\", error);\n }\n });\n }\n\n return () => {\n active = false;\n view.destroy();\n };\n }, [children, copyable, language, wrap]);\n\n return <div ref={container} />;\n}\n\nexport default CodeBlockView;\n"],"mappings":";;;;;;;;;;;AAWA,IAAM,YAAkD;CACtD,MAAM,CAAC;CACP,MAAM,KAAK;CACX,YAAY,WAAW;CACvB,OAAO,eAAe,OAAO,KAAK;CAClC,KAAK,IAAI;AACX;AAEA,IAAM,iBAAiB,eAAe,OAAO;CAC3C;EAAE,KAAK,KAAK;EAAS,OAAO;EAAsB,WAAW;CAAS;CACtE;EAAE,KAAK;GAAC,KAAK;GAAS,KAAK;GAAiB,KAAK;EAAc;EAAG,OAAO;CAAoB;CAC7F;EAAE,KAAK,CAAC,KAAK,QAAQ,KAAK,QAAQ,KAAK,MAAM,CAAC;EAAG,OAAO;CAAoB;CAC5E;EAAE,KAAK;GAAC,KAAK;GAAQ,KAAK;GAAM,KAAK;EAAI;EAAG,OAAO;CAAiB;CACpE;EAAE,KAAK,CAAC,KAAK,SAAS,KAAK,YAAY,GAAG,KAAK,SAAS;EAAG,OAAO;CAAsB;CACxF;EAAE,KAAK,CAAC,KAAK,UAAU,KAAK,SAAS;EAAG,OAAO;CAAmB;CAClE;EAAE,KAAK,KAAK;EAAM,OAAO;CAAqB;AAChD,CAAC;AAED,IAAM,iBAAiB,WAAW,MAAM;CACtC,KAAK;EACH,iBAAiB;EACjB,OAAO;EACP,UAAU;CACZ;CACA,gBAAgB;EACd,YAAY;EACZ,YAAY;EACZ,UAAU;CACZ;CACA,eAAe,EAAE,SAAS,UAAU;CACpC,YAAY,EAAE,SAAS,IAAI;AAC7B,CAAC;AAED,IAAM,gBAAgB,WAAW,MAAM,EACrC,eAAe,EAAE,SAAS,0BAA0B,EACtD,CAAC;AAED,SAAS,cAAc,EAAE,UAAU,UAAU,UAAU,QAA4B;CACjF,MAAM,YAAY,OAAuB,IAAI;CAE7C,gBAAgB;EACd,IAAI,CAAC,UAAU,SACb;EAGF,MAAM,sBAAsB,IAAI,YAAY;EAC5C,MAAM,iBAAiB,OAAO,aAAa,aAAa,WAAW;EACnE,MAAM,kBAAkB,OAAO,aAAa,aAAa,CAAC,IAAI,UAAU;EACxE,MAAM,OAAO,IAAI,WAAW;GAC1B,KAAK;GACL,QAAQ,UAAU;GAClB,YAAY;IACV,YAAY,SAAS,GAAG,IAAI;IAC5B,WAAW,SAAS,GAAG,KAAK;IAC5B,WAAW,kBAAkB,GAAG,EAAE,MAAM,OAAO,CAAC;IAChD,oBAAoB,GAAG,eAAe;IACtC,mBAAmB,cAAc;IACjC;IACA,WAAW,gBAAgB,CAAC;IAC5B,OAAO,WAAW,eAAe,CAAC;GACpC;EACF,CAAC;EACD,IAAI,SAAS;EAEb,IAAI,gBACF,eAAoB,CAAC,CAClB,MAAM,cAAc;GACnB,IAAI,QACF,KAAK,SAAS,EAAE,SAAS,oBAAoB,YAAY,SAAS,EAAE,CAAC;EAEzE,CAAC,CAAC,CACD,OAAO,UAAmB;GACzB,IAAI,QACF,QAAQ,KAAK,gDAAgD,KAAK;EAEtE,CAAC;EAGL,aAAa;GACX,SAAS;GACT,KAAK,QAAQ;EACf;CACF,GAAG;EAAC;EAAU;EAAU;EAAU;CAAI,CAAC;CAEvC,OAAO,oBAAC,OAAD,EAAK,KAAK,UAAY,CAAA;AAC/B"}
@@ -0,0 +1,22 @@
1
+ import { ComponentProps } from 'react';
2
+ import { Extension } from '@codemirror/state';
3
+ import { RendererComponent } from '../core/types.js';
4
+ type CodeBlockLanguage = "text" | "json" | "javascript" | "shell" | "php";
5
+ type CodeBlockLanguageLoader = () => Promise<Extension>;
6
+ interface CodeBlockProps extends Omit<ComponentProps<"div">, "children"> {
7
+ children: string;
8
+ copyable?: boolean;
9
+ language?: CodeBlockLanguage | CodeBlockLanguageLoader;
10
+ wrap?: boolean;
11
+ }
12
+ interface CodeBlockViewProps {
13
+ children: string;
14
+ copyable: boolean;
15
+ language: CodeBlockLanguage | CodeBlockLanguageLoader;
16
+ wrap: boolean;
17
+ }
18
+ declare function CodeBlock({ "aria-label": ariaLabel, children, className, copyable, language, role, wrap, ...props }: CodeBlockProps): import("react").JSX.Element;
19
+ declare const CodeBlockComponent: RendererComponent<"code-block">;
20
+ export default CodeBlockComponent;
21
+ export { CodeBlock };
22
+ export type { CodeBlockLanguage, CodeBlockLanguageLoader, CodeBlockProps, CodeBlockViewProps };
@@ -0,0 +1,43 @@
1
+ import { cn } from "../lib/utils.js";
2
+ import { CopyButton } from "./copyable-text.js";
3
+ import { Suspense, lazy } from "react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ //#region resources/js/ui/code-block.tsx
6
+ var CodeBlockView = lazy(() => import("./code-block-view.js"));
7
+ function CodeBlock({ "aria-label": ariaLabel, children, className, copyable = false, language = "text", role = ariaLabel ? "region" : void 0, wrap = false, ...props }) {
8
+ const fallback = /* @__PURE__ */ jsx("pre", {
9
+ className: cn("max-w-full overflow-x-auto p-3 font-lt-mono text-xs", wrap && "whitespace-pre-wrap wrap-anywhere", copyable && "pt-11"),
10
+ children
11
+ });
12
+ return /* @__PURE__ */ jsxs("div", {
13
+ "data-slot": "code-block",
14
+ "aria-label": ariaLabel,
15
+ role,
16
+ ...props,
17
+ className: cn("relative max-w-full overflow-hidden rounded-lt-sm bg-lt-muted text-lt-fg", className),
18
+ children: [copyable ? /* @__PURE__ */ jsx(CopyButton, {
19
+ value: children,
20
+ label: ariaLabel ?? "code",
21
+ iconOnly: true,
22
+ className: "absolute top-2 right-2 z-10 bg-lt-bg/80 hover:bg-lt-bg"
23
+ }) : null, /* @__PURE__ */ jsx(Suspense, {
24
+ fallback,
25
+ children: /* @__PURE__ */ jsx(CodeBlockView, {
26
+ copyable,
27
+ language,
28
+ wrap,
29
+ children
30
+ })
31
+ })]
32
+ });
33
+ }
34
+ var CodeBlockComponent = ({ node }) => /* @__PURE__ */ jsx(CodeBlock, {
35
+ copyable: node.props.copyable,
36
+ language: node.props.language,
37
+ wrap: node.props.wrap,
38
+ children: node.props.code
39
+ });
40
+ //#endregion
41
+ export { CodeBlock, CodeBlockComponent as default };
42
+
43
+ //# sourceMappingURL=code-block.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-block.js","names":[],"sources":["../../resources/js/ui/code-block.tsx"],"sourcesContent":["import { lazy, Suspense } from \"react\";\nimport type { ComponentProps } from \"react\";\nimport type { Extension } from \"@codemirror/state\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { CopyButton } from \"./copyable-text\";\n\nconst CodeBlockView = lazy(() => import(\"./code-block-view\"));\n\ntype CodeBlockLanguage = \"text\" | \"json\" | \"javascript\" | \"shell\" | \"php\";\ntype CodeBlockLanguageLoader = () => Promise<Extension>;\n\ninterface CodeBlockProps extends Omit<ComponentProps<\"div\">, \"children\"> {\n children: string;\n copyable?: boolean;\n language?: CodeBlockLanguage | CodeBlockLanguageLoader;\n wrap?: boolean;\n}\n\ninterface CodeBlockViewProps {\n children: string;\n copyable: boolean;\n language: CodeBlockLanguage | CodeBlockLanguageLoader;\n wrap: boolean;\n}\n\nfunction CodeBlock({\n \"aria-label\": ariaLabel,\n children,\n className,\n copyable = false,\n language = \"text\",\n role = ariaLabel ? \"region\" : undefined,\n wrap = false,\n ...props\n}: CodeBlockProps) {\n const fallback = (\n <pre\n className={cn(\n \"max-w-full overflow-x-auto p-3 font-lt-mono text-xs\",\n wrap && \"whitespace-pre-wrap wrap-anywhere\",\n copyable && \"pt-11\",\n )}\n >\n {children}\n </pre>\n );\n\n return (\n <div\n data-slot=\"code-block\"\n aria-label={ariaLabel}\n role={role}\n {...props}\n className={cn(\n \"relative max-w-full overflow-hidden rounded-lt-sm bg-lt-muted text-lt-fg\",\n className,\n )}\n >\n {copyable ? (\n <CopyButton\n value={children}\n label={ariaLabel ?? \"code\"}\n iconOnly\n className=\"absolute top-2 right-2 z-10 bg-lt-bg/80 hover:bg-lt-bg\"\n />\n ) : null}\n <Suspense fallback={fallback}>\n <CodeBlockView copyable={copyable} language={language} wrap={wrap}>\n {children}\n </CodeBlockView>\n </Suspense>\n </div>\n );\n}\n\nconst CodeBlockComponent: RendererComponent<\"code-block\"> = ({ node }) => (\n <CodeBlock copyable={node.props.copyable} language={node.props.language} wrap={node.props.wrap}>\n {node.props.code}\n </CodeBlock>\n);\n\nexport default CodeBlockComponent;\nexport { CodeBlock };\nexport type { CodeBlockLanguage, CodeBlockLanguageLoader, CodeBlockProps, CodeBlockViewProps };\n"],"mappings":";;;;;AAOA,IAAM,gBAAgB,WAAW,OAAO,uBAAoB;AAmB5D,SAAS,UAAU,EACjB,cAAc,WACd,UACA,WACA,WAAW,OACX,WAAW,QACX,OAAO,YAAY,WAAW,KAAA,GAC9B,OAAO,OACP,GAAG,SACc;CACjB,MAAM,WACJ,oBAAC,OAAD;EACE,WAAW,GACT,uDACA,QAAQ,qCACR,YAAY,OACd;EAEC;CACE,CAAA;CAGP,OACE,qBAAC,OAAD;EACE,aAAU;EACV,cAAY;EACN;EACN,GAAI;EACJ,WAAW,GACT,4EACA,SACF;EARF,UAAA,CAUG,WACC,oBAAC,YAAD;GACE,OAAO;GACP,OAAO,aAAa;GACpB,UAAA;GACA,WAAU;EACX,CAAA,IACC,MACJ,oBAAC,UAAD;GAAoB;GAClB,UAAA,oBAAC,eAAD;IAAyB;IAAoB;IAAgB;IAC1D;GACY,CAAA;EACP,CAAA,CACP;;AAET;AAEA,IAAM,sBAAuD,EAAE,WAC7D,oBAAC,WAAD;CAAW,UAAU,KAAK,MAAM;CAAU,UAAU,KAAK,MAAM;CAAU,MAAM,KAAK,MAAM;CACvF,UAAA,KAAK,MAAM;AACH,CAAA"}
@@ -5,8 +5,10 @@ interface CopyButtonProps {
5
5
  label: string;
6
6
  testId?: string;
7
7
  className?: string;
8
+ iconOnly?: boolean;
9
+ children?: ReactNode;
8
10
  }
9
- export declare function CopyButton({ value, label, testId, className }: CopyButtonProps): ReactNode;
11
+ export declare function CopyButton({ value, label, testId, className, iconOnly, children, }: CopyButtonProps): ReactNode;
10
12
  interface CopyableTextProps {
11
13
  value: string;
12
14
  label: string;
@@ -1,6 +1,6 @@
1
1
  import { useT } from "../i18n/instance.js";
2
- import { cn } from "../lib/utils.js";
3
- import { Icon } from "../icons/sprite.js";
2
+ import { Button } from "./button.js";
3
+ import { IconButton } from "./icon-button.js";
4
4
  import { useEffect, useState } from "react";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  //#region resources/js/ui/copyable-text.tsx
@@ -13,7 +13,7 @@ async function copyToClipboard(text) {
13
13
  return false;
14
14
  }
15
15
  }
16
- function CopyButton({ value, label, testId, className }) {
16
+ function CopyButton({ value, label, testId, className, iconOnly = false, children }) {
17
17
  const { t } = useT("lattice");
18
18
  const [copied, setCopied] = useState(false);
19
19
  useEffect(() => {
@@ -21,21 +21,27 @@ function CopyButton({ value, label, testId, className }) {
21
21
  const timeout = window.setTimeout(() => setCopied(false), 1500);
22
22
  return () => window.clearTimeout(timeout);
23
23
  }, [copied]);
24
- function handleCopy() {
25
- copyToClipboard(value);
26
- setCopied(true);
24
+ async function handleCopy() {
25
+ if (await copyToClipboard(value)) setCopied(true);
27
26
  }
28
27
  const ariaLabel = copied ? t("common.copied-value", "Copied {{label}}", { label }) : t("common.copy-value", "Copy {{label}}", { label });
29
- return /* @__PURE__ */ jsxs("button", {
28
+ if (iconOnly) return /* @__PURE__ */ jsx(IconButton, {
29
+ icon: copied ? "check" : "copy",
30
+ label: ariaLabel,
31
+ "data-test": testId,
32
+ className,
33
+ onClick: () => void handleCopy()
34
+ });
35
+ return /* @__PURE__ */ jsx(Button, {
30
36
  type: "button",
37
+ size: "sm",
38
+ emphasis: "outline",
39
+ icon: copied ? "check" : "copy",
31
40
  "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),
41
+ className,
33
42
  "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")]
43
+ onClick: () => void handleCopy(),
44
+ children: copied ? t("common.copied", "Copied") : children ?? t("common.copy", "Copy")
39
45
  });
40
46
  }
41
47
  function CopyableText({ value, label, testId, children }) {
@@ -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 { 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;EARX,UAAA,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;EAAf,UAAA,CACG,YAAY,OACb,oBAAC,YAAD;GAAmB;GAAc;GAAe;EAAS,CAAA,CACtD;;AAET"}
1
+ {"version":3,"file":"copyable-text.js","names":[],"sources":["../../resources/js/ui/copyable-text.tsx"],"sourcesContent":["import { useT } from \"@lattice-php/lattice/i18n\";\nimport { type ReactNode, useEffect, useState } from \"react\";\nimport { Button } from \"./button\";\nimport { IconButton } from \"./icon-button\";\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 iconOnly?: boolean;\n children?: ReactNode;\n}\n\nexport function CopyButton({\n value,\n label,\n testId,\n className,\n iconOnly = false,\n children,\n}: 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 async function handleCopy(): Promise<void> {\n if (await copyToClipboard(value)) {\n setCopied(true);\n }\n }\n\n const ariaLabel = copied\n ? t(\"common.copied-value\", \"Copied {{label}}\", { label })\n : t(\"common.copy-value\", \"Copy {{label}}\", { label });\n\n if (iconOnly) {\n return (\n <IconButton\n icon={copied ? \"check\" : \"copy\"}\n label={ariaLabel}\n data-test={testId}\n className={className}\n onClick={() => void handleCopy()}\n />\n );\n }\n\n return (\n <Button\n type=\"button\"\n size=\"sm\"\n emphasis=\"outline\"\n icon={copied ? \"check\" : \"copy\"}\n data-test={testId}\n className={className}\n aria-label={ariaLabel}\n onClick={() => void handleCopy()}\n >\n {copied ? t(\"common.copied\", \"Copied\") : (children ?? 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;AAWA,SAAgB,WAAW,EACzB,OACA,OACA,QACA,WACA,WAAW,OACX,YAC6B;CAC7B,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,eAAe,aAA4B;EACzC,IAAI,MAAM,gBAAgB,KAAK,GAC7B,UAAU,IAAI;CAElB;CAEA,MAAM,YAAY,SACd,EAAE,uBAAuB,oBAAoB,EAAE,MAAM,CAAC,IACtD,EAAE,qBAAqB,kBAAkB,EAAE,MAAM,CAAC;CAEtD,IAAI,UACF,OACE,oBAAC,YAAD;EACE,MAAM,SAAS,UAAU;EACzB,OAAO;EACP,aAAW;EACA;EACX,eAAe,KAAK,WAAW;CAChC,CAAA;CAIL,OACE,oBAAC,QAAD;EACE,MAAK;EACL,MAAK;EACL,UAAS;EACT,MAAM,SAAS,UAAU;EACzB,aAAW;EACA;EACX,cAAY;EACZ,eAAe,KAAK,WAAW;EAE9B,UAAA,SAAS,EAAE,iBAAiB,QAAQ,IAAK,YAAY,EAAE,eAAe,MAAM;CACvE,CAAA;AAEZ;AASA,SAAgB,aAAa,EAAE,OAAO,OAAO,QAAQ,YAA0C;CAC7F,OACE,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACG,YAAY,OACb,oBAAC,YAAD;GAAmB;GAAc;GAAe;EAAS,CAAA,CACtD;;AAET"}
@@ -5,6 +5,8 @@ export { Button, buttonVariants } from './button.js';
5
5
  export type { Emphasis, Variant } from './button.js';
6
6
  export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from './card.js';
7
7
  export { Checkbox } from './checkbox.js';
8
+ export { CodeBlock } from './code-block.js';
9
+ export type { CodeBlockLanguage, CodeBlockLanguageLoader, CodeBlockProps } from './code-block.js';
8
10
  export { Combobox } from './combobox.js';
9
11
  export { ConfirmDialog } from './confirm-dialog.js';
10
12
  export { ActionTrigger, ActionTriggerProvider, useActionTrigger, useClickBehavior, } from './click-behavior.js';
@@ -18,6 +20,7 @@ export { Input } from './input.js';
18
20
  export { default as InputError } from './input-error.js';
19
21
  export { InputOTP } from './input-otp.js';
20
22
  export { Label } from './label.js';
23
+ export { NativeSelect } from './native-select.js';
21
24
  export { TextLink } from './link.js';
22
25
  export { default as PasswordInput } from './password-input.js';
23
26
  export { Popover, PopoverClose, PopoverContent, PopoverTrigger } from './popover.js';
package/dist/ui/index.js CHANGED
@@ -11,6 +11,7 @@ import { Badge } from "./badge.js";
11
11
  import { InfoTooltip } from "./info-tooltip.js";
12
12
  import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "./card.js";
13
13
  import { CopyButton, CopyableText, copyToClipboard } from "./copyable-text.js";
14
+ import { CodeBlock } from "./code-block.js";
14
15
  import { PreviewableImage } from "./image-preview.js";
15
16
  import { TextLink } from "./link.js";
16
17
  import { SegmentedPills } from "./segmented-pills.js";
@@ -24,4 +25,5 @@ import { InputOTP } from "./input-otp.js";
24
25
  import PasswordInput from "./password-input.js";
25
26
  import { Combobox } from "./combobox.js";
26
27
  import { Textarea } from "./textarea.js";
27
- export { ActionMenuProvider, ActionTrigger, ActionTriggerProvider, AffixGroup, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Combobox, ConfirmDialog, CopyButton, CopyableText, Dialog, DialogClose, DialogContent, DialogDescription, DialogHeader, DialogTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, InfoTooltip, Input, InputError, InputOTP, Label, PasswordInput, Popover, PopoverClose, PopoverContent, PopoverTrigger, PreviewableImage, SegmentedPills, Skeleton, Spinner, TextLink, Textarea, actionMenuItemClassName, buttonVariants, copyToClipboard, uiComponents, useActionMenu, useActionTrigger, useClickBehavior };
28
+ import { NativeSelect } from "./native-select.js";
29
+ export { ActionMenuProvider, ActionTrigger, ActionTriggerProvider, AffixGroup, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, CodeBlock, Combobox, ConfirmDialog, CopyButton, CopyableText, Dialog, DialogClose, DialogContent, DialogDescription, DialogHeader, DialogTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, InfoTooltip, Input, InputError, InputOTP, Label, NativeSelect, PasswordInput, Popover, PopoverClose, PopoverContent, PopoverTrigger, PreviewableImage, SegmentedPills, Skeleton, Spinner, TextLink, Textarea, actionMenuItemClassName, buttonVariants, copyToClipboard, uiComponents, useActionMenu, useActionTrigger, useClickBehavior };
package/dist/ui/plugin.js CHANGED
@@ -4,6 +4,7 @@ import AvatarComponent from "./avatar.js";
4
4
  import BadgeComponent from "./badge.js";
5
5
  import CardComponent from "./card.js";
6
6
  import ChartComponent from "./chart.js";
7
+ import CodeBlockComponent from "./code-block.js";
7
8
  import CollapsibleComponent from "./collapsible.js";
8
9
  import FloatingPanelComponent from "./floating-panel.js";
9
10
  import FragmentComponent from "./fragment.js";
@@ -30,6 +31,7 @@ var uiComponents = createPlugin({
30
31
  button: eagerComponent(ButtonComponent),
31
32
  card: eagerComponent(CardComponent),
32
33
  chart: eagerComponent(ChartComponent),
34
+ "code-block": eagerComponent(CodeBlockComponent),
33
35
  collapsible: eagerComponent(CollapsibleComponent),
34
36
  "floating-panel": eagerComponent(FloatingPanelComponent),
35
37
  fragment: eagerComponent(FragmentComponent),
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":[],"sources":["../../resources/js/ui/plugin.ts"],"sourcesContent":["import {\n createPlugin,\n eagerComponent,\n type ComponentRegistryFor,\n} from \"@lattice-php/lattice/core/registry\";\nimport type { FragmentNodeType, UiNodeType } from \"@lattice-php/lattice/types/generated\";\nimport AvatarComponent from \"./avatar\";\nimport BadgeComponent from \"./badge\";\nimport ButtonComponent from \"./button\";\nimport CardComponent from \"./card\";\nimport ChartComponent from \"./chart\";\nimport CollapsibleComponent from \"./collapsible\";\nimport FloatingPanelComponent from \"./floating-panel\";\nimport FragmentComponent from \"./fragment\";\nimport GridComponent from \"./grid\";\nimport HeadingComponent from \"./heading\";\nimport IconComponent from \"./icon\";\nimport ImageComponent from \"./image\";\nimport LinkComponent from \"./link\";\nimport ModalComponent from \"./modal\";\nimport ProgressComponent from \"./progress\";\nimport RawBlockComponent from \"./raw-block\";\nimport SectionComponent from \"./section\";\nimport SegmentedControlComponent from \"./segmented-control\";\nimport SeparatorComponent from \"./separator\";\nimport StackComponent from \"./stack\";\nimport TabComponent, { TabsComponent } from \"./tabs\";\nimport TextComponent from \"./text\";\nimport TooltipComponent from \"./tooltip\";\n\ntype UiComponentType = UiNodeType | FragmentNodeType;\n\nexport const uiComponents = createPlugin({\n components: {\n avatar: eagerComponent(AvatarComponent),\n badge: eagerComponent(BadgeComponent),\n button: eagerComponent(ButtonComponent),\n card: eagerComponent(CardComponent),\n chart: eagerComponent(ChartComponent),\n collapsible: eagerComponent(CollapsibleComponent),\n \"floating-panel\": eagerComponent(FloatingPanelComponent),\n fragment: eagerComponent(FragmentComponent),\n grid: eagerComponent(GridComponent),\n heading: eagerComponent(HeadingComponent),\n icon: eagerComponent(IconComponent),\n image: eagerComponent(ImageComponent),\n link: eagerComponent(LinkComponent),\n modal: eagerComponent(ModalComponent),\n progress: eagerComponent(ProgressComponent),\n \"raw-block\": eagerComponent(RawBlockComponent),\n section: eagerComponent(SectionComponent),\n \"segmented-control\": eagerComponent(SegmentedControlComponent),\n separator: eagerComponent(SeparatorComponent),\n stack: eagerComponent(StackComponent),\n tab: eagerComponent(TabComponent),\n tabs: eagerComponent(TabsComponent),\n text: eagerComponent(TextComponent),\n tooltip: eagerComponent(TooltipComponent),\n } satisfies ComponentRegistryFor<UiComponentType>,\n name: \"lattice/ui\",\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAa,eAAe,aAAa;CACvC,YAAY;EACV,QAAQ,eAAe,eAAe;EACtC,OAAO,eAAe,cAAc;EACpC,QAAQ,eAAe,eAAe;EACtC,MAAM,eAAe,aAAa;EAClC,OAAO,eAAe,cAAc;EACpC,aAAa,eAAe,oBAAoB;EAChD,kBAAkB,eAAe,sBAAsB;EACvD,UAAU,eAAe,iBAAiB;EAC1C,MAAM,eAAe,aAAa;EAClC,SAAS,eAAe,gBAAgB;EACxC,MAAM,eAAe,aAAa;EAClC,OAAO,eAAe,cAAc;EACpC,MAAM,eAAe,aAAa;EAClC,OAAO,eAAe,cAAc;EACpC,UAAU,eAAe,iBAAiB;EAC1C,aAAa,eAAe,iBAAiB;EAC7C,SAAS,eAAe,gBAAgB;EACxC,qBAAqB,eAAe,yBAAyB;EAC7D,WAAW,eAAe,kBAAkB;EAC5C,OAAO,eAAe,cAAc;EACpC,KAAK,eAAe,YAAY;EAChC,MAAM,eAAe,aAAa;EAClC,MAAM,eAAe,aAAa;EAClC,SAAS,eAAe,gBAAgB;CAC1C;CACA,MAAM;AACR,CAAC"}
1
+ {"version":3,"file":"plugin.js","names":[],"sources":["../../resources/js/ui/plugin.ts"],"sourcesContent":["import {\n createPlugin,\n eagerComponent,\n type ComponentRegistryFor,\n} from \"@lattice-php/lattice/core/registry\";\nimport type { FragmentNodeType, UiNodeType } from \"@lattice-php/lattice/types/generated\";\nimport AvatarComponent from \"./avatar\";\nimport BadgeComponent from \"./badge\";\nimport ButtonComponent from \"./button\";\nimport CardComponent from \"./card\";\nimport ChartComponent from \"./chart\";\nimport CodeBlockComponent from \"./code-block\";\nimport CollapsibleComponent from \"./collapsible\";\nimport FloatingPanelComponent from \"./floating-panel\";\nimport FragmentComponent from \"./fragment\";\nimport GridComponent from \"./grid\";\nimport HeadingComponent from \"./heading\";\nimport IconComponent from \"./icon\";\nimport ImageComponent from \"./image\";\nimport LinkComponent from \"./link\";\nimport ModalComponent from \"./modal\";\nimport ProgressComponent from \"./progress\";\nimport RawBlockComponent from \"./raw-block\";\nimport SectionComponent from \"./section\";\nimport SegmentedControlComponent from \"./segmented-control\";\nimport SeparatorComponent from \"./separator\";\nimport StackComponent from \"./stack\";\nimport TabComponent, { TabsComponent } from \"./tabs\";\nimport TextComponent from \"./text\";\nimport TooltipComponent from \"./tooltip\";\n\ntype UiComponentType = UiNodeType | FragmentNodeType;\n\nexport const uiComponents = createPlugin({\n components: {\n avatar: eagerComponent(AvatarComponent),\n badge: eagerComponent(BadgeComponent),\n button: eagerComponent(ButtonComponent),\n card: eagerComponent(CardComponent),\n chart: eagerComponent(ChartComponent),\n \"code-block\": eagerComponent(CodeBlockComponent),\n collapsible: eagerComponent(CollapsibleComponent),\n \"floating-panel\": eagerComponent(FloatingPanelComponent),\n fragment: eagerComponent(FragmentComponent),\n grid: eagerComponent(GridComponent),\n heading: eagerComponent(HeadingComponent),\n icon: eagerComponent(IconComponent),\n image: eagerComponent(ImageComponent),\n link: eagerComponent(LinkComponent),\n modal: eagerComponent(ModalComponent),\n progress: eagerComponent(ProgressComponent),\n \"raw-block\": eagerComponent(RawBlockComponent),\n section: eagerComponent(SectionComponent),\n \"segmented-control\": eagerComponent(SegmentedControlComponent),\n separator: eagerComponent(SeparatorComponent),\n stack: eagerComponent(StackComponent),\n tab: eagerComponent(TabComponent),\n tabs: eagerComponent(TabsComponent),\n text: eagerComponent(TextComponent),\n tooltip: eagerComponent(TooltipComponent),\n } satisfies ComponentRegistryFor<UiComponentType>,\n name: \"lattice/ui\",\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,IAAa,eAAe,aAAa;CACvC,YAAY;EACV,QAAQ,eAAe,eAAe;EACtC,OAAO,eAAe,cAAc;EACpC,QAAQ,eAAe,eAAe;EACtC,MAAM,eAAe,aAAa;EAClC,OAAO,eAAe,cAAc;EACpC,cAAc,eAAe,kBAAkB;EAC/C,aAAa,eAAe,oBAAoB;EAChD,kBAAkB,eAAe,sBAAsB;EACvD,UAAU,eAAe,iBAAiB;EAC1C,MAAM,eAAe,aAAa;EAClC,SAAS,eAAe,gBAAgB;EACxC,MAAM,eAAe,aAAa;EAClC,OAAO,eAAe,cAAc;EACpC,MAAM,eAAe,aAAa;EAClC,OAAO,eAAe,cAAc;EACpC,UAAU,eAAe,iBAAiB;EAC1C,aAAa,eAAe,iBAAiB;EAC7C,SAAS,eAAe,gBAAgB;EACxC,qBAAqB,eAAe,yBAAyB;EAC7D,WAAW,eAAe,kBAAkB;EAC5C,OAAO,eAAe,cAAc;EACpC,KAAK,eAAe,YAAY;EAChC,MAAM,eAAe,aAAa;EAClC,MAAM,eAAe,aAAa;EAClC,SAAS,eAAe,gBAAgB;CAC1C;CACA,MAAM;AACR,CAAC"}
@@ -1,9 +1,10 @@
1
+ import { ComponentProps } from 'react';
1
2
  import { Option } from '../core/types.js';
2
3
  /**
3
4
  * Presentational segmented pill group. Used by the form choice field (bound to a
4
5
  * form value) and the core segmented control (standalone, emits an event).
5
6
  */
6
- export declare function SegmentedPills({ ariaLabel, autoFocus, disabled, name, onSelect, options, tabIndex, value, }: {
7
+ export declare function SegmentedPills({ ariaLabel, autoFocus, className, disabled, name, onSelect, options, tabIndex, value, ...props }: Omit<ComponentProps<"div">, "children" | "onSelect" | "ref"> & {
7
8
  ariaLabel?: string;
8
9
  autoFocus?: boolean;
9
10
  disabled?: boolean;
@@ -7,7 +7,7 @@ import { jsx } from "react/jsx-runtime";
7
7
  * Presentational segmented pill group. Used by the form choice field (bound to a
8
8
  * form value) and the core segmented control (standalone, emits an event).
9
9
  */
10
- function SegmentedPills({ ariaLabel, autoFocus = false, disabled = false, name, onSelect, options, tabIndex, value }) {
10
+ function SegmentedPills({ ariaLabel, autoFocus = false, className, disabled = false, name, onSelect, options, tabIndex, value, ...props }) {
11
11
  const groupRef = useRef(null);
12
12
  useEffect(() => {
13
13
  if (!autoFocus) return;
@@ -15,8 +15,9 @@ function SegmentedPills({ ariaLabel, autoFocus = false, disabled = false, name,
15
15
  (group?.querySelector("button[aria-checked=\"true\"]") ?? group?.querySelector("button"))?.focus();
16
16
  }, [autoFocus]);
17
17
  return /* @__PURE__ */ jsx("div", {
18
+ ...props,
18
19
  "aria-label": ariaLabel,
19
- className: "inline-flex w-fit max-w-full gap-1 overflow-x-auto rounded-lt bg-lt-muted p-1",
20
+ className: cn("inline-flex w-fit max-w-full gap-1 overflow-x-auto rounded-lt bg-lt-muted p-1", className),
20
21
  ref: groupRef,
21
22
  role: "radiogroup",
22
23
  children: options.map((option) => {
@@ -1 +1 @@
1
- {"version":3,"file":"segmented-pills.js","names":[],"sources":["../../resources/js/ui/segmented-pills.tsx"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport type { Option } from \"@lattice-php/lattice/core/types\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { pillClassName } from \"./pill\";\n\n/**\n * Presentational segmented pill group. Used by the form choice field (bound to a\n * form value) and the core segmented control (standalone, emits an event).\n */\nexport function SegmentedPills({\n ariaLabel,\n autoFocus = false,\n disabled = false,\n name,\n onSelect,\n options,\n tabIndex,\n value,\n}: {\n ariaLabel?: string;\n autoFocus?: boolean;\n disabled?: boolean;\n name: string;\n onSelect: (value: string) => void;\n options: Option[];\n tabIndex?: number;\n value: string;\n}) {\n const groupRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!autoFocus) {\n return;\n }\n\n const group = groupRef.current;\n const target =\n group?.querySelector<HTMLButtonElement>('button[aria-checked=\"true\"]') ??\n group?.querySelector<HTMLButtonElement>(\"button\");\n\n target?.focus();\n }, [autoFocus]);\n\n return (\n <div\n aria-label={ariaLabel}\n className=\"inline-flex w-fit max-w-full gap-1 overflow-x-auto rounded-lt bg-lt-muted p-1\"\n ref={groupRef}\n role=\"radiogroup\"\n >\n {options.map((option) => {\n const isSelected = value === option.value;\n\n return (\n <button\n aria-checked={isSelected}\n data-test={`${name}-${option.value}`}\n className={cn(pillClassName(isSelected), disabled && \"cursor-not-allowed opacity-60\")}\n disabled={disabled}\n key={option.value}\n onClick={() => onSelect(option.value)}\n role=\"radio\"\n tabIndex={tabIndex}\n type=\"button\"\n >\n {option.label}\n </button>\n );\n })}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AASA,SAAgB,eAAe,EAC7B,WACA,YAAY,OACZ,WAAW,OACX,MACA,UACA,SACA,UACA,SAUC;CACD,MAAM,WAAW,OAAuB,IAAI;CAE5C,gBAAgB;EACd,IAAI,CAAC,WACH;EAGF,MAAM,QAAQ,SAAS;EAKvB,CAHE,OAAO,cAAiC,+BAA6B,KACrE,OAAO,cAAiC,QAAQ,EAAA,EAE1C,MAAM;CAChB,GAAG,CAAC,SAAS,CAAC;CAEd,OACE,oBAAC,OAAD;EACE,cAAY;EACZ,WAAU;EACV,KAAK;EACL,MAAK;EAEJ,UAAA,QAAQ,KAAK,WAAW;GACvB,MAAM,aAAa,UAAU,OAAO;GAEpC,OACE,oBAAC,UAAD;IACE,gBAAc;IACd,aAAW,GAAG,KAAK,GAAG,OAAO;IAC7B,WAAW,GAAG,cAAc,UAAU,GAAG,YAAY,+BAA+B;IAC1E;IAEV,eAAe,SAAS,OAAO,KAAK;IACpC,MAAK;IACK;IACV,MAAK;IAEJ,UAAA,OAAO;GACF,GAPD,OAAO,KAON;EAEZ,CAAC;CACE,CAAA;AAET"}
1
+ {"version":3,"file":"segmented-pills.js","names":[],"sources":["../../resources/js/ui/segmented-pills.tsx"],"sourcesContent":["import { type ComponentProps, useEffect, useRef } from \"react\";\nimport type { Option } from \"@lattice-php/lattice/core/types\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { pillClassName } from \"./pill\";\n\n/**\n * Presentational segmented pill group. Used by the form choice field (bound to a\n * form value) and the core segmented control (standalone, emits an event).\n */\nexport function SegmentedPills({\n ariaLabel,\n autoFocus = false,\n className,\n disabled = false,\n name,\n onSelect,\n options,\n tabIndex,\n value,\n ...props\n}: Omit<ComponentProps<\"div\">, \"children\" | \"onSelect\" | \"ref\"> & {\n ariaLabel?: string;\n autoFocus?: boolean;\n disabled?: boolean;\n name: string;\n onSelect: (value: string) => void;\n options: Option[];\n tabIndex?: number;\n value: string;\n}) {\n const groupRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!autoFocus) {\n return;\n }\n\n const group = groupRef.current;\n const target =\n group?.querySelector<HTMLButtonElement>('button[aria-checked=\"true\"]') ??\n group?.querySelector<HTMLButtonElement>(\"button\");\n\n target?.focus();\n }, [autoFocus]);\n\n return (\n <div\n {...props}\n aria-label={ariaLabel}\n className={cn(\n \"inline-flex w-fit max-w-full gap-1 overflow-x-auto rounded-lt bg-lt-muted p-1\",\n className,\n )}\n ref={groupRef}\n role=\"radiogroup\"\n >\n {options.map((option) => {\n const isSelected = value === option.value;\n\n return (\n <button\n aria-checked={isSelected}\n data-test={`${name}-${option.value}`}\n className={cn(pillClassName(isSelected), disabled && \"cursor-not-allowed opacity-60\")}\n disabled={disabled}\n key={option.value}\n onClick={() => onSelect(option.value)}\n role=\"radio\"\n tabIndex={tabIndex}\n type=\"button\"\n >\n {option.label}\n </button>\n );\n })}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AASA,SAAgB,eAAe,EAC7B,WACA,YAAY,OACZ,WACA,WAAW,OACX,MACA,UACA,SACA,UACA,OACA,GAAG,SAUF;CACD,MAAM,WAAW,OAAuB,IAAI;CAE5C,gBAAgB;EACd,IAAI,CAAC,WACH;EAGF,MAAM,QAAQ,SAAS;EAKvB,CAHE,OAAO,cAAiC,+BAA6B,KACrE,OAAO,cAAiC,QAAQ,EAAA,EAE1C,MAAM;CAChB,GAAG,CAAC,SAAS,CAAC;CAEd,OACE,oBAAC,OAAD;EACE,GAAI;EACJ,cAAY;EACZ,WAAW,GACT,iFACA,SACF;EACA,KAAK;EACL,MAAK;EAEJ,UAAA,QAAQ,KAAK,WAAW;GACvB,MAAM,aAAa,UAAU,OAAO;GAEpC,OACE,oBAAC,UAAD;IACE,gBAAc;IACd,aAAW,GAAG,KAAK,GAAG,OAAO;IAC7B,WAAW,GAAG,cAAc,UAAU,GAAG,YAAY,+BAA+B;IAC1E;IAEV,eAAe,SAAS,OAAO,KAAK;IACpC,MAAK;IACK;IACV,MAAK;IAEJ,UAAA,OAAO;GACF,GAPD,OAAO,KAON;EAEZ,CAAC;CACE,CAAA;AAET"}