@adea-ai/ui 0.133.0 → 0.134.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.
@@ -36,6 +36,14 @@ export declare function SettingsRow(props: ComponentProps<'div'> & {
36
36
  orientation?: 'horizontal' | 'vertical';
37
37
  /** Hide the divider this row would otherwise contribute. */
38
38
  bare?: boolean;
39
+ /**
40
+ * Drop the row's own 16px padding so its label and control sit on the same
41
+ * edges as the fields around it. For a row placed loose in a
42
+ * `bodyLayout="content"` section, where nothing encloses it and the section's
43
+ * gap already spaces it from its neighbours. Leave it off inside a divided
44
+ * rows body or a `Card`, whose edge the padding is there to clear.
45
+ */
46
+ flush?: boolean;
39
47
  }): JSX.Element;
40
48
  /** A stacked label-and-control pair, for a control too wide for a row. */
41
49
  export declare function SettingsField(props: ComponentProps<'div'> & {
@@ -1 +1 @@
1
- {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/settings/settings.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAMnD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,cAAc,CAAC,SAAS,CAAC,GAAG;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kEAAkE;IAClE,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACpB,wFAAwF;IACxF,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAChC,eA0CF;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,qEAAqE;IACrE,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAA;IACvC,4DAA4D;IAC5D,IAAI,CAAC,EAAE,OAAO,CAAA;CACf,eAsDF;AAED,0EAA0E;AAC1E,wBAAgB,aAAa,CAC3B,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,eAiBzF;AAED,2EAA2E;AAC3E,wBAAgB,YAAY,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,eASxD"}
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/settings/settings.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAMnD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,cAAc,CAAC,SAAS,CAAC,GAAG;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kEAAkE;IAClE,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACpB,wFAAwF;IACxF,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAChC,eA0CF;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,qEAAqE;IACrE,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAA;IACvC,4DAA4D;IAC5D,IAAI,CAAC,EAAE,OAAO,CAAA;IACd;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,eAwDF;AAED,0EAA0E;AAC1E,wBAAgB,aAAa,CAC3B,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,eAiBzF;AAED,2EAA2E;AAC3E,wBAAgB,YAAY,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,eASxD"}
@@ -85,6 +85,7 @@ function SettingsRow(props) {
85
85
  "description",
86
86
  "orientation",
87
87
  "bare",
88
+ "flush",
88
89
  "children"
89
90
  ]);
90
91
  const rowId = createUniqueId();
@@ -103,7 +104,7 @@ function SettingsRow(props) {
103
104
  get children() {
104
105
  var _el$7 = _tmpl$4(), _el$8 = _el$7.firstChild, _el$9 = _el$8.firstChild, _el$1 = _el$8.nextSibling;
105
106
  spread(_el$7, mergeProps({ get ["class"]() {
106
- return cn("flex gap-4 p-4", local.orientation === "vertical" ? "flex-col" : "flex-col sm:flex-row sm:items-center sm:justify-between", local.class);
107
+ return cn("flex gap-4", local.flush ? "p-0" : "p-4", local.orientation === "vertical" ? "flex-col" : "flex-col sm:flex-row sm:items-center sm:justify-between", local.class);
107
108
  } }, rest), false, true);
108
109
  setAttribute(_el$9, "id", labelId);
109
110
  insert(_el$9, () => local.label);
@@ -1 +1 @@
1
- {"version":3,"file":"settings.js","names":["ComponentProps","JSX","createUniqueId","Show","splitProps","createFormFieldContext","FormFieldContext","cn","Heading","SettingsSection","props","title","description","action","Element","bodyLayout","local","rest","_el$","_tmpl$3","_el$2","firstChild","_el$3","_el$6","nextSibling","_$spread","_$mergeProps","class","_$insert","_$createComponent","as","size","children","when","_el$4","_tmpl$","_el$5","_tmpl$2","_$effect","_$className","SettingsRow","label","orientation","bare","rowId","labelId","descriptionId","field","defaultControlId","hint","error","undefined","group","Provider","value","_el$7","_tmpl$4","_el$8","_el$9","_el$1","_$setAttribute","_el$0","SettingsField","htmlFor","_el$10","_tmpl$5","_el$11","_el$12","_el$14","_el$13","SettingsPage","_el$15","_tmpl$6","_$mergeProps","_$createComponent","_$className","_$setAttribute"],"sources":["../../../../src/components/composites/settings/settings.tsx"],"sourcesContent":["import type { ComponentProps, JSX } from 'solid-js'\nimport { createUniqueId, Show, splitProps } from 'solid-js'\nimport { createFormFieldContext, FormFieldContext } from '#lib/form-field'\nimport { cn } from '#lib/utils'\nimport { Heading } from '../../ui/typography'\n\n/**\n * SettingsSection and SettingsRow.\n *\n * A settings page is the most repetitive surface in any application, and the\n * one where drift is most visible: label sizes, control widths and description\n * placement all wander unless something fixes them.\n *\n * The row puts the label and description at the start and the control at the\n * end (stacked on a narrow window), so controls line up down the page's end\n * edge regardless of how long the labels are. `SettingsRow` also\n * exposes its label and description through the shared form-field context to\n * the first control inside it. `SettingsField` is the visual stacked layout;\n * use `FormField` when the control needs label, help, or validation wiring.\n */\nexport function SettingsSection(\n props: ComponentProps<'section'> & {\n title: string\n description?: string\n /** A control for the section as a whole, e.g. a master toggle. */\n action?: JSX.Element\n /** Use a spaced content stack for fields, alerts, and cards instead of divided rows. */\n bodyLayout?: 'rows' | 'content'\n }\n) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'title',\n 'description',\n 'action',\n 'bodyLayout',\n 'children',\n ])\n\n return (\n <section\n data-slot=\"settings-section\"\n class={cn('flex flex-col gap-4 px-5 py-5', local.class)}\n {...rest}\n >\n <div class=\"flex items-start justify-between gap-4\">\n <div class=\"flex min-w-0 flex-col gap-0.5\">\n <Heading as=\"h2\" size=\"card\">\n {local.title}\n </Heading>\n <Show when={local.description}>\n <p class=\"max-w-prose text-sm text-muted-foreground text-pretty\">{local.description}</p>\n </Show>\n </div>\n <Show when={local.action}>\n <div class=\"flex shrink-0 items-center gap-2\">{local.action}</div>\n </Show>\n </div>\n <div\n data-slot=\"settings-section-body\"\n class={cn(\n local.bodyLayout === 'content'\n ? 'flex flex-col gap-4'\n : 'divide-y divide-border rounded-xl border border-border [&>[data-slot=empty-description]]:p-4'\n )}\n >\n {local.children}\n </div>\n </section>\n )\n}\n\n/**\n * One setting: a label and description on the leading side, a control on the\n * trailing side.\n *\n * The label and description are linked to a shared control through field\n * context. An explicit `aria-label` or `aria-labelledby` on the control keeps\n * precedence; otherwise the visible row label names it.\n */\nexport function SettingsRow(\n props: ComponentProps<'div'> & {\n label: string\n description?: string\n /** Render a vertical stack when the control needs the full width. */\n orientation?: 'horizontal' | 'vertical'\n /** Hide the divider this row would otherwise contribute. */\n bare?: boolean\n }\n) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'label',\n 'description',\n 'orientation',\n 'bare',\n 'children',\n ])\n const rowId = createUniqueId()\n const labelId = `${rowId}-label`\n const descriptionId = `${rowId}-description`\n const field = createFormFieldContext({\n defaultControlId: rowId,\n labelId,\n descriptionId,\n hint: () => local.description,\n error: () => undefined,\n group: () => false,\n })\n\n return (\n <FormFieldContext.Provider value={field}>\n <div\n data-slot=\"settings-row\"\n class={cn(\n 'flex gap-4 p-4',\n local.orientation === 'vertical'\n ? 'flex-col'\n : 'flex-col sm:flex-row sm:items-center sm:justify-between',\n local.class\n )}\n {...rest}\n >\n <div class=\"flex min-w-0 flex-col gap-0.5\">\n <div id={labelId} data-slot=\"settings-row-label\" class=\"text-sm font-medium\">\n {local.label}\n </div>\n <Show when={local.description}>\n <p id={descriptionId} class=\"max-w-prose text-sm text-muted-foreground text-pretty\">\n {local.description}\n </p>\n </Show>\n </div>\n <div\n data-slot=\"settings-row-control\"\n class={cn('shrink-0', local.orientation === 'vertical' && 'w-full')}\n >\n {local.children}\n </div>\n </div>\n </FormFieldContext.Provider>\n )\n}\n\n/** A stacked label-and-control pair, for a control too wide for a row. */\nexport function SettingsField(\n props: ComponentProps<'div'> & { label: string; description?: string; htmlFor?: string }\n) {\n const [local, rest] = splitProps(props, ['class', 'label', 'description', 'htmlFor', 'children'])\n\n return (\n <div data-slot=\"settings-field\" class={cn('flex flex-col gap-2 p-4', local.class)} {...rest}>\n <div class=\"flex flex-col gap-0.5\">\n <label for={local.htmlFor} class=\"text-sm font-medium\">\n {local.label}\n </label>\n <Show when={local.description}>\n <p class=\"max-w-prose text-sm text-muted-foreground text-pretty\">{local.description}</p>\n </Show>\n </div>\n <div>{local.children}</div>\n </div>\n )\n}\n\n/** A settings page's scrolling body, with the header sticky at the top. */\nexport function SettingsPage(props: ComponentProps<'div'>) {\n const [local, rest] = splitProps(props, ['class'])\n return (\n <div\n data-slot=\"settings-page\"\n class={cn('flex min-h-0 flex-1 flex-col overflow-y-auto', local.class)}\n {...rest}\n />\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"settings.js","names":["ComponentProps","JSX","createUniqueId","Show","splitProps","createFormFieldContext","FormFieldContext","cn","Heading","SettingsSection","props","title","description","action","Element","bodyLayout","local","rest","_el$","_tmpl$3","_el$2","firstChild","_el$3","_el$6","nextSibling","_$spread","_$mergeProps","class","_$insert","_$createComponent","as","size","children","when","_el$4","_tmpl$","_el$5","_tmpl$2","_$effect","_$className","SettingsRow","label","orientation","bare","flush","rowId","labelId","descriptionId","field","defaultControlId","hint","error","undefined","group","Provider","value","_el$7","_tmpl$4","_el$8","_el$9","_el$1","_$setAttribute","_el$0","SettingsField","htmlFor","_el$10","_tmpl$5","_el$11","_el$12","_el$14","_el$13","SettingsPage","_el$15","_tmpl$6","_$mergeProps","_$createComponent","_$className","_$setAttribute"],"sources":["../../../../src/components/composites/settings/settings.tsx"],"sourcesContent":["import type { ComponentProps, JSX } from 'solid-js'\nimport { createUniqueId, Show, splitProps } from 'solid-js'\nimport { createFormFieldContext, FormFieldContext } from '#lib/form-field'\nimport { cn } from '#lib/utils'\nimport { Heading } from '../../ui/typography'\n\n/**\n * SettingsSection and SettingsRow.\n *\n * A settings page is the most repetitive surface in any application, and the\n * one where drift is most visible: label sizes, control widths and description\n * placement all wander unless something fixes them.\n *\n * The row puts the label and description at the start and the control at the\n * end (stacked on a narrow window), so controls line up down the page's end\n * edge regardless of how long the labels are. `SettingsRow` also\n * exposes its label and description through the shared form-field context to\n * the first control inside it. `SettingsField` is the visual stacked layout;\n * use `FormField` when the control needs label, help, or validation wiring.\n */\nexport function SettingsSection(\n props: ComponentProps<'section'> & {\n title: string\n description?: string\n /** A control for the section as a whole, e.g. a master toggle. */\n action?: JSX.Element\n /** Use a spaced content stack for fields, alerts, and cards instead of divided rows. */\n bodyLayout?: 'rows' | 'content'\n }\n) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'title',\n 'description',\n 'action',\n 'bodyLayout',\n 'children',\n ])\n\n return (\n <section\n data-slot=\"settings-section\"\n class={cn('flex flex-col gap-4 px-5 py-5', local.class)}\n {...rest}\n >\n <div class=\"flex items-start justify-between gap-4\">\n <div class=\"flex min-w-0 flex-col gap-0.5\">\n <Heading as=\"h2\" size=\"card\">\n {local.title}\n </Heading>\n <Show when={local.description}>\n <p class=\"max-w-prose text-sm text-muted-foreground text-pretty\">{local.description}</p>\n </Show>\n </div>\n <Show when={local.action}>\n <div class=\"flex shrink-0 items-center gap-2\">{local.action}</div>\n </Show>\n </div>\n <div\n data-slot=\"settings-section-body\"\n class={cn(\n local.bodyLayout === 'content'\n ? 'flex flex-col gap-4'\n : 'divide-y divide-border rounded-xl border border-border [&>[data-slot=empty-description]]:p-4'\n )}\n >\n {local.children}\n </div>\n </section>\n )\n}\n\n/**\n * One setting: a label and description on the leading side, a control on the\n * trailing side.\n *\n * The label and description are linked to a shared control through field\n * context. An explicit `aria-label` or `aria-labelledby` on the control keeps\n * precedence; otherwise the visible row label names it.\n */\nexport function SettingsRow(\n props: ComponentProps<'div'> & {\n label: string\n description?: string\n /** Render a vertical stack when the control needs the full width. */\n orientation?: 'horizontal' | 'vertical'\n /** Hide the divider this row would otherwise contribute. */\n bare?: boolean\n /**\n * Drop the row's own 16px padding so its label and control sit on the same\n * edges as the fields around it. For a row placed loose in a\n * `bodyLayout=\"content\"` section, where nothing encloses it and the section's\n * gap already spaces it from its neighbours. Leave it off inside a divided\n * rows body or a `Card`, whose edge the padding is there to clear.\n */\n flush?: boolean\n }\n) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'label',\n 'description',\n 'orientation',\n 'bare',\n 'flush',\n 'children',\n ])\n const rowId = createUniqueId()\n const labelId = `${rowId}-label`\n const descriptionId = `${rowId}-description`\n const field = createFormFieldContext({\n defaultControlId: rowId,\n labelId,\n descriptionId,\n hint: () => local.description,\n error: () => undefined,\n group: () => false,\n })\n\n return (\n <FormFieldContext.Provider value={field}>\n <div\n data-slot=\"settings-row\"\n class={cn(\n 'flex gap-4',\n local.flush ? 'p-0' : 'p-4',\n local.orientation === 'vertical'\n ? 'flex-col'\n : 'flex-col sm:flex-row sm:items-center sm:justify-between',\n local.class\n )}\n {...rest}\n >\n <div class=\"flex min-w-0 flex-col gap-0.5\">\n <div id={labelId} data-slot=\"settings-row-label\" class=\"text-sm font-medium\">\n {local.label}\n </div>\n <Show when={local.description}>\n <p id={descriptionId} class=\"max-w-prose text-sm text-muted-foreground text-pretty\">\n {local.description}\n </p>\n </Show>\n </div>\n <div\n data-slot=\"settings-row-control\"\n class={cn('shrink-0', local.orientation === 'vertical' && 'w-full')}\n >\n {local.children}\n </div>\n </div>\n </FormFieldContext.Provider>\n )\n}\n\n/** A stacked label-and-control pair, for a control too wide for a row. */\nexport function SettingsField(\n props: ComponentProps<'div'> & { label: string; description?: string; htmlFor?: string }\n) {\n const [local, rest] = splitProps(props, ['class', 'label', 'description', 'htmlFor', 'children'])\n\n return (\n <div data-slot=\"settings-field\" class={cn('flex flex-col gap-2 p-4', local.class)} {...rest}>\n <div class=\"flex flex-col gap-0.5\">\n <label for={local.htmlFor} class=\"text-sm font-medium\">\n {local.label}\n </label>\n <Show when={local.description}>\n <p class=\"max-w-prose text-sm text-muted-foreground text-pretty\">{local.description}</p>\n </Show>\n </div>\n <div>{local.children}</div>\n </div>\n )\n}\n\n/** A settings page's scrolling body, with the header sticky at the top. */\nexport function SettingsPage(props: ComponentProps<'div'>) {\n const [local, rest] = splitProps(props, ['class'])\n return (\n <div\n data-slot=\"settings-page\"\n class={cn('flex min-h-0 flex-1 flex-col overflow-y-auto', local.class)}\n {...rest}\n />\n )\n}\n"],"mappings":"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"}
@@ -4,8 +4,27 @@ export type SecretInputGroupProps = {
4
4
  disabled: boolean;
5
5
  onChange: JSX.EventHandler<HTMLInputElement, Event>;
6
6
  onClear?: () => void;
7
+ /**
8
+ * What the clear action does, for this field: its accessible name and its
9
+ * tooltip while enabled ("Clear stored token", "Clear Google token path").
10
+ * Defaults to "Clear API key".
11
+ */
12
+ clearLabel?: string;
13
+ /**
14
+ * Why the clear action is unavailable, shown as its tooltip while the group is
15
+ * `disabled`. Defaults to the policy message, so name the real reason when a
16
+ * field is disabled for another one ("Set a token variable first").
17
+ */
18
+ disabledReason?: string;
7
19
  class?: string;
8
20
  } & Pick<ComponentProps<'input'>, 'id' | 'aria-label' | 'aria-describedby' | 'aria-invalid'>;
9
- /** A password input with an optional keyboard-accessible clear action. */
21
+ /**
22
+ * A password input with an optional keyboard-accessible clear action.
23
+ *
24
+ * The wording is the caller's: inside a `FormField` the input takes the field's
25
+ * label, outside one it needs `aria-label` (the fallback names an API key), and
26
+ * `clearLabel` and `disabledReason` name the clear action for the field it
27
+ * belongs to, so a bearer token or a token path is not announced as an API key.
28
+ */
10
29
  export declare function SecretInputGroup(props: SecretInputGroupProps): JSX.Element;
11
30
  //# sourceMappingURL=secret-input-group.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"secret-input-group.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/input-group/secret-input-group.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,cAAc,EAAE,KAAK,GAAG,EAAE,MAAM,UAAU,CAAA;AAKhF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,GAAG,CAAC,YAAY,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAA;IACnD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,IAAI,GAAG,YAAY,GAAG,kBAAkB,GAAG,cAAc,CAAC,CAAA;AAE5F,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,eA2C5D"}
1
+ {"version":3,"file":"secret-input-group.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/input-group/secret-input-group.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,cAAc,EAAE,KAAK,GAAG,EAAE,MAAM,UAAU,CAAA;AAKhF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,GAAG,CAAC,YAAY,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAA;IACnD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,IAAI,GAAG,YAAY,GAAG,kBAAkB,GAAG,cAAc,CAAC,CAAA;AAE5F;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,eAiD5D"}
@@ -1,10 +1,17 @@
1
1
  import { FormFieldContext } from "../../../lib/form-field.js";
2
2
  import { ActionButton } from "../../composites/action-button/action-button.js";
3
3
  import { InputGroup, InputGroupInput } from "./input-group.js";
4
- import { createComponent, mergeProps } from "solid-js/web";
4
+ import { createComponent, memo, mergeProps } from "solid-js/web";
5
5
  import { splitProps, useContext } from "solid-js";
6
6
  //#region src/components/ui/input-group/secret-input-group.tsx
7
- /** A password input with an optional keyboard-accessible clear action. */
7
+ /**
8
+ * A password input with an optional keyboard-accessible clear action.
9
+ *
10
+ * The wording is the caller's: inside a `FormField` the input takes the field's
11
+ * label, outside one it needs `aria-label` (the fallback names an API key), and
12
+ * `clearLabel` and `disabledReason` name the clear action for the field it
13
+ * belongs to, so a bearer token or a token path is not announced as an API key.
14
+ */
8
15
  function SecretInputGroup(props) {
9
16
  const field = useContext(FormFieldContext);
10
17
  const [local, rest] = splitProps(props, [
@@ -12,6 +19,8 @@ function SecretInputGroup(props) {
12
19
  "disabled",
13
20
  "onChange",
14
21
  "onClear",
22
+ "clearLabel",
23
+ "disabledReason",
15
24
  "aria-label",
16
25
  "class"
17
26
  ]);
@@ -51,10 +60,12 @@ function SecretInputGroup(props) {
51
60
  return local.disabled;
52
61
  },
53
62
  get tooltip() {
54
- return local.disabled ? "This API key is managed by policy" : "Clear API key";
63
+ return memo(() => !!local.disabled)() ? local.disabledReason ?? "This API key is managed by policy" : local.clearLabel ?? "Clear API key";
55
64
  },
56
65
  tooltipSide: "top",
57
- "aria-label": "Clear API key",
66
+ get ["aria-label"]() {
67
+ return local.clearLabel ?? "Clear API key";
68
+ },
58
69
  onClick: () => local.onClear?.(),
59
70
  children: "Clear"
60
71
  })];
@@ -1 +1 @@
1
- {"version":3,"file":"secret-input-group.js","names":["splitProps","useContext","ComponentProps","JSX","ActionButton","FormFieldContext","InputGroup","InputGroupInput","SecretInputGroupProps","value","disabled","onChange","EventHandler","HTMLInputElement","Event","onClear","class","Pick","SecretInputGroup","props","field","local","rest","input","_$createComponent","_$mergeProps","aria-label","undefined","type","autocomplete","onInput","children","variant","size","tooltip","tooltipSide","onClick","_$createComponent","_$mergeProps"],"sources":["../../../../src/components/ui/input-group/secret-input-group.tsx"],"sourcesContent":["import { splitProps, useContext, type ComponentProps, type JSX } from 'solid-js'\nimport { ActionButton } from '../../composites/action-button'\nimport { FormFieldContext } from '#lib/form-field'\nimport { InputGroup, InputGroupInput } from './input-group'\n\nexport type SecretInputGroupProps = {\n value: string\n disabled: boolean\n onChange: JSX.EventHandler<HTMLInputElement, Event>\n onClear?: () => void\n class?: string\n} & Pick<ComponentProps<'input'>, 'id' | 'aria-label' | 'aria-describedby' | 'aria-invalid'>\n\n/** A password input with an optional keyboard-accessible clear action. */\nexport function SecretInputGroup(props: SecretInputGroupProps) {\n const field = useContext(FormFieldContext)\n const [local, rest] = splitProps(props, [\n 'value',\n 'disabled',\n 'onChange',\n 'onClear',\n 'aria-label',\n 'class',\n ])\n\n const input = (\n <InputGroupInput\n {...rest}\n aria-label={local['aria-label'] ?? (field ? undefined : 'New API key')}\n type=\"password\"\n autocomplete=\"new-password\"\n value={local.value}\n disabled={local.disabled}\n onInput={local.onChange}\n />\n )\n\n if (!local.onClear) return <InputGroup class={local.class}>{input}</InputGroup>\n\n return (\n <InputGroup class={local.class}>\n {input}\n <ActionButton\n data-slot=\"input-group-button\"\n variant=\"ghost\"\n size=\"xs\"\n class=\"-me-1 h-6 min-w-0 shrink-0 px-2\"\n disabled={local.disabled}\n tooltip={local.disabled ? 'This API key is managed by policy' : 'Clear API key'}\n tooltipSide=\"top\"\n aria-label=\"Clear API key\"\n onClick={() => local.onClear?.()}\n >\n Clear\n </ActionButton>\n </InputGroup>\n )\n}\n"],"mappings":";;;;;;;AAcA,SAAgBkB,iBAAiBC,OAA8B;CAC7D,MAAMC,QAAQnB,WAAWI,gBAAgB;CACzC,MAAM,CAACgB,OAAOC,QAAQtB,WAAWmB,OAAO;EACtC;EACA;EACA;EACA;EACA;EACA;CAAO,CACR;CAED,MAAMI,QAAKC,gBACRjB,iBAAekB,WACVH,MAAI;EAAA,KAAA,gBAAA;GAAA,OACID,MAAM,kBAAkBD,QAAQO,KAAAA,IAAY;EAAc;EACtEC,MAAI;EACJC,cAAY;EAAA,IACZpB,QAAK;GAAA,OAAEY,MAAMZ;EAAK;EAAA,IAClBC,WAAQ;GAAA,OAAEW,MAAMX;EAAQ;EAAA,IACxBoB,UAAO;GAAA,OAAET,MAAMV;EAAQ;CAAA,CAAA,CAAA;CAI3B,IAAI,CAACU,MAAMN,SAAS,OAAAS,gBAAQlB,YAAU;EAAA,KAAA,WAAA;GAAA,OAAQe,MAAML;EAAK;EAAAe,UAAGR;CAAK,CAAA;CAEjE,OAAAC,gBACGlB,YAAU;EAAA,KAAA,WAAA;GAAA,OAAQe,MAAML;EAAK;EAAA,IAAAe,WAAA;GAAA,OAAA,CAC3BR,OAAKC,gBACLpB,cAAY;IAAA,aAAA;IAEX4B,SAAO;IACPC,MAAI;IAAA,SAAA;IAAA,IAEJvB,WAAQ;KAAA,OAAEW,MAAMX;IAAQ;IAAA,IACxBwB,UAAO;KAAA,OAAEb,MAAMX,WAAW,sCAAsC;IAAe;IAC/EyB,aAAW;IAAA,cAAA;IAEXC,eAAef,MAAMN,UAAU;IAACgB,UAAA;GAAA,CAAA,CAAA;EAAA;CAAA,CAAA;AAMxC"}
1
+ {"version":3,"file":"secret-input-group.js","names":["splitProps","useContext","ComponentProps","JSX","ActionButton","FormFieldContext","InputGroup","InputGroupInput","SecretInputGroupProps","value","disabled","onChange","EventHandler","HTMLInputElement","Event","onClear","clearLabel","disabledReason","class","Pick","SecretInputGroup","props","field","local","rest","input","_$createComponent","_$mergeProps","aria-label","undefined","type","autocomplete","onInput","children","variant","size","tooltip","_$memo","tooltipSide","onClick","_$createComponent","_$mergeProps","_$memo"],"sources":["../../../../src/components/ui/input-group/secret-input-group.tsx"],"sourcesContent":["import { splitProps, useContext, type ComponentProps, type JSX } from 'solid-js'\nimport { ActionButton } from '../../composites/action-button'\nimport { FormFieldContext } from '#lib/form-field'\nimport { InputGroup, InputGroupInput } from './input-group'\n\nexport type SecretInputGroupProps = {\n value: string\n disabled: boolean\n onChange: JSX.EventHandler<HTMLInputElement, Event>\n onClear?: () => void\n /**\n * What the clear action does, for this field: its accessible name and its\n * tooltip while enabled (\"Clear stored token\", \"Clear Google token path\").\n * Defaults to \"Clear API key\".\n */\n clearLabel?: string\n /**\n * Why the clear action is unavailable, shown as its tooltip while the group is\n * `disabled`. Defaults to the policy message, so name the real reason when a\n * field is disabled for another one (\"Set a token variable first\").\n */\n disabledReason?: string\n class?: string\n} & Pick<ComponentProps<'input'>, 'id' | 'aria-label' | 'aria-describedby' | 'aria-invalid'>\n\n/**\n * A password input with an optional keyboard-accessible clear action.\n *\n * The wording is the caller's: inside a `FormField` the input takes the field's\n * label, outside one it needs `aria-label` (the fallback names an API key), and\n * `clearLabel` and `disabledReason` name the clear action for the field it\n * belongs to, so a bearer token or a token path is not announced as an API key.\n */\nexport function SecretInputGroup(props: SecretInputGroupProps) {\n const field = useContext(FormFieldContext)\n const [local, rest] = splitProps(props, [\n 'value',\n 'disabled',\n 'onChange',\n 'onClear',\n 'clearLabel',\n 'disabledReason',\n 'aria-label',\n 'class',\n ])\n\n const input = (\n <InputGroupInput\n {...rest}\n aria-label={local['aria-label'] ?? (field ? undefined : 'New API key')}\n type=\"password\"\n autocomplete=\"new-password\"\n value={local.value}\n disabled={local.disabled}\n onInput={local.onChange}\n />\n )\n\n if (!local.onClear) return <InputGroup class={local.class}>{input}</InputGroup>\n\n return (\n <InputGroup class={local.class}>\n {input}\n <ActionButton\n data-slot=\"input-group-button\"\n variant=\"ghost\"\n size=\"xs\"\n class=\"-me-1 h-6 min-w-0 shrink-0 px-2\"\n disabled={local.disabled}\n tooltip={\n local.disabled\n ? (local.disabledReason ?? 'This API key is managed by policy')\n : (local.clearLabel ?? 'Clear API key')\n }\n tooltipSide=\"top\"\n aria-label={local.clearLabel ?? 'Clear API key'}\n onClick={() => local.onClear?.()}\n >\n Clear\n </ActionButton>\n </InputGroup>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;AAiCA,SAAgBoB,iBAAiBC,OAA8B;CAC7D,MAAMC,QAAQrB,WAAWI,gBAAgB;CACzC,MAAM,CAACkB,OAAOC,QAAQxB,WAAWqB,OAAO;EACtC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CAAO,CACR;CAED,MAAMI,QAAKC,gBACRnB,iBAAeoB,WACVH,MAAI;EAAA,KAAA,gBAAA;GAAA,OACID,MAAM,kBAAkBD,QAAQO,KAAAA,IAAY;EAAc;EACtEC,MAAI;EACJC,cAAY;EAAA,IACZtB,QAAK;GAAA,OAAEc,MAAMd;EAAK;EAAA,IAClBC,WAAQ;GAAA,OAAEa,MAAMb;EAAQ;EAAA,IACxBsB,UAAO;GAAA,OAAET,MAAMZ;EAAQ;CAAA,CAAA,CAAA;CAI3B,IAAI,CAACY,MAAMR,SAAS,OAAAW,gBAAQpB,YAAU;EAAA,KAAA,WAAA;GAAA,OAAQiB,MAAML;EAAK;EAAAe,UAAGR;CAAK,CAAA;CAEjE,OAAAC,gBACGpB,YAAU;EAAA,KAAA,WAAA;GAAA,OAAQiB,MAAML;EAAK;EAAA,IAAAe,WAAA;GAAA,OAAA,CAC3BR,OAAKC,gBACLtB,cAAY;IAAA,aAAA;IAEX8B,SAAO;IACPC,MAAI;IAAA,SAAA;IAAA,IAEJzB,WAAQ;KAAA,OAAEa,MAAMb;IAAQ;IAAA,IACxB0B,UAAO;KAAA,OACLC,WAAA,CAAA,CAAAd,MAAMb,QAAQ,CAAA,CAAA,IACTa,MAAMN,kBAAkB,sCACxBM,MAAMP,cAAc;IAAgB;IAE3CsB,aAAW;IAAA,KAAA,gBAAA;KAAA,OACCf,MAAMP,cAAc;IAAe;IAC/CuB,eAAehB,MAAMR,UAAU;IAACkB,UAAA;GAAA,CAAA,CAAA;EAAA;CAAA,CAAA;AAMxC"}
@@ -7,6 +7,14 @@ export type NativeSelectProps = Omit<ComponentProps<'select'>, 'size'> & {
7
7
  * control that this styled single-line select is not.
8
8
  */
9
9
  size?: 'sm' | 'md' | 'lg';
10
+ /**
11
+ * Layout classes for the element that wraps the select and its chevron: a
12
+ * width or `max-w-*`, `flex-1`, `col-span-*`, a margin. `class` reaches the
13
+ * `<select>` itself, which is the wrong box for any of those, because the
14
+ * wrapper is what a flex or grid parent lays out. The select fills the
15
+ * wrapper, so the chevron stays at the control's edge at any width.
16
+ */
17
+ wrapperClass?: string;
10
18
  /** Render the choices from data instead of raw option children. */
11
19
  options?: readonly NativeSelectOption[];
12
20
  /** Initialize an uncontrolled selection after its native options mount. */
@@ -1 +1 @@
1
- {"version":3,"file":"native-select.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/native-select/native-select.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAK9C,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,GAAG;IACvE;;;;;OAKG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACzB,mEAAmE;IACnE,OAAO,CAAC,EAAE,SAAS,kBAAkB,EAAE,CAAA;IACvC,2EAA2E;IAC3E,YAAY,CAAC,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAA;CACjD,CAAA;AAkCD;;;;;;;;;GASG;AACH,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAC,CAAA;AAEF,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,kCAuFpD"}
1
+ {"version":3,"file":"native-select.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/native-select/native-select.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAK9C,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,GAAG;IACvE;;;;;OAKG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACzB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,mEAAmE;IACnE,OAAO,CAAC,EAAE,SAAS,kBAAkB,EAAE,CAAA;IACvC,2EAA2E;IAC3E,YAAY,CAAC,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAA;CACjD,CAAA;AAkCD;;;;;;;;;GASG;AACH,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAC,CAAA;AAEF,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,kCA2FpD"}
@@ -1,10 +1,10 @@
1
1
  import { cn } from "../../../lib/utils.js";
2
2
  import { useFormFieldControl } from "../../../lib/form-field.js";
3
- import { createComponent, effect, insert, mergeProps, spread, template, use } from "solid-js/web";
3
+ import { className, createComponent, effect, insert, mergeProps, spread, template, use } from "solid-js/web";
4
4
  import { ChevronDownIcon } from "lucide-solid";
5
5
  import { For, Show, createEffect, onMount, splitProps } from "solid-js";
6
6
  //#region src/components/ui/native-select/native-select.tsx
7
- var _tmpl$ = /*#__PURE__*/ template(`<span data-slot=native-select-wrapper class="relative inline-flex min-w-0"><select data-slot=native-select>`);
7
+ var _tmpl$ = /*#__PURE__*/ template(`<span data-slot=native-select-wrapper><select data-slot=native-select>`);
8
8
  var _tmpl$2 = /*#__PURE__*/ template(`<option>`);
9
9
  function setSelectedValue(select, value, multiple) {
10
10
  if (value === void 0) return;
@@ -24,6 +24,7 @@ function initializeDefaultSelection(select, value, multiple) {
24
24
  function NativeSelect(props) {
25
25
  const [local, rest] = splitProps(props, [
26
26
  "class",
27
+ "wrapperClass",
27
28
  "size",
28
29
  "children",
29
30
  "defaultValue",
@@ -76,7 +77,7 @@ function NativeSelect(props) {
76
77
  return field?.["aria-errormessage"] ?? local["aria-errormessage"];
77
78
  },
78
79
  get ["class"]() {
79
- return cn("min-w-0 appearance-none rounded-md border border-input bg-transparent", {
80
+ return cn("w-full min-w-0 appearance-none rounded-md border border-input bg-transparent", {
80
81
  "h-control-sm px-control-sm pr-8 text-xs": local.size === "sm",
81
82
  "h-control-md px-control-md pr-9 text-sm": local.size === "md" || local.size === void 0,
82
83
  "h-control-lg px-control-lg pr-9 text-sm": local.size === "lg"
@@ -110,6 +111,7 @@ function NativeSelect(props) {
110
111
  return cn("pointer-events-none absolute end-2 top-1/2 -translate-y-1/2 text-muted-foreground", local.size === "sm" ? "size-3.5" : "size-4");
111
112
  }
112
113
  }), null);
114
+ effect(() => className(_el$, cn("relative inline-flex min-w-0", local.wrapperClass)));
113
115
  return _el$;
114
116
  })();
115
117
  }
@@ -1 +1 @@
1
- {"version":3,"file":"native-select.js","names":["ChevronDownIcon","ComponentProps","createEffect","For","onMount","Show","splitProps","useFormFieldControl","cn","NativeSelectProps","Omit","size","options","NativeSelectOption","defaultValue","setSelectedValue","select","HTMLSelectElement","value","multiple","undefined","Array","isArray","selected","Set","map","String","option","has","initializeDefaultSelection","defaultValues","defaultSelected","Readonly","label","disabled","NativeSelect","props","local","rest","field","id","Boolean","_el$","_tmpl$","_el$2","firstChild","_$use","element","_$spread","_$mergeProps","aria-label","aria-labelledby","aria-describedby","aria-invalid","aria-errormessage","class","_$insert","_$createComponent","when","fallback","children","each","_el$3","_tmpl$2","_$effect","_$mergeProps","_$createComponent"],"sources":["../../../../src/components/ui/native-select/native-select.tsx"],"sourcesContent":["import { ChevronDownIcon } from 'lucide-solid'\nimport type { ComponentProps } from 'solid-js'\nimport { createEffect, For, onMount, Show, splitProps } from 'solid-js'\nimport { useFormFieldControl } from '#lib/form-field'\nimport { cn } from '#lib/utils'\n\nexport type NativeSelectProps = Omit<ComponentProps<'select'>, 'size'> & {\n /**\n * The control rung, matching `Input` and `SelectTrigger`: `sm` for a dense\n * toolbar or pane filter, `md` (the default) everywhere else. This replaces the\n * native `size` attribute, which turns a select into a list box — a different\n * control that this styled single-line select is not.\n */\n size?: 'sm' | 'md' | 'lg'\n /** Render the choices from data instead of raw option children. */\n options?: readonly NativeSelectOption[]\n /** Initialize an uncontrolled selection after its native options mount. */\n defaultValue?: ComponentProps<'select'>['value']\n}\n\nfunction setSelectedValue(\n select: HTMLSelectElement,\n value: ComponentProps<'select'>['value'],\n multiple: boolean\n) {\n if (value === undefined) return\n\n if (multiple && Array.isArray(value)) {\n const selected = new Set(value.map(String))\n for (const option of select.options) option.selected = selected.has(option.value)\n return\n }\n\n select.value = String(Array.isArray(value) ? (value[0] ?? '') : value)\n}\n\nfunction initializeDefaultSelection(\n select: HTMLSelectElement,\n value: ComponentProps<'select'>['value'],\n multiple: boolean\n) {\n if (value === undefined) return\n\n const defaultValues =\n multiple && Array.isArray(value)\n ? new Set(value.map(String))\n : new Set([String(Array.isArray(value) ? (value[0] ?? '') : value)])\n\n for (const option of select.options) option.defaultSelected = defaultValues.has(option.value)\n setSelectedValue(select, value, multiple)\n}\n\n/**\n * NativeSelect.\n *\n * A styled native `<select>` for compact, finite choices. Use `Select` when the\n * list needs a custom popover, typed options, or runtime filtering; this keeps\n * the browser's built-in keyboard, form, option, and label behavior.\n *\n * Translated from shadcn/ui's MIT-licensed NativeSelect wrapper to SolidJS and\n * the Adea token system; see the root NOTICE for source and attribution.\n */\nexport type NativeSelectOption = Readonly<{\n value: string\n label: string\n disabled?: boolean\n}>\n\nexport function NativeSelect(props: NativeSelectProps) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'size',\n 'children',\n 'defaultValue',\n 'options',\n 'id',\n 'aria-label',\n 'aria-labelledby',\n 'aria-describedby',\n 'aria-invalid',\n 'aria-errormessage',\n ])\n const field = useFormFieldControl('control', {\n id: local.id,\n 'aria-label': local['aria-label'],\n 'aria-labelledby': local['aria-labelledby'],\n 'aria-describedby': local['aria-describedby'],\n 'aria-invalid': local['aria-invalid'],\n 'aria-errormessage': local['aria-errormessage'],\n })\n let select: HTMLSelectElement | undefined\n\n // Set selection after native options exist; setting select.value before its\n // children are mounted makes browsers fall back to the first option.\n createEffect(() => {\n if (!select) return\n setSelectedValue(select, rest.value, Boolean(rest.multiple))\n })\n onMount(() => {\n if (select && rest.value === undefined) {\n initializeDefaultSelection(select, local.defaultValue, Boolean(rest.multiple))\n }\n })\n\n return (\n <span data-slot=\"native-select-wrapper\" class=\"relative inline-flex min-w-0\">\n <select\n ref={(element) => {\n select = element\n }}\n data-slot=\"native-select\"\n id={field?.id ?? local.id}\n aria-label={local['aria-label']}\n aria-labelledby={field?.['aria-labelledby'] ?? local['aria-labelledby']}\n aria-describedby={field?.['aria-describedby'] ?? local['aria-describedby']}\n aria-invalid={field?.['aria-invalid'] ?? local['aria-invalid']}\n aria-errormessage={field?.['aria-errormessage'] ?? local['aria-errormessage']}\n class={cn(\n 'min-w-0 appearance-none rounded-md border border-input bg-transparent',\n {\n 'h-control-sm px-control-sm pr-8 text-xs': local.size === 'sm',\n 'h-control-md px-control-md pr-9 text-sm':\n local.size === 'md' || local.size === undefined,\n 'h-control-lg px-control-lg pr-9 text-sm': local.size === 'lg',\n },\n 'transition-[color,box-shadow,border-color] ease-out outline-none',\n 'focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-primary-subtle',\n 'aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive-subtle',\n 'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 dark:bg-input/30',\n local.class\n )}\n {...rest}\n >\n <Show when={local.options} fallback={local.children}>\n {(options) => (\n <For each={options()}>\n {(option) => (\n <option value={option.value} disabled={option.disabled}>\n {option.label}\n </option>\n )}\n </For>\n )}\n </Show>\n </select>\n <ChevronDownIcon\n data-slot=\"native-select-icon\"\n aria-hidden=\"true\"\n class={cn(\n 'pointer-events-none absolute end-2 top-1/2 -translate-y-1/2 text-muted-foreground',\n local.size === 'sm' ? 'size-3.5' : 'size-4'\n )}\n />\n </span>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"native-select.js","names":["ChevronDownIcon","ComponentProps","createEffect","For","onMount","Show","splitProps","useFormFieldControl","cn","NativeSelectProps","Omit","size","wrapperClass","options","NativeSelectOption","defaultValue","setSelectedValue","select","HTMLSelectElement","value","multiple","undefined","Array","isArray","selected","Set","map","String","option","has","initializeDefaultSelection","defaultValues","defaultSelected","Readonly","label","disabled","NativeSelect","props","local","rest","field","id","Boolean","_el$","_tmpl$","_el$2","firstChild","_$use","element","_$spread","_$mergeProps","aria-label","aria-labelledby","aria-describedby","aria-invalid","aria-errormessage","class","_$insert","_$createComponent","when","fallback","children","each","_el$3","_tmpl$2","_$effect","_$className","_$mergeProps","_$createComponent","_$className"],"sources":["../../../../src/components/ui/native-select/native-select.tsx"],"sourcesContent":["import { ChevronDownIcon } from 'lucide-solid'\nimport type { ComponentProps } from 'solid-js'\nimport { createEffect, For, onMount, Show, splitProps } from 'solid-js'\nimport { useFormFieldControl } from '#lib/form-field'\nimport { cn } from '#lib/utils'\n\nexport type NativeSelectProps = Omit<ComponentProps<'select'>, 'size'> & {\n /**\n * The control rung, matching `Input` and `SelectTrigger`: `sm` for a dense\n * toolbar or pane filter, `md` (the default) everywhere else. This replaces the\n * native `size` attribute, which turns a select into a list box — a different\n * control that this styled single-line select is not.\n */\n size?: 'sm' | 'md' | 'lg'\n /**\n * Layout classes for the element that wraps the select and its chevron: a\n * width or `max-w-*`, `flex-1`, `col-span-*`, a margin. `class` reaches the\n * `<select>` itself, which is the wrong box for any of those, because the\n * wrapper is what a flex or grid parent lays out. The select fills the\n * wrapper, so the chevron stays at the control's edge at any width.\n */\n wrapperClass?: string\n /** Render the choices from data instead of raw option children. */\n options?: readonly NativeSelectOption[]\n /** Initialize an uncontrolled selection after its native options mount. */\n defaultValue?: ComponentProps<'select'>['value']\n}\n\nfunction setSelectedValue(\n select: HTMLSelectElement,\n value: ComponentProps<'select'>['value'],\n multiple: boolean\n) {\n if (value === undefined) return\n\n if (multiple && Array.isArray(value)) {\n const selected = new Set(value.map(String))\n for (const option of select.options) option.selected = selected.has(option.value)\n return\n }\n\n select.value = String(Array.isArray(value) ? (value[0] ?? '') : value)\n}\n\nfunction initializeDefaultSelection(\n select: HTMLSelectElement,\n value: ComponentProps<'select'>['value'],\n multiple: boolean\n) {\n if (value === undefined) return\n\n const defaultValues =\n multiple && Array.isArray(value)\n ? new Set(value.map(String))\n : new Set([String(Array.isArray(value) ? (value[0] ?? '') : value)])\n\n for (const option of select.options) option.defaultSelected = defaultValues.has(option.value)\n setSelectedValue(select, value, multiple)\n}\n\n/**\n * NativeSelect.\n *\n * A styled native `<select>` for compact, finite choices. Use `Select` when the\n * list needs a custom popover, typed options, or runtime filtering; this keeps\n * the browser's built-in keyboard, form, option, and label behavior.\n *\n * Translated from shadcn/ui's MIT-licensed NativeSelect wrapper to SolidJS and\n * the Adea token system; see the root NOTICE for source and attribution.\n */\nexport type NativeSelectOption = Readonly<{\n value: string\n label: string\n disabled?: boolean\n}>\n\nexport function NativeSelect(props: NativeSelectProps) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'wrapperClass',\n 'size',\n 'children',\n 'defaultValue',\n 'options',\n 'id',\n 'aria-label',\n 'aria-labelledby',\n 'aria-describedby',\n 'aria-invalid',\n 'aria-errormessage',\n ])\n const field = useFormFieldControl('control', {\n id: local.id,\n 'aria-label': local['aria-label'],\n 'aria-labelledby': local['aria-labelledby'],\n 'aria-describedby': local['aria-describedby'],\n 'aria-invalid': local['aria-invalid'],\n 'aria-errormessage': local['aria-errormessage'],\n })\n let select: HTMLSelectElement | undefined\n\n // Set selection after native options exist; setting select.value before its\n // children are mounted makes browsers fall back to the first option.\n createEffect(() => {\n if (!select) return\n setSelectedValue(select, rest.value, Boolean(rest.multiple))\n })\n onMount(() => {\n if (select && rest.value === undefined) {\n initializeDefaultSelection(select, local.defaultValue, Boolean(rest.multiple))\n }\n })\n\n return (\n <span\n data-slot=\"native-select-wrapper\"\n class={cn('relative inline-flex min-w-0', local.wrapperClass)}\n >\n <select\n ref={(element) => {\n select = element\n }}\n data-slot=\"native-select\"\n id={field?.id ?? local.id}\n aria-label={local['aria-label']}\n aria-labelledby={field?.['aria-labelledby'] ?? local['aria-labelledby']}\n aria-describedby={field?.['aria-describedby'] ?? local['aria-describedby']}\n aria-invalid={field?.['aria-invalid'] ?? local['aria-invalid']}\n aria-errormessage={field?.['aria-errormessage'] ?? local['aria-errormessage']}\n class={cn(\n 'w-full min-w-0 appearance-none rounded-md border border-input bg-transparent',\n {\n 'h-control-sm px-control-sm pr-8 text-xs': local.size === 'sm',\n 'h-control-md px-control-md pr-9 text-sm':\n local.size === 'md' || local.size === undefined,\n 'h-control-lg px-control-lg pr-9 text-sm': local.size === 'lg',\n },\n 'transition-[color,box-shadow,border-color] ease-out outline-none',\n 'focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-primary-subtle',\n 'aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive-subtle',\n 'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 dark:bg-input/30',\n local.class\n )}\n {...rest}\n >\n <Show when={local.options} fallback={local.children}>\n {(options) => (\n <For each={options()}>\n {(option) => (\n <option value={option.value} disabled={option.disabled}>\n {option.label}\n </option>\n )}\n </For>\n )}\n </Show>\n </select>\n <ChevronDownIcon\n data-slot=\"native-select-icon\"\n aria-hidden=\"true\"\n class={cn(\n 'pointer-events-none absolute end-2 top-1/2 -translate-y-1/2 text-muted-foreground',\n local.size === 'sm' ? 'size-3.5' : 'size-4'\n )}\n />\n </span>\n )\n}\n"],"mappings":"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"}
@@ -86,6 +86,14 @@ export function SettingsRow(
86
86
  orientation?: 'horizontal' | 'vertical'
87
87
  /** Hide the divider this row would otherwise contribute. */
88
88
  bare?: boolean
89
+ /**
90
+ * Drop the row's own 16px padding so its label and control sit on the same
91
+ * edges as the fields around it. For a row placed loose in a
92
+ * `bodyLayout="content"` section, where nothing encloses it and the section's
93
+ * gap already spaces it from its neighbours. Leave it off inside a divided
94
+ * rows body or a `Card`, whose edge the padding is there to clear.
95
+ */
96
+ flush?: boolean
89
97
  }
90
98
  ) {
91
99
  const [local, rest] = splitProps(props, [
@@ -94,6 +102,7 @@ export function SettingsRow(
94
102
  'description',
95
103
  'orientation',
96
104
  'bare',
105
+ 'flush',
97
106
  'children',
98
107
  ])
99
108
  const rowId = createUniqueId()
@@ -113,7 +122,8 @@ export function SettingsRow(
113
122
  <div
114
123
  data-slot="settings-row"
115
124
  class={cn(
116
- 'flex gap-4 p-4',
125
+ 'flex gap-4',
126
+ local.flush ? 'p-0' : 'p-4',
117
127
  local.orientation === 'vertical'
118
128
  ? 'flex-col'
119
129
  : 'flex-col sm:flex-row sm:items-center sm:justify-between',
@@ -8,10 +8,29 @@ export type SecretInputGroupProps = {
8
8
  disabled: boolean
9
9
  onChange: JSX.EventHandler<HTMLInputElement, Event>
10
10
  onClear?: () => void
11
+ /**
12
+ * What the clear action does, for this field: its accessible name and its
13
+ * tooltip while enabled ("Clear stored token", "Clear Google token path").
14
+ * Defaults to "Clear API key".
15
+ */
16
+ clearLabel?: string
17
+ /**
18
+ * Why the clear action is unavailable, shown as its tooltip while the group is
19
+ * `disabled`. Defaults to the policy message, so name the real reason when a
20
+ * field is disabled for another one ("Set a token variable first").
21
+ */
22
+ disabledReason?: string
11
23
  class?: string
12
24
  } & Pick<ComponentProps<'input'>, 'id' | 'aria-label' | 'aria-describedby' | 'aria-invalid'>
13
25
 
14
- /** A password input with an optional keyboard-accessible clear action. */
26
+ /**
27
+ * A password input with an optional keyboard-accessible clear action.
28
+ *
29
+ * The wording is the caller's: inside a `FormField` the input takes the field's
30
+ * label, outside one it needs `aria-label` (the fallback names an API key), and
31
+ * `clearLabel` and `disabledReason` name the clear action for the field it
32
+ * belongs to, so a bearer token or a token path is not announced as an API key.
33
+ */
15
34
  export function SecretInputGroup(props: SecretInputGroupProps) {
16
35
  const field = useContext(FormFieldContext)
17
36
  const [local, rest] = splitProps(props, [
@@ -19,6 +38,8 @@ export function SecretInputGroup(props: SecretInputGroupProps) {
19
38
  'disabled',
20
39
  'onChange',
21
40
  'onClear',
41
+ 'clearLabel',
42
+ 'disabledReason',
22
43
  'aria-label',
23
44
  'class',
24
45
  ])
@@ -46,9 +67,13 @@ export function SecretInputGroup(props: SecretInputGroupProps) {
46
67
  size="xs"
47
68
  class="-me-1 h-6 min-w-0 shrink-0 px-2"
48
69
  disabled={local.disabled}
49
- tooltip={local.disabled ? 'This API key is managed by policy' : 'Clear API key'}
70
+ tooltip={
71
+ local.disabled
72
+ ? (local.disabledReason ?? 'This API key is managed by policy')
73
+ : (local.clearLabel ?? 'Clear API key')
74
+ }
50
75
  tooltipSide="top"
51
- aria-label="Clear API key"
76
+ aria-label={local.clearLabel ?? 'Clear API key'}
52
77
  onClick={() => local.onClear?.()}
53
78
  >
54
79
  Clear
@@ -12,6 +12,14 @@ export type NativeSelectProps = Omit<ComponentProps<'select'>, 'size'> & {
12
12
  * control that this styled single-line select is not.
13
13
  */
14
14
  size?: 'sm' | 'md' | 'lg'
15
+ /**
16
+ * Layout classes for the element that wraps the select and its chevron: a
17
+ * width or `max-w-*`, `flex-1`, `col-span-*`, a margin. `class` reaches the
18
+ * `<select>` itself, which is the wrong box for any of those, because the
19
+ * wrapper is what a flex or grid parent lays out. The select fills the
20
+ * wrapper, so the chevron stays at the control's edge at any width.
21
+ */
22
+ wrapperClass?: string
15
23
  /** Render the choices from data instead of raw option children. */
16
24
  options?: readonly NativeSelectOption[]
17
25
  /** Initialize an uncontrolled selection after its native options mount. */
@@ -69,6 +77,7 @@ export type NativeSelectOption = Readonly<{
69
77
  export function NativeSelect(props: NativeSelectProps) {
70
78
  const [local, rest] = splitProps(props, [
71
79
  'class',
80
+ 'wrapperClass',
72
81
  'size',
73
82
  'children',
74
83
  'defaultValue',
@@ -103,7 +112,10 @@ export function NativeSelect(props: NativeSelectProps) {
103
112
  })
104
113
 
105
114
  return (
106
- <span data-slot="native-select-wrapper" class="relative inline-flex min-w-0">
115
+ <span
116
+ data-slot="native-select-wrapper"
117
+ class={cn('relative inline-flex min-w-0', local.wrapperClass)}
118
+ >
107
119
  <select
108
120
  ref={(element) => {
109
121
  select = element
@@ -116,7 +128,7 @@ export function NativeSelect(props: NativeSelectProps) {
116
128
  aria-invalid={field?.['aria-invalid'] ?? local['aria-invalid']}
117
129
  aria-errormessage={field?.['aria-errormessage'] ?? local['aria-errormessage']}
118
130
  class={cn(
119
- 'min-w-0 appearance-none rounded-md border border-input bg-transparent',
131
+ 'w-full min-w-0 appearance-none rounded-md border border-input bg-transparent',
120
132
  {
121
133
  'h-control-sm px-control-sm pr-8 text-xs': local.size === 'sm',
122
134
  'h-control-md px-control-md pr-9 text-sm':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adea-ai/ui",
3
- "version": "0.133.0",
3
+ "version": "0.134.0",
4
4
  "description": "The Adea design system: Kobalte-backed Solid components, semantic OKLCH tokens, and the app-shell layout primitives shared by Adea and Cortana.",
5
5
  "keywords": [
6
6
  "adea",
@@ -86,6 +86,14 @@ export function SettingsRow(
86
86
  orientation?: 'horizontal' | 'vertical'
87
87
  /** Hide the divider this row would otherwise contribute. */
88
88
  bare?: boolean
89
+ /**
90
+ * Drop the row's own 16px padding so its label and control sit on the same
91
+ * edges as the fields around it. For a row placed loose in a
92
+ * `bodyLayout="content"` section, where nothing encloses it and the section's
93
+ * gap already spaces it from its neighbours. Leave it off inside a divided
94
+ * rows body or a `Card`, whose edge the padding is there to clear.
95
+ */
96
+ flush?: boolean
89
97
  }
90
98
  ) {
91
99
  const [local, rest] = splitProps(props, [
@@ -94,6 +102,7 @@ export function SettingsRow(
94
102
  'description',
95
103
  'orientation',
96
104
  'bare',
105
+ 'flush',
97
106
  'children',
98
107
  ])
99
108
  const rowId = createUniqueId()
@@ -113,7 +122,8 @@ export function SettingsRow(
113
122
  <div
114
123
  data-slot="settings-row"
115
124
  class={cn(
116
- 'flex gap-4 p-4',
125
+ 'flex gap-4',
126
+ local.flush ? 'p-0' : 'p-4',
117
127
  local.orientation === 'vertical'
118
128
  ? 'flex-col'
119
129
  : 'flex-col sm:flex-row sm:items-center sm:justify-between',
@@ -8,10 +8,29 @@ export type SecretInputGroupProps = {
8
8
  disabled: boolean
9
9
  onChange: JSX.EventHandler<HTMLInputElement, Event>
10
10
  onClear?: () => void
11
+ /**
12
+ * What the clear action does, for this field: its accessible name and its
13
+ * tooltip while enabled ("Clear stored token", "Clear Google token path").
14
+ * Defaults to "Clear API key".
15
+ */
16
+ clearLabel?: string
17
+ /**
18
+ * Why the clear action is unavailable, shown as its tooltip while the group is
19
+ * `disabled`. Defaults to the policy message, so name the real reason when a
20
+ * field is disabled for another one ("Set a token variable first").
21
+ */
22
+ disabledReason?: string
11
23
  class?: string
12
24
  } & Pick<ComponentProps<'input'>, 'id' | 'aria-label' | 'aria-describedby' | 'aria-invalid'>
13
25
 
14
- /** A password input with an optional keyboard-accessible clear action. */
26
+ /**
27
+ * A password input with an optional keyboard-accessible clear action.
28
+ *
29
+ * The wording is the caller's: inside a `FormField` the input takes the field's
30
+ * label, outside one it needs `aria-label` (the fallback names an API key), and
31
+ * `clearLabel` and `disabledReason` name the clear action for the field it
32
+ * belongs to, so a bearer token or a token path is not announced as an API key.
33
+ */
15
34
  export function SecretInputGroup(props: SecretInputGroupProps) {
16
35
  const field = useContext(FormFieldContext)
17
36
  const [local, rest] = splitProps(props, [
@@ -19,6 +38,8 @@ export function SecretInputGroup(props: SecretInputGroupProps) {
19
38
  'disabled',
20
39
  'onChange',
21
40
  'onClear',
41
+ 'clearLabel',
42
+ 'disabledReason',
22
43
  'aria-label',
23
44
  'class',
24
45
  ])
@@ -46,9 +67,13 @@ export function SecretInputGroup(props: SecretInputGroupProps) {
46
67
  size="xs"
47
68
  class="-me-1 h-6 min-w-0 shrink-0 px-2"
48
69
  disabled={local.disabled}
49
- tooltip={local.disabled ? 'This API key is managed by policy' : 'Clear API key'}
70
+ tooltip={
71
+ local.disabled
72
+ ? (local.disabledReason ?? 'This API key is managed by policy')
73
+ : (local.clearLabel ?? 'Clear API key')
74
+ }
50
75
  tooltipSide="top"
51
- aria-label="Clear API key"
76
+ aria-label={local.clearLabel ?? 'Clear API key'}
52
77
  onClick={() => local.onClear?.()}
53
78
  >
54
79
  Clear
@@ -12,6 +12,14 @@ export type NativeSelectProps = Omit<ComponentProps<'select'>, 'size'> & {
12
12
  * control that this styled single-line select is not.
13
13
  */
14
14
  size?: 'sm' | 'md' | 'lg'
15
+ /**
16
+ * Layout classes for the element that wraps the select and its chevron: a
17
+ * width or `max-w-*`, `flex-1`, `col-span-*`, a margin. `class` reaches the
18
+ * `<select>` itself, which is the wrong box for any of those, because the
19
+ * wrapper is what a flex or grid parent lays out. The select fills the
20
+ * wrapper, so the chevron stays at the control's edge at any width.
21
+ */
22
+ wrapperClass?: string
15
23
  /** Render the choices from data instead of raw option children. */
16
24
  options?: readonly NativeSelectOption[]
17
25
  /** Initialize an uncontrolled selection after its native options mount. */
@@ -69,6 +77,7 @@ export type NativeSelectOption = Readonly<{
69
77
  export function NativeSelect(props: NativeSelectProps) {
70
78
  const [local, rest] = splitProps(props, [
71
79
  'class',
80
+ 'wrapperClass',
72
81
  'size',
73
82
  'children',
74
83
  'defaultValue',
@@ -103,7 +112,10 @@ export function NativeSelect(props: NativeSelectProps) {
103
112
  })
104
113
 
105
114
  return (
106
- <span data-slot="native-select-wrapper" class="relative inline-flex min-w-0">
115
+ <span
116
+ data-slot="native-select-wrapper"
117
+ class={cn('relative inline-flex min-w-0', local.wrapperClass)}
118
+ >
107
119
  <select
108
120
  ref={(element) => {
109
121
  select = element
@@ -116,7 +128,7 @@ export function NativeSelect(props: NativeSelectProps) {
116
128
  aria-invalid={field?.['aria-invalid'] ?? local['aria-invalid']}
117
129
  aria-errormessage={field?.['aria-errormessage'] ?? local['aria-errormessage']}
118
130
  class={cn(
119
- 'min-w-0 appearance-none rounded-md border border-input bg-transparent',
131
+ 'w-full min-w-0 appearance-none rounded-md border border-input bg-transparent',
120
132
  {
121
133
  'h-control-sm px-control-sm pr-8 text-xs': local.size === 'sm',
122
134
  'h-control-md px-control-md pr-9 text-sm':