@conscia-labs/design-system 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,7 @@ The shared React component library for building clear, consistent, and accessibl
6
6
 
7
7
  > **Package:** available publicly as [`@conscia-labs/design-system`](https://www.npmjs.com/package/@conscia-labs/design-system).
8
8
  >
9
- > **v1.0.0:** this README documents the clean-break v1 contract. The repository is now preparing the `1.0.0` release candidate for publication through the Phase 8 workflow.
9
+ > **v1.0.0:** this README documents the clean-break v1 contract. `1.0.0` is published as the stable v1 release.
10
10
  >
11
11
  > **Migration:** upgrading an application to v1? Follow the [Design System v1 Migration Guide](https://github.com/conscia-labs/design-system/blob/main/docs/design-system-v1-migration.md).
12
12
 
@@ -71,6 +71,7 @@ markup and Base UI behavior where interaction complexity requires it:
71
71
  - Avatar
72
72
  - Badge
73
73
  - Button
74
+ - LoadingButton
74
75
  - IconButton
75
76
  - Card
76
77
  - Checkbox
@@ -162,8 +163,7 @@ function App() {
162
163
 
163
164
  ### 1. Install the package
164
165
 
165
- For the v1 release, install the explicit `1.0.0` range after Phase 8 publishes
166
- the package.
166
+ For v1, install the explicit `1.0.0` range:
167
167
 
168
168
  Using pnpm:
169
169
 
@@ -313,6 +313,40 @@ export function CreateConnectionCard() {
313
313
  }
314
314
  ```
315
315
 
316
+ ### Text-in-track switches
317
+
318
+ Use `LabeledSwitch` when the switch state should be visible inside the control.
319
+ It keeps Base UI’s switch behavior and accepts any React node for either label:
320
+
321
+ ```tsx
322
+ import { LabeledSwitch } from "@conscia-labs/design-system";
323
+
324
+ <LabeledSwitch
325
+ aria-label="Deployment status"
326
+ defaultChecked
327
+ onLabel="ENABLED"
328
+ offLabel="DISABLED"
329
+ />
330
+ ```
331
+
332
+ `Switch` also accepts `onLabel` and `offLabel` when a single API needs to support
333
+ both the compact unlabeled style and the text-in-track style. If a label is
334
+ provided without its pair, the missing label defaults to `ON` or `OFF`.
335
+
336
+ ### Loading buttons
337
+
338
+ Use `LoadingButton` for async actions that need stable button layout while a
339
+ mutation is pending. It disables the button, sets `aria-busy`, and replaces the
340
+ normal content with a small spinner and `pendingLabel`:
341
+
342
+ ```tsx
343
+ import { LoadingButton } from "@conscia-labs/design-system";
344
+
345
+ <LoadingButton pending={saveMutation.isPending} pendingLabel="Saving…">
346
+ Save changes
347
+ </LoadingButton>
348
+ ```
349
+
316
350
  ## Building an application shell
317
351
 
318
352
  The shared shell owns presentation and responsive behavior. The host application supplies routes, links, user context, and actions.
@@ -787,7 +821,7 @@ match the version in `package.json` exactly. npm’s trusted-publishing flow
787
821
  provides short-lived CI authentication and provenance for the published
788
822
  package.
789
823
 
790
- Before creating the v1 release tag, complete the release checklist in the
824
+ Before creating a release tag, complete the release checklist in the
791
825
  [migration ledger](./docs/base-ui-migration.md), finalize the
792
826
  [app-owner migration guide](./docs/design-system-v1-migration.md), and run the
793
827
  full local validation suite:
@@ -804,19 +838,20 @@ pnpm build:playground
804
838
  pnpm test:visual
805
839
  ```
806
840
 
807
- Then prepare and tag the release:
841
+ Then prepare and tag a release. Replace `VERSION` with the package version you
842
+ are releasing:
808
843
 
809
844
  ```bash
810
- pnpm version 1.0.0 --no-git-tag-version
845
+ pnpm version VERSION --no-git-tag-version
811
846
  git add package.json README.md docs/base-ui-migration.md docs/design-system-v1-migration.md
812
- git commit -m "Release v1.0.0"
813
- git tag -a v1.0.0 -m "Release v1.0.0"
847
+ git commit -m "Release vVERSION"
848
+ git tag -a vVERSION -m "Release vVERSION"
814
849
  git push origin main
815
- git push origin v1.0.0
850
+ git push origin vVERSION
816
851
  ```
817
852
 
818
853
  Pushing the tag starts the `npm-production` release workflow. The workflow
819
- verifies that `v1.0.0` matches `package.json`, runs the release validation, and
854
+ verifies that the tag matches `package.json`, runs the release validation, and
820
855
  publishes the package to npm without a long-lived npm token. Because this is a
821
856
  scoped public package, the release configuration must retain public access;
822
857
  see npm’s [scoped-package publishing guidance](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages/).
@@ -759,9 +759,64 @@ var SwitchThumb = /* @__PURE__ */ React6.forwardRef(function SwitchThumb2(compon
759
759
  if (true) SwitchThumb.displayName = "SwitchThumb";
760
760
 
761
761
  // src/primitives/switch.tsx
762
- import { jsx as jsx6 } from "react/jsx-runtime";
763
- function Switch({ className, size = "default", ...props }) {
764
- return /* @__PURE__ */ jsx6(index_parts_exports2.Root, { "data-slot": "switch", "data-size": size, className: cn("peer group/switch inline-flex shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:border-focus focus-visible:ring-[3px] focus-visible:ring-focus/50 disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-[1.15rem] data-[size=default]:w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6 data-checked:bg-action data-unchecked:bg-control-border", className), ...props, children: /* @__PURE__ */ jsx6(index_parts_exports2.Thumb, { "data-slot": "switch-thumb", className: "pointer-events-none block rounded-full bg-canvas transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 data-checked:translate-x-[calc(100%-2px)] data-unchecked:translate-x-0" }) });
762
+ import { Fragment, jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
763
+ function Switch({
764
+ className,
765
+ size = "default",
766
+ onLabel,
767
+ offLabel,
768
+ ...props
769
+ }) {
770
+ const labeled = onLabel != null || offLabel != null;
771
+ return /* @__PURE__ */ jsxs2(
772
+ index_parts_exports2.Root,
773
+ {
774
+ "data-slot": "switch",
775
+ "data-size": size,
776
+ "data-labeled": labeled ? "true" : void 0,
777
+ className: cn(
778
+ "peer group/switch inline-flex shrink-0 items-center border border-transparent shadow-xs transition-all outline-none focus-visible:border-focus focus-visible:ring-[3px] focus-visible:ring-focus/50 disabled:cursor-not-allowed disabled:opacity-50",
779
+ labeled ? "relative rounded-[0.625rem] p-0.5 data-[size=default]:h-6 data-[size=default]:w-20 data-[size=default]:[--switch-thumb-travel:3.75rem] data-[size=sm]:h-5 data-[size=sm]:w-[4.5rem] data-[size=sm]:[--switch-thumb-travel:3.5rem] data-checked:bg-action data-unchecked:bg-control-border" : "rounded-full data-[size=default]:h-[1.15rem] data-[size=default]:w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6 data-checked:bg-action data-unchecked:bg-control-border",
780
+ className
781
+ ),
782
+ ...props,
783
+ children: [
784
+ labeled ? /* @__PURE__ */ jsxs2(Fragment, { children: [
785
+ /* @__PURE__ */ jsx6(
786
+ "span",
787
+ {
788
+ "aria-hidden": "true",
789
+ "data-slot": "switch-on-label",
790
+ className: "pointer-events-none absolute inset-y-0.5 left-0 flex w-1/2 items-center justify-center overflow-hidden px-0.5 text-[0.5625rem] font-semibold leading-none tracking-[0.02em] text-action-foreground transition-opacity group-data-checked/switch:opacity-100 group-data-unchecked/switch:opacity-0",
791
+ children: onLabel ?? "ON"
792
+ }
793
+ ),
794
+ /* @__PURE__ */ jsx6(
795
+ "span",
796
+ {
797
+ "aria-hidden": "true",
798
+ "data-slot": "switch-off-label",
799
+ className: "pointer-events-none absolute inset-y-0.5 right-0 flex w-1/2 items-center justify-center overflow-hidden px-0.5 text-[0.5625rem] font-semibold leading-none tracking-[0.02em] text-text-supporting transition-opacity group-data-checked/switch:opacity-0 group-data-unchecked/switch:opacity-100",
800
+ children: offLabel ?? "OFF"
801
+ }
802
+ )
803
+ ] }) : null,
804
+ /* @__PURE__ */ jsx6(
805
+ index_parts_exports2.Thumb,
806
+ {
807
+ "data-slot": "switch-thumb",
808
+ className: cn(
809
+ "pointer-events-none block rounded-full bg-canvas transition-transform",
810
+ labeled ? "absolute top-1/2 left-0.5 z-10 -translate-y-1/2 data-checked:translate-x-[var(--switch-thumb-travel)] data-unchecked:translate-x-0 group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3" : "group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 data-checked:translate-x-[calc(100%-2px)] data-unchecked:translate-x-0"
811
+ )
812
+ }
813
+ )
814
+ ]
815
+ }
816
+ );
817
+ }
818
+ function LabeledSwitch({ onLabel = "ON", offLabel = "OFF", ...props }) {
819
+ return /* @__PURE__ */ jsx6(Switch, { ...props, onLabel, offLabel });
765
820
  }
766
821
 
767
822
  // src/primitives/textarea.tsx
@@ -778,7 +833,7 @@ function Textarea({ className, ...props }) {
778
833
  }
779
834
 
780
835
  // src/primitives/compat.tsx
781
- import { jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
836
+ import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
782
837
  var ConsciaButton = Button;
783
838
  var ConsciaIconButton = IconButton;
784
839
  var ConsciaCard = Card;
@@ -793,7 +848,7 @@ function ConsciaCardHeader({
793
848
  if (!actionRight) {
794
849
  return /* @__PURE__ */ jsx8(CardHeader, { className, ...props, children });
795
850
  }
796
- return /* @__PURE__ */ jsxs2(
851
+ return /* @__PURE__ */ jsxs3(
797
852
  CardHeader,
798
853
  {
799
854
  className: cn(
@@ -897,7 +952,7 @@ function FieldError({
897
952
 
898
953
  // src/primitives/filter-chip.tsx
899
954
  import { XIcon } from "lucide-react";
900
- import { jsx as jsx10, jsxs as jsxs3 } from "react/jsx-runtime";
955
+ import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
901
956
  function FilterChip({
902
957
  className,
903
958
  label,
@@ -907,7 +962,7 @@ function FilterChip({
907
962
  ...props
908
963
  }) {
909
964
  const fallbackRemoveLabel = typeof label === "string" ? `Remove ${label} filter` : "Remove filter";
910
- return /* @__PURE__ */ jsxs3(
965
+ return /* @__PURE__ */ jsxs4(
911
966
  "div",
912
967
  {
913
968
  "data-slot": "filter-chip",
@@ -938,7 +993,7 @@ function FilterChip({
938
993
 
939
994
  // src/primitives/form-select.tsx
940
995
  import * as React7 from "react";
941
- import { jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
996
+ import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
942
997
  var emptyValue = "__conscia_empty_select_value__";
943
998
  function toSelectValue(value) {
944
999
  if (value === void 0) {
@@ -974,7 +1029,7 @@ function FormSelect({
974
1029
  }
975
1030
  onValueChange?.(actualValue);
976
1031
  }
977
- return /* @__PURE__ */ jsx11("div", { className: cn("min-w-0", className), onClick, children: /* @__PURE__ */ jsxs4(
1032
+ return /* @__PURE__ */ jsx11("div", { className: cn("min-w-0", className), onClick, children: /* @__PURE__ */ jsxs5(
978
1033
  Select,
979
1034
  {
980
1035
  name,
@@ -1008,6 +1063,58 @@ function Label({
1008
1063
  return /* @__PURE__ */ jsx12("label", { "data-slot": "label", className: cn("ds-type-label flex items-center gap-2 text-text-primary select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50", className), ...props });
1009
1064
  }
1010
1065
 
1066
+ // src/primitives/spinner.tsx
1067
+ import { LoaderCircle } from "lucide-react";
1068
+ import { jsx as jsx13 } from "react/jsx-runtime";
1069
+ function Spinner({
1070
+ "aria-label": ariaLabel,
1071
+ className,
1072
+ label,
1073
+ size = "default",
1074
+ ...props
1075
+ }) {
1076
+ const accessibleLabel = ariaLabel ?? label;
1077
+ return /* @__PURE__ */ jsx13(
1078
+ "span",
1079
+ {
1080
+ "data-slot": "spinner",
1081
+ "data-size": size,
1082
+ className: cn(
1083
+ "inline-flex shrink-0 animate-spin items-center justify-center text-current motion-reduce:animate-none data-[size=default]:size-4 data-[size=lg]:size-5 data-[size=sm]:size-3.5",
1084
+ className
1085
+ ),
1086
+ role: accessibleLabel ? "status" : void 0,
1087
+ "aria-hidden": accessibleLabel ? void 0 : true,
1088
+ "aria-label": accessibleLabel,
1089
+ ...props,
1090
+ children: /* @__PURE__ */ jsx13(LoaderCircle, { "aria-hidden": "true", className: "size-full" })
1091
+ }
1092
+ );
1093
+ }
1094
+
1095
+ // src/primitives/loading-button.tsx
1096
+ import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs6 } from "react/jsx-runtime";
1097
+ function LoadingButton({
1098
+ children,
1099
+ pending = false,
1100
+ pendingLabel = "Working\u2026",
1101
+ disabled,
1102
+ ...props
1103
+ }) {
1104
+ return /* @__PURE__ */ jsx14(
1105
+ Button,
1106
+ {
1107
+ ...props,
1108
+ disabled: disabled || pending,
1109
+ "aria-busy": pending || void 0,
1110
+ children: pending ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
1111
+ /* @__PURE__ */ jsx14(Spinner, { "aria-hidden": "true", size: "sm" }),
1112
+ pendingLabel
1113
+ ] }) : children
1114
+ }
1115
+ );
1116
+ }
1117
+
1011
1118
  // node_modules/.pnpm/@base-ui+react@1.7.0_@types+react@19.2.17_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/@base-ui/react/popover/index.parts.mjs
1012
1119
  var index_parts_exports3 = {};
1013
1120
  __export(index_parts_exports3, {
@@ -1886,39 +1993,39 @@ function createPopoverHandle() {
1886
1993
  }
1887
1994
 
1888
1995
  // src/primitives/popover.tsx
1889
- import { jsx as jsx13 } from "react/jsx-runtime";
1996
+ import { jsx as jsx15 } from "react/jsx-runtime";
1890
1997
  function Popover({ ...props }) {
1891
- return /* @__PURE__ */ jsx13(index_parts_exports3.Root, { "data-slot": "popover", ...props });
1998
+ return /* @__PURE__ */ jsx15(index_parts_exports3.Root, { "data-slot": "popover", ...props });
1892
1999
  }
1893
2000
  function PopoverTrigger3({ ...props }) {
1894
- return /* @__PURE__ */ jsx13(index_parts_exports3.Trigger, { "data-slot": "popover-trigger", ...props });
2001
+ return /* @__PURE__ */ jsx15(index_parts_exports3.Trigger, { "data-slot": "popover-trigger", ...props });
1895
2002
  }
1896
2003
  function PopoverPortal3({ ...props }) {
1897
- return /* @__PURE__ */ jsx13(index_parts_exports3.Portal, { "data-slot": "popover-portal", ...props });
2004
+ return /* @__PURE__ */ jsx15(index_parts_exports3.Portal, { "data-slot": "popover-portal", ...props });
1898
2005
  }
1899
2006
  function PopoverPositioner3({ className, ...props }) {
1900
- return /* @__PURE__ */ jsx13(index_parts_exports3.Positioner, { "data-slot": "popover-positioner", className, ...props });
2007
+ return /* @__PURE__ */ jsx15(index_parts_exports3.Positioner, { "data-slot": "popover-positioner", className, ...props });
1901
2008
  }
1902
2009
  function PopoverContent({ className, children, ...props }) {
1903
- return /* @__PURE__ */ jsx13(PopoverPortal3, { children: /* @__PURE__ */ jsx13(PopoverPositioner3, { children: /* @__PURE__ */ jsx13(index_parts_exports3.Popup, { "data-slot": "popover-content", className: cn("z-50 w-72 rounded-md border bg-surface-floating p-4 text-text-primary shadow-[var(--ds-shadow-floating)] outline-none data-open:animate-in data-closed:animate-out", className), ...props, children }) }) });
2010
+ return /* @__PURE__ */ jsx15(PopoverPortal3, { children: /* @__PURE__ */ jsx15(PopoverPositioner3, { children: /* @__PURE__ */ jsx15(index_parts_exports3.Popup, { "data-slot": "popover-content", className: cn("z-50 w-72 rounded-md border bg-surface-floating p-4 text-text-primary shadow-[var(--ds-shadow-floating)] outline-none data-open:animate-in data-closed:animate-out", className), ...props, children }) }) });
1904
2011
  }
1905
2012
  function PopoverArrow3({ ...props }) {
1906
- return /* @__PURE__ */ jsx13(index_parts_exports3.Arrow, { "data-slot": "popover-arrow", ...props });
2013
+ return /* @__PURE__ */ jsx15(index_parts_exports3.Arrow, { "data-slot": "popover-arrow", ...props });
1907
2014
  }
1908
2015
  function PopoverTitle3({ className, ...props }) {
1909
- return /* @__PURE__ */ jsx13(index_parts_exports3.Title, { "data-slot": "popover-title", className: cn("font-semibold", className), ...props });
2016
+ return /* @__PURE__ */ jsx15(index_parts_exports3.Title, { "data-slot": "popover-title", className: cn("font-semibold", className), ...props });
1910
2017
  }
1911
2018
  function PopoverDescription3({ className, ...props }) {
1912
- return /* @__PURE__ */ jsx13(index_parts_exports3.Description, { "data-slot": "popover-description", className: cn("text-sm text-text-supporting", className), ...props });
2019
+ return /* @__PURE__ */ jsx15(index_parts_exports3.Description, { "data-slot": "popover-description", className: cn("text-sm text-text-supporting", className), ...props });
1913
2020
  }
1914
2021
  function PopoverClose3({ ...props }) {
1915
- return /* @__PURE__ */ jsx13(index_parts_exports3.Close, { "data-slot": "popover-close", ...props });
2022
+ return /* @__PURE__ */ jsx15(index_parts_exports3.Close, { "data-slot": "popover-close", ...props });
1916
2023
  }
1917
2024
 
1918
2025
  // src/primitives/searchable-select.tsx
1919
2026
  import * as React24 from "react";
1920
2027
  import { Check, ChevronsUpDown, X } from "lucide-react";
1921
- import { jsx as jsx14, jsxs as jsxs5 } from "react/jsx-runtime";
2028
+ import { jsx as jsx16, jsxs as jsxs7 } from "react/jsx-runtime";
1922
2029
  function SearchableSelect({ id, name, value = "", options, onValueChange, placeholder = "Select an option", searchPlaceholder = "Search...", emptyMessage = "No matching options.", disabled = false, clearable = false, clearLabel = "Clear selection", optionsLabel, className, "aria-label": ariaLabel }) {
1923
2030
  const selected = options.find((option) => option.value === value) ?? null;
1924
2031
  const items = React24.useMemo(() => options.map((option) => option.value), [options]);
@@ -1928,7 +2035,7 @@ function SearchableSelect({ id, name, value = "", options, onValueChange, placeh
1928
2035
  React24.useLayoutEffect(() => {
1929
2036
  if (!open && inputRef.current) inputRef.current.value = selected?.label ?? "";
1930
2037
  }, [open, selected?.label]);
1931
- return /* @__PURE__ */ jsx14("div", { "data-slot": "searchable-select", className: cn("relative min-w-0", className), children: /* @__PURE__ */ jsxs5(index_parts_exports.Root, { items, autoHighlight: true, open, onOpenChange: (nextOpen) => {
2038
+ return /* @__PURE__ */ jsx16("div", { "data-slot": "searchable-select", className: cn("relative min-w-0", className), children: /* @__PURE__ */ jsxs7(index_parts_exports.Root, { items, autoHighlight: true, open, onOpenChange: (nextOpen) => {
1932
2039
  setOpen(nextOpen);
1933
2040
  if (!nextOpen) setInputValue(selected?.label ?? "");
1934
2041
  }, value, inputValue: open ? inputValue : selected?.label ?? "", onInputValueChange: setInputValue, onValueChange: (next) => {
@@ -1936,8 +2043,8 @@ function SearchableSelect({ id, name, value = "", options, onValueChange, placeh
1936
2043
  setInputValue(nextOption?.label ?? "");
1937
2044
  onValueChange(next ?? "");
1938
2045
  }, itemToStringLabel: (item) => options.find((option) => option.value === item)?.label ?? "", itemToStringValue: (item) => item ?? "", name, disabled, children: [
1939
- /* @__PURE__ */ jsxs5(index_parts_exports.InputGroup, { className: "relative flex items-center", children: [
1940
- /* @__PURE__ */ jsx14(index_parts_exports.Input, { ref: inputRef, id, "aria-label": ariaLabel, placeholder: searchPlaceholder, onKeyDown: (event) => {
2046
+ /* @__PURE__ */ jsxs7(index_parts_exports.InputGroup, { className: "relative flex items-center", children: [
2047
+ /* @__PURE__ */ jsx16(index_parts_exports.Input, { ref: inputRef, id, "aria-label": ariaLabel, placeholder: searchPlaceholder, onKeyDown: (event) => {
1941
2048
  if (event.key === "Enter" && open) {
1942
2049
  const matches = options.filter((option) => !option.disabled && option.label.toLowerCase().includes(inputValue.toLowerCase()));
1943
2050
  const match = matches.at(-1);
@@ -1948,19 +2055,19 @@ function SearchableSelect({ id, name, value = "", options, onValueChange, placeh
1948
2055
  }
1949
2056
  }
1950
2057
  }, className: "ds-type-control h-[var(--ds-field-control-height)] w-full rounded-[var(--ds-field-control-radius)] border border-control-border bg-surface-control px-3 pr-16 outline-none focus-visible:border-focus focus-visible:ring-[3px] focus-visible:ring-focus/50 disabled:cursor-not-allowed disabled:opacity-50" }),
1951
- clearable && selected ? /* @__PURE__ */ jsx14(IconButton, { type: "button", size: "sm", variant: "ghost", "aria-label": clearLabel, onClick: () => onValueChange(""), className: "absolute right-7", children: /* @__PURE__ */ jsx14(X, { className: "size-4" }) }) : null,
1952
- /* @__PURE__ */ jsx14(index_parts_exports.Trigger, { "aria-label": ariaLabel ?? placeholder, className: "absolute right-1 inline-flex size-8 items-center justify-center rounded-sm text-text-supporting outline-none focus-visible:ring-2 focus-visible:ring-focus", children: /* @__PURE__ */ jsx14(ChevronsUpDown, { className: "size-4" }) })
2058
+ clearable && selected ? /* @__PURE__ */ jsx16(IconButton, { type: "button", size: "sm", variant: "ghost", "aria-label": clearLabel, onClick: () => onValueChange(""), className: "absolute right-7", children: /* @__PURE__ */ jsx16(X, { className: "size-4" }) }) : null,
2059
+ /* @__PURE__ */ jsx16(index_parts_exports.Trigger, { "aria-label": ariaLabel ?? placeholder, className: "absolute right-1 inline-flex size-8 items-center justify-center rounded-sm text-text-supporting outline-none focus-visible:ring-2 focus-visible:ring-focus", children: /* @__PURE__ */ jsx16(ChevronsUpDown, { className: "size-4" }) })
1953
2060
  ] }),
1954
- /* @__PURE__ */ jsx14(index_parts_exports.Portal, { children: /* @__PURE__ */ jsx14(index_parts_exports.Positioner, { className: "z-50 w-[var(--anchor-width)]", children: /* @__PURE__ */ jsxs5(index_parts_exports.Popup, { className: "mt-1 max-h-72 overflow-auto rounded-md border bg-surface-floating p-1 text-text-primary shadow-[var(--ds-shadow-floating)] outline-none", children: [
1955
- /* @__PURE__ */ jsx14(index_parts_exports.Empty, { className: "px-2 py-3 text-sm text-text-supporting", children: emptyMessage }),
1956
- /* @__PURE__ */ jsx14(index_parts_exports.List, { "aria-label": optionsLabel ?? "Options", children: (optionValue) => {
2061
+ /* @__PURE__ */ jsx16(index_parts_exports.Portal, { children: /* @__PURE__ */ jsx16(index_parts_exports.Positioner, { className: "z-50 w-[var(--anchor-width)]", children: /* @__PURE__ */ jsxs7(index_parts_exports.Popup, { className: "mt-1 max-h-72 overflow-auto rounded-md border bg-surface-floating p-1 text-text-primary shadow-[var(--ds-shadow-floating)] outline-none", children: [
2062
+ /* @__PURE__ */ jsx16(index_parts_exports.Empty, { className: "px-2 py-3 text-sm text-text-supporting", children: emptyMessage }),
2063
+ /* @__PURE__ */ jsx16(index_parts_exports.List, { "aria-label": optionsLabel ?? "Options", children: (optionValue) => {
1957
2064
  const option = options.find((candidate) => candidate.value === optionValue);
1958
2065
  if (!option) return null;
1959
- return /* @__PURE__ */ jsxs5(index_parts_exports.Item, { value: option.value, disabled: option.disabled, className: "ds-type-menu-item data-highlighted:bg-surface-muted relative flex cursor-default items-start gap-2 rounded-sm px-2 py-2 pr-8 outline-none data-disabled:pointer-events-none data-disabled:opacity-50", children: [
1960
- /* @__PURE__ */ jsx14("span", { className: "mt-0.5 flex size-4 shrink-0 items-center justify-center", children: /* @__PURE__ */ jsx14(index_parts_exports.ItemIndicator, { children: /* @__PURE__ */ jsx14(Check, { className: "size-4" }) }) }),
1961
- /* @__PURE__ */ jsxs5("span", { className: "min-w-0", children: [
1962
- /* @__PURE__ */ jsx14("span", { className: "block truncate text-sm", children: option.label }),
1963
- option.description ? /* @__PURE__ */ jsx14("span", { className: "block truncate text-xs text-text-supporting", children: option.description }) : null
2066
+ return /* @__PURE__ */ jsxs7(index_parts_exports.Item, { value: option.value, disabled: option.disabled, className: "ds-type-menu-item data-highlighted:bg-surface-muted relative flex cursor-default items-start gap-2 rounded-sm px-2 py-2 pr-8 outline-none data-disabled:pointer-events-none data-disabled:opacity-50", children: [
2067
+ /* @__PURE__ */ jsx16("span", { className: "mt-0.5 flex size-4 shrink-0 items-center justify-center", children: /* @__PURE__ */ jsx16(index_parts_exports.ItemIndicator, { children: /* @__PURE__ */ jsx16(Check, { className: "size-4" }) }) }),
2068
+ /* @__PURE__ */ jsxs7("span", { className: "min-w-0", children: [
2069
+ /* @__PURE__ */ jsx16("span", { className: "block truncate text-sm", children: option.label }),
2070
+ option.description ? /* @__PURE__ */ jsx16("span", { className: "block truncate text-xs text-text-supporting", children: option.description }) : null
1964
2071
  ] })
1965
2072
  ] }, option.value);
1966
2073
  } })
@@ -1968,35 +2075,6 @@ function SearchableSelect({ id, name, value = "", options, onValueChange, placeh
1968
2075
  ] }) });
1969
2076
  }
1970
2077
 
1971
- // src/primitives/spinner.tsx
1972
- import { LoaderCircle } from "lucide-react";
1973
- import { jsx as jsx15 } from "react/jsx-runtime";
1974
- function Spinner({
1975
- "aria-label": ariaLabel,
1976
- className,
1977
- label,
1978
- size = "default",
1979
- ...props
1980
- }) {
1981
- const accessibleLabel = ariaLabel ?? label;
1982
- return /* @__PURE__ */ jsx15(
1983
- "span",
1984
- {
1985
- "data-slot": "spinner",
1986
- "data-size": size,
1987
- className: cn(
1988
- "inline-flex shrink-0 animate-spin items-center justify-center text-current motion-reduce:animate-none data-[size=default]:size-4 data-[size=lg]:size-5 data-[size=sm]:size-3.5",
1989
- className
1990
- ),
1991
- role: accessibleLabel ? "status" : void 0,
1992
- "aria-hidden": accessibleLabel ? void 0 : true,
1993
- "aria-label": accessibleLabel,
1994
- ...props,
1995
- children: /* @__PURE__ */ jsx15(LoaderCircle, { "aria-hidden": "true", className: "size-full" })
1996
- }
1997
- );
1998
- }
1999
-
2000
2078
  // src/primitives/toast.tsx
2001
2079
  import * as React41 from "react";
2002
2080
 
@@ -3553,7 +3631,7 @@ function createToastManager() {
3553
3631
 
3554
3632
  // src/primitives/toast.tsx
3555
3633
  import { XIcon as XIcon2 } from "lucide-react";
3556
- import { jsx as jsx16, jsxs as jsxs6 } from "react/jsx-runtime";
3634
+ import { jsx as jsx17, jsxs as jsxs8 } from "react/jsx-runtime";
3557
3635
  function toBaseToastOptions(options) {
3558
3636
  const { action, variant, ...rest } = options;
3559
3637
  return {
@@ -3571,7 +3649,7 @@ function toBasePromiseOption(option) {
3571
3649
  return typeof option === "string" ? option : toBaseToastOptions(option);
3572
3650
  }
3573
3651
  function ToastProvider3({ children, limit = 3, timeout = 5e3 }) {
3574
- return /* @__PURE__ */ jsx16(index_parts_exports4.Provider, { limit, timeout, children });
3652
+ return /* @__PURE__ */ jsx17(index_parts_exports4.Provider, { limit, timeout, children });
3575
3653
  }
3576
3654
  function useToast() {
3577
3655
  const manager = index_parts_exports4.useToastManager();
@@ -3609,7 +3687,7 @@ function useToast() {
3609
3687
  }
3610
3688
  function ToastViewport3({ className, placement = "bottom-end", ...props }) {
3611
3689
  const { toasts } = index_parts_exports4.useToastManager();
3612
- return /* @__PURE__ */ jsx16(index_parts_exports4.Portal, { children: /* @__PURE__ */ jsx16(
3690
+ return /* @__PURE__ */ jsx17(index_parts_exports4.Portal, { children: /* @__PURE__ */ jsx17(
3613
3691
  index_parts_exports4.Viewport,
3614
3692
  {
3615
3693
  "data-slot": "toast-viewport",
@@ -3622,7 +3700,7 @@ function ToastViewport3({ className, placement = "bottom-end", ...props }) {
3622
3700
  className
3623
3701
  ),
3624
3702
  ...props,
3625
- children: toasts.map((toast) => /* @__PURE__ */ jsx16(Toast, { toast }, toast.id))
3703
+ children: toasts.map((toast) => /* @__PURE__ */ jsx17(Toast, { toast }, toast.id))
3626
3704
  }
3627
3705
  ) });
3628
3706
  }
@@ -3633,7 +3711,7 @@ function Toast({
3633
3711
  ...props
3634
3712
  }) {
3635
3713
  const variant = toast.data?.variant ?? toast.type ?? "default";
3636
- return /* @__PURE__ */ jsx16(
3714
+ return /* @__PURE__ */ jsx17(
3637
3715
  index_parts_exports4.Root,
3638
3716
  {
3639
3717
  toast,
@@ -3644,40 +3722,40 @@ function Toast({
3644
3722
  className
3645
3723
  ),
3646
3724
  ...props,
3647
- children: children ?? /* @__PURE__ */ jsxs6(ToastContent3, { children: [
3648
- /* @__PURE__ */ jsxs6("div", { className: "min-w-0", children: [
3649
- /* @__PURE__ */ jsx16(ToastTitle3, {}),
3650
- /* @__PURE__ */ jsx16(ToastDescription3, {})
3725
+ children: children ?? /* @__PURE__ */ jsxs8(ToastContent3, { children: [
3726
+ /* @__PURE__ */ jsxs8("div", { className: "min-w-0", children: [
3727
+ /* @__PURE__ */ jsx17(ToastTitle3, {}),
3728
+ /* @__PURE__ */ jsx17(ToastDescription3, {})
3651
3729
  ] }),
3652
- /* @__PURE__ */ jsxs6("div", { className: "flex items-start gap-1", children: [
3653
- /* @__PURE__ */ jsx16(ToastAction3, {}),
3654
- /* @__PURE__ */ jsx16(ToastClose3, {})
3730
+ /* @__PURE__ */ jsxs8("div", { className: "flex items-start gap-1", children: [
3731
+ /* @__PURE__ */ jsx17(ToastAction3, {}),
3732
+ /* @__PURE__ */ jsx17(ToastClose3, {})
3655
3733
  ] })
3656
3734
  ] })
3657
3735
  }
3658
3736
  );
3659
3737
  }
3660
3738
  function ToastContent3({ className, ...props }) {
3661
- return /* @__PURE__ */ jsx16(index_parts_exports4.Content, { "data-slot": "toast-content", className: cn("contents", className), ...props });
3739
+ return /* @__PURE__ */ jsx17(index_parts_exports4.Content, { "data-slot": "toast-content", className: cn("contents", className), ...props });
3662
3740
  }
3663
3741
  function ToastTitle3({ className, ...props }) {
3664
- return /* @__PURE__ */ jsx16(index_parts_exports4.Title, { "data-slot": "toast-title", className: cn("font-semibold", className), ...props });
3742
+ return /* @__PURE__ */ jsx17(index_parts_exports4.Title, { "data-slot": "toast-title", className: cn("font-semibold", className), ...props });
3665
3743
  }
3666
3744
  function ToastDescription3({ className, ...props }) {
3667
- return /* @__PURE__ */ jsx16(index_parts_exports4.Description, { "data-slot": "toast-description", className: cn("mt-1 text-text-supporting", className), ...props });
3745
+ return /* @__PURE__ */ jsx17(index_parts_exports4.Description, { "data-slot": "toast-description", className: cn("mt-1 text-text-supporting", className), ...props });
3668
3746
  }
3669
3747
  function ToastAction3({ className, ...props }) {
3670
- return /* @__PURE__ */ jsx16(index_parts_exports4.Action, { "data-slot": "toast-action", className: cn("rounded px-2 py-1 text-xs font-medium text-text-link outline-none hover:bg-surface-muted focus-visible:ring-2 focus-visible:ring-focus", className), ...props });
3748
+ return /* @__PURE__ */ jsx17(index_parts_exports4.Action, { "data-slot": "toast-action", className: cn("rounded px-2 py-1 text-xs font-medium text-text-link outline-none hover:bg-surface-muted focus-visible:ring-2 focus-visible:ring-focus", className), ...props });
3671
3749
  }
3672
3750
  function ToastClose3({ "aria-label": ariaLabel = "Dismiss notification", children, className, ...props }) {
3673
- return /* @__PURE__ */ jsx16(
3751
+ return /* @__PURE__ */ jsx17(
3674
3752
  index_parts_exports4.Close,
3675
3753
  {
3676
3754
  "data-slot": "toast-close",
3677
3755
  "aria-label": ariaLabel,
3678
3756
  className: cn("rounded p-1 text-text-supporting outline-none hover:bg-surface-muted hover:text-text-primary focus-visible:ring-2 focus-visible:ring-focus", className),
3679
3757
  ...props,
3680
- children: children ?? /* @__PURE__ */ jsx16(XIcon2, { "aria-hidden": "true", className: "size-4" })
3758
+ children: children ?? /* @__PURE__ */ jsx17(XIcon2, { "aria-hidden": "true", className: "size-4" })
3681
3759
  }
3682
3760
  );
3683
3761
  }
@@ -3696,6 +3774,7 @@ export {
3696
3774
  BrandIcon,
3697
3775
  BrandWordmark,
3698
3776
  Switch,
3777
+ LabeledSwitch,
3699
3778
  Textarea,
3700
3779
  ConsciaButton,
3701
3780
  ConsciaIconButton,
@@ -3730,6 +3809,8 @@ export {
3730
3809
  FormSelect,
3731
3810
  ConsciaFormSelect,
3732
3811
  Label,
3812
+ Spinner,
3813
+ LoadingButton,
3733
3814
  Popover,
3734
3815
  PopoverTrigger3 as PopoverTrigger,
3735
3816
  PopoverPortal3 as PopoverPortal,
@@ -3740,7 +3821,6 @@ export {
3740
3821
  PopoverDescription3 as PopoverDescription,
3741
3822
  PopoverClose3 as PopoverClose,
3742
3823
  SearchableSelect,
3743
- Spinner,
3744
3824
  ToastProvider3 as ToastProvider,
3745
3825
  useToast,
3746
3826
  ToastViewport3 as ToastViewport,
@@ -3751,4 +3831,4 @@ export {
3751
3831
  ToastAction3 as ToastAction,
3752
3832
  ToastClose3 as ToastClose
3753
3833
  };
3754
- //# sourceMappingURL=chunk-3UTRMJLB.js.map
3834
+ //# sourceMappingURL=chunk-XQSBTQRR.js.map