@dev-dga/react 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,53 @@
1
1
  # @dev-dga/react
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Breaking Changes
6
+
7
+ - **`DgaContextValue.rootRef` (RefObject) → `rootEl` (`HTMLElement | null`).** The `useDga()` return shape changed: `rootRef.current` is now `rootEl` directly. Callers reading `useDga().rootRef` will see a TypeScript error. Reason: the previous `useRef`-based exposure had a race — `useRef` doesn't trigger re-renders when the element mounts, so portal-rendering components reading `rootRef.current` raced to `<body>` on the first commit. The new state-backed callback ref triggers a re-render once the element is available. Pre-1.0 release; almost certainly no external consumers existed.
8
+
9
+ ### Minor Changes
10
+
11
+ - **Theming API — `theme` prop on `<DgaProvider>`.** Accepts a `DgaTheme` (from `@dev-dga/tokens`'s new `buildTheme()`); memoizes the resulting CSS vars and merges them into the root element's `style`. Three input shapes supported: palette name (`'lavender'`), single CSS color (hover/active derived via `color-mix()` — requires Safari 16.4+), or explicit `{ base, hover, active, foreground? }` triplet. Consumer's manual `style` override still wins (escape hatch preserved):
12
+
13
+ ```tsx
14
+ // palette name
15
+ <DgaProvider theme={{ primary: 'lavender' }}>{…}</DgaProvider>
16
+
17
+ // single hex (derives hover + active)
18
+ <DgaProvider theme={{ primary: '#7C3AED' }}>{…}</DgaProvider>
19
+
20
+ // explicit triplet
21
+ <DgaProvider theme={{ primary: { base: '#A', hover: '#B', active: '#C', foreground: '#D' } }}>
22
+ {…}
23
+ </DgaProvider>
24
+ ```
25
+
26
+ Theme types re-exported from `@dev-dga/react`: `DgaTheme`, `PaletteName`, `ThemeColor`.
27
+
28
+ - **Public field primitive — `useFieldA11y` + `FieldMessage`.** Promoted from `internal/` to the public surface. The same primitive every shipped form control uses; lets consumers build custom fields (date picker, file upload, combobox) with the library's id generation, helper/error precedence, ARIA wiring, and dev-time accessible-name warning. Exports: `useFieldA11y`, `FieldMessage`, `UseFieldA11yOptions`, `FieldA11y`, `FieldMessageProps`.
29
+
30
+ - **Three new components on the field primitive.** Each ships with tests, an autodocs story (EN + RTL/AR copy), and per-component CSS in `@dev-dga/css`:
31
+ - **`RadioGroup` + `Radio`** — Radix RadioGroup. Compound shape with `label`/`helperText`/`error`/`errorMessage`/`size`/`orientation` on the group; size + error cascade from group to items via CSS. Group label associated via `aria-labelledby`.
32
+ - **`Switch`** — single boolean. Same `useFieldA11y` wiring as Checkbox.
33
+ - **`Select` + `SelectItem`** — Radix Select. Closed shape — Trigger + Portal + Content + Viewport rendered internally; consumers compose with `<SelectItem>` children. Trigger height matches `Input` (32/36) so triggers + text inputs align in form rows. Content panel width matches trigger via `--radix-select-trigger-width`.
34
+
35
+ - **`asChild` composition on Button (Radix Slot).** New `asChild?: boolean` prop. When true, renders via `SlotPrimitive.Slot` with `Slottable` wrapping `children` so icons land as siblings inside the consumer's element. Unblocks router-link composition without invalid `<button><a>` nesting:
36
+
37
+ ```tsx
38
+ <Button asChild>
39
+ <Link href="/about">About</Link>
40
+ </Button>
41
+ ```
42
+
43
+ In Slot mode the `type` and native `disabled` attributes are dropped (no effect on non-form-controls); the disabled state still applies via `aria-disabled` + `tabIndex={-1}`. `@dev-dga/css` matches `[aria-disabled='true']` alongside `:disabled` so the visual disabled state works on any rendered element. Convention set for future visual components (Card, Badge, Avatar, Alert, …); form-control components that already wrap Radix primitives skip `asChild` — the primitive handles Slot internally.
44
+
45
+ - **Select dropdown anchored to the DgaProvider root for theme + dark-mode inheritance.** Radix Portal defaults to `<body>`, which lives above the `DgaProvider` root in the DOM tree — `data-theme`, custom theme vars set via the new `theme` prop, and `.ddga-theme-*` classes set on the root never reached the dropdown. Select now re-anchors via `<Select.Portal container={ctx.rootEl}>`. Same pattern documented in `DgaContext.ts` for future portal-using components (Tooltip / Modal / Toast / Popover / DropdownMenu).
46
+
47
+ ### Patch Changes
48
+
49
+ - **`errorMessage` alone now sets `aria-invalid` and renders the message.** Previously, `useFieldA11y` (and the form components that use it) gated rendering on `error: true`, so passing `errorMessage="…"` without `error` was a silent no-op — a footgun. `hasError` is now derived as `error || isPresent(errorMessage)`. The `error` boolean is still useful for the message-less invalid case (form-level validation marking the field invalid without per-field copy). Backward-compatible for callers passing both `error` + `errorMessage`.
50
+
3
51
  ## 0.2.0
4
52
 
5
53
  ### Minor Changes
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  React 19 components for the **DGA (Digital Government Authority)** design system , Saudi Arabia's national reference for government digital platforms.
4
4
 
5
- Accessible (WCAG 2.2 AA), RTL-native, dark-mode ready. **Ships no CSS** , pair with `@dev-dga/css`.
5
+ Accessible (WCAG 2.2 AA), RTL-native, dark-mode ready, themable. **Ships no CSS** , pair with `@dev-dga/css`.
6
6
 
7
7
  ## Install
8
8
 
@@ -16,29 +16,77 @@ Requires `react@^19` and `react-dom@^19` (peer dependencies).
16
16
 
17
17
  ```tsx
18
18
  import '@dev-dga/css'; // required , components ship no styles
19
- import { DgaProvider, Button, Input, Textarea, Checkbox } from '@dev-dga/react';
19
+ import { DgaProvider, Button, Input, Select, SelectItem, Switch } from '@dev-dga/react';
20
20
 
21
21
  export default function App() {
22
22
  return (
23
23
  <DgaProvider>
24
- <Button>Hello</Button>
25
24
  <Input label="Email" type="email" helperText="We'll never share it." />
25
+ <Select label="Country" placeholder="Choose…">
26
+ <SelectItem value="sa">Saudi Arabia</SelectItem>
27
+ <SelectItem value="ae">United Arab Emirates</SelectItem>
28
+ </Select>
29
+ <Switch label="Subscribe to the newsletter" />
30
+ <Button>Submit</Button>
26
31
  </DgaProvider>
27
32
  );
28
33
  }
29
34
  ```
30
35
 
31
- For RTL or dark mode, configure the provider:
36
+ For RTL, dark mode, or a brand color, configure the provider:
32
37
 
33
38
  ```tsx
34
- <DgaProvider dir="rtl" mode="dark">
39
+ <DgaProvider dir="rtl" mode="dark" theme={{ primary: 'lavender' }}>
35
40
  {/* … */}
36
41
  </DgaProvider>
37
42
  ```
38
43
 
44
+ ## Compose with router links (`asChild`)
45
+
46
+ Visual components support `asChild` so you can render them as your router's `Link` (Next.js, React Router, TanStack Router, …) without invalid `<button><a>` nesting:
47
+
48
+ ```tsx
49
+ <Button asChild>
50
+ <Link href="/about">About</Link>
51
+ </Button>
52
+ ```
53
+
54
+ ## Build your own field
55
+
56
+ `useFieldA11y` + `FieldMessage` are public , the same primitives every shipped form control uses. Build a custom field (date picker, file upload, combobox) with the library's a11y wiring, helper/error precedence, and dev-time warnings:
57
+
58
+ ```tsx
59
+ import { useFieldA11y, FieldMessage } from '@dev-dga/react';
60
+
61
+ function MyField({ label, helperText, error, errorMessage, ...rest }) {
62
+ const { fieldId, errorId, helperId, hasError, hasErrorMessage, hasHelper, controlProps } =
63
+ useFieldA11y({ name: 'MyField', label, error, errorMessage, helperText });
64
+ return (
65
+ <div className="ddga-field">
66
+ {label && <label htmlFor={fieldId}>{label}</label>}
67
+ <input {...controlProps} {...rest} />
68
+ {hasErrorMessage && (
69
+ <FieldMessage id={errorId} variant="error">
70
+ {errorMessage}
71
+ </FieldMessage>
72
+ )}
73
+ {hasHelper && (
74
+ <FieldMessage id={helperId} variant="helper">
75
+ {helperText}
76
+ </FieldMessage>
77
+ )}
78
+ </div>
79
+ );
80
+ }
81
+ ```
82
+
39
83
  ## Components
40
84
 
41
- `Button` · `Input` · `Textarea` · `Checkbox` · `DgaProvider` , plus `cn` (clsx), `useDga`, and `useDir`.
85
+ **Form:** `Button` · `Input` · `Textarea` · `Checkbox` · `RadioGroup` + `Radio` · `Switch` · `Select` + `SelectItem`
86
+
87
+ **Primitives:** `DgaProvider` · `useDga` · `useDir` · `useFieldA11y` · `FieldMessage` · `cn`
88
+
89
+ **Theming types:** `DgaTheme` · `PaletteName` · `ThemeColor`
42
90
 
43
91
  ## Docs
44
92
 
package/dist/index.cjs CHANGED
@@ -23,15 +23,26 @@ __export(src_exports, {
23
23
  Button: () => Button,
24
24
  Checkbox: () => Checkbox,
25
25
  DgaProvider: () => DgaProvider,
26
+ FieldMessage: () => FieldMessage,
26
27
  Input: () => Input,
28
+ Radio: () => Radio,
29
+ RadioGroup: () => RadioGroup,
30
+ Select: () => Select,
31
+ SelectItem: () => SelectItem,
32
+ Switch: () => Switch,
27
33
  Textarea: () => Textarea,
28
34
  buttonVariants: () => buttonVariants,
29
35
  checkboxVariants: () => checkboxVariants,
30
36
  cn: () => cn,
31
37
  inputVariants: () => inputVariants,
38
+ radioGroupVariants: () => radioGroupVariants,
39
+ radioVariants: () => radioVariants,
40
+ selectTriggerVariants: () => selectTriggerVariants,
41
+ switchVariants: () => switchVariants,
32
42
  textareaVariants: () => textareaVariants,
33
43
  useDga: () => useDga,
34
- useDir: () => useDir
44
+ useDir: () => useDir,
45
+ useFieldA11y: () => useFieldA11y
35
46
  });
36
47
  module.exports = __toCommonJS(src_exports);
37
48
 
@@ -102,6 +113,24 @@ function Minus(props) {
102
113
  }
103
114
  );
104
115
  }
116
+ function ChevronDown(props) {
117
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
118
+ "svg",
119
+ {
120
+ xmlns: "http://www.w3.org/2000/svg",
121
+ width: "1em",
122
+ height: "1em",
123
+ viewBox: "0 0 24 24",
124
+ fill: "none",
125
+ stroke: "currentColor",
126
+ strokeWidth: "2",
127
+ strokeLinecap: "round",
128
+ strokeLinejoin: "round",
129
+ ...props,
130
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m6 9 6 6 6-6" })
131
+ }
132
+ );
133
+ }
105
134
 
106
135
  // src/components/Button/Button.tsx
107
136
  var import_jsx_runtime2 = require("react/jsx-runtime");
@@ -180,7 +209,7 @@ function Button({
180
209
  // src/components/Input/Input.tsx
181
210
  var import_class_variance_authority2 = require("class-variance-authority");
182
211
 
183
- // src/internal/field/use-field-a11y.ts
212
+ // src/field/use-field-a11y.ts
184
213
  var import_react2 = require("react");
185
214
  function isPresent(value) {
186
215
  return value != null && value !== "";
@@ -190,7 +219,7 @@ function useFieldA11y(options) {
190
219
  const fieldId = options.id ?? generatedId;
191
220
  const helperId = `${fieldId}-helper`;
192
221
  const errorId = `${fieldId}-error`;
193
- const hasError = !!options.error;
222
+ const hasError = !!options.error || isPresent(options.errorMessage);
194
223
  const hasErrorMessage = hasError && isPresent(options.errorMessage);
195
224
  const hasHelper = !hasErrorMessage && isPresent(options.helperText);
196
225
  if (process.env.NODE_ENV === "development" && !options.label) {
@@ -215,7 +244,7 @@ function useFieldA11y(options) {
215
244
  };
216
245
  }
217
246
 
218
- // src/internal/field/FieldMessage.tsx
247
+ // src/field/FieldMessage.tsx
219
248
  var import_jsx_runtime3 = require("react/jsx-runtime");
220
249
  function FieldMessage({ id, variant, children }) {
221
250
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
@@ -456,52 +485,306 @@ function Checkbox({
456
485
  ] });
457
486
  }
458
487
 
459
- // src/providers/DgaProvider.tsx
460
- var import_react4 = require("react");
488
+ // src/components/Radio/Radio.tsx
489
+ var import_react3 = require("react");
461
490
  var import_radix_ui2 = require("radix-ui");
491
+ var import_class_variance_authority5 = require("class-variance-authority");
492
+ var import_jsx_runtime7 = require("react/jsx-runtime");
493
+ var radioGroupVariants = (0, import_class_variance_authority5.cva)("ddga-radio-group", {
494
+ variants: {
495
+ size: {
496
+ sm: "ddga-radio-group--sm",
497
+ md: "ddga-radio-group--md"
498
+ },
499
+ orientation: {
500
+ vertical: "ddga-radio-group--vertical",
501
+ horizontal: "ddga-radio-group--horizontal"
502
+ },
503
+ error: {
504
+ true: "ddga-radio-group--error"
505
+ }
506
+ },
507
+ defaultVariants: {
508
+ size: "md",
509
+ orientation: "vertical"
510
+ }
511
+ });
512
+ var radioVariants = (0, import_class_variance_authority5.cva)("ddga-radio");
513
+ function RadioGroup({
514
+ id: externalId,
515
+ label,
516
+ helperText,
517
+ errorMessage,
518
+ error,
519
+ size,
520
+ orientation,
521
+ required,
522
+ className,
523
+ children,
524
+ ...props
525
+ }) {
526
+ const { errorId, helperId, hasError, hasErrorMessage, hasHelper, controlProps, fieldId } = useFieldA11y({
527
+ name: "RadioGroup",
528
+ id: externalId,
529
+ label,
530
+ error,
531
+ errorMessage,
532
+ helperText,
533
+ "aria-label": props["aria-label"],
534
+ "aria-labelledby": props["aria-labelledby"]
535
+ });
536
+ const labelId = `${fieldId}-label`;
537
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { "data-slot": "radio-group-field", className: "ddga-field", children: [
538
+ label && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { id: labelId, className: "ddga-radio-group__label", children: [
539
+ label,
540
+ required && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "aria-hidden": "true", className: "ddga-radio-group__required", children: "*" })
541
+ ] }),
542
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
543
+ import_radix_ui2.RadioGroup.Root,
544
+ {
545
+ ...props,
546
+ "aria-invalid": controlProps["aria-invalid"],
547
+ "aria-describedby": controlProps["aria-describedby"],
548
+ "aria-labelledby": label ? labelId : props["aria-labelledby"],
549
+ orientation: orientation === "horizontal" ? "horizontal" : "vertical",
550
+ required,
551
+ "data-slot": "radio-group",
552
+ className: cn(radioGroupVariants({ size, orientation, error: hasError }), className),
553
+ children
554
+ }
555
+ ),
556
+ hasErrorMessage && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(FieldMessage, { id: errorId, variant: "error", children: errorMessage }),
557
+ hasHelper && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(FieldMessage, { id: helperId, variant: "helper", children: helperText })
558
+ ] });
559
+ }
560
+ function Radio({ id: externalId, label, className, ...props }) {
561
+ const generatedId = (0, import_react3.useId)();
562
+ const itemId = externalId ?? generatedId;
563
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "ddga-radio-row", children: [
564
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
565
+ import_radix_ui2.RadioGroup.Item,
566
+ {
567
+ ...props,
568
+ id: itemId,
569
+ "data-slot": "radio",
570
+ className: cn(radioVariants(), className),
571
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
572
+ import_radix_ui2.RadioGroup.Indicator,
573
+ {
574
+ "data-slot": "radio-indicator",
575
+ className: "ddga-radio__indicator",
576
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "ddga-radio__dot", "aria-hidden": "true" })
577
+ }
578
+ )
579
+ }
580
+ ),
581
+ label && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { htmlFor: itemId, className: "ddga-radio__label", children: label })
582
+ ] });
583
+ }
584
+
585
+ // src/components/Switch/Switch.tsx
586
+ var import_radix_ui3 = require("radix-ui");
587
+ var import_class_variance_authority6 = require("class-variance-authority");
588
+ var import_jsx_runtime8 = require("react/jsx-runtime");
589
+ var switchVariants = (0, import_class_variance_authority6.cva)("ddga-switch", {
590
+ variants: {
591
+ size: {
592
+ sm: "ddga-switch--sm",
593
+ md: "ddga-switch--md"
594
+ },
595
+ error: {
596
+ true: "ddga-switch--error"
597
+ }
598
+ },
599
+ defaultVariants: {
600
+ size: "md"
601
+ }
602
+ });
603
+ function Switch({
604
+ id: externalId,
605
+ label,
606
+ helperText,
607
+ errorMessage,
608
+ error,
609
+ size,
610
+ required,
611
+ className,
612
+ ...props
613
+ }) {
614
+ const { fieldId, errorId, helperId, hasError, hasErrorMessage, hasHelper, controlProps } = useFieldA11y({
615
+ name: "Switch",
616
+ id: externalId,
617
+ label,
618
+ error,
619
+ errorMessage,
620
+ helperText,
621
+ "aria-label": props["aria-label"],
622
+ "aria-labelledby": props["aria-labelledby"]
623
+ });
624
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { "data-slot": "switch-field", className: "ddga-field", children: [
625
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "ddga-switch-row", children: [
626
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
627
+ import_radix_ui3.Switch.Root,
628
+ {
629
+ ...props,
630
+ ...controlProps,
631
+ "data-slot": "switch",
632
+ required,
633
+ className: cn(switchVariants({ size, error: hasError }), className),
634
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_radix_ui3.Switch.Thumb, { "data-slot": "switch-thumb", className: "ddga-switch__thumb" })
635
+ }
636
+ ),
637
+ label && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { htmlFor: fieldId, className: "ddga-switch__label", children: [
638
+ label,
639
+ required && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { "aria-hidden": "true", className: "ddga-switch__required", children: "*" })
640
+ ] })
641
+ ] }),
642
+ hasErrorMessage && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(FieldMessage, { id: errorId, variant: "error", children: errorMessage }),
643
+ hasHelper && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(FieldMessage, { id: helperId, variant: "helper", children: helperText })
644
+ ] });
645
+ }
646
+
647
+ // src/components/Select/Select.tsx
648
+ var import_react5 = require("react");
649
+ var import_radix_ui4 = require("radix-ui");
650
+ var import_class_variance_authority7 = require("class-variance-authority");
462
651
 
463
652
  // src/providers/DgaContext.ts
464
- var import_react3 = require("react");
465
- var DgaContext = (0, import_react3.createContext)(null);
653
+ var import_react4 = require("react");
654
+ var DgaContext = (0, import_react4.createContext)(null);
466
655
  function useDga() {
467
- const ctx = (0, import_react3.useContext)(DgaContext);
656
+ const ctx = (0, import_react4.useContext)(DgaContext);
468
657
  if (!ctx) {
469
658
  throw new Error("useDga must be used within a <DgaProvider>");
470
659
  }
471
660
  return ctx;
472
661
  }
473
662
 
663
+ // src/components/Select/Select.tsx
664
+ var import_jsx_runtime9 = require("react/jsx-runtime");
665
+ var selectTriggerVariants = (0, import_class_variance_authority7.cva)("ddga-select-trigger", {
666
+ variants: {
667
+ size: {
668
+ sm: "ddga-select-trigger--sm",
669
+ md: "ddga-select-trigger--md"
670
+ },
671
+ error: {
672
+ true: "ddga-select-trigger--error"
673
+ }
674
+ },
675
+ defaultVariants: {
676
+ size: "md"
677
+ }
678
+ });
679
+ function Select({
680
+ id: externalId,
681
+ label,
682
+ helperText,
683
+ errorMessage,
684
+ error,
685
+ size,
686
+ required,
687
+ placeholder,
688
+ className,
689
+ children,
690
+ ...rootProps
691
+ }) {
692
+ const { fieldId, errorId, helperId, hasError, hasErrorMessage, hasHelper, controlProps } = useFieldA11y({
693
+ name: "Select",
694
+ id: externalId,
695
+ label,
696
+ error,
697
+ errorMessage,
698
+ helperText,
699
+ "aria-label": rootProps["aria-label"],
700
+ "aria-labelledby": rootProps["aria-labelledby"]
701
+ });
702
+ const ctx = (0, import_react5.useContext)(DgaContext);
703
+ const portalContainer = ctx?.rootEl ?? void 0;
704
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { "data-slot": "select-field", className: "ddga-field", children: [
705
+ label && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { htmlFor: fieldId, className: "ddga-select__label", children: [
706
+ label,
707
+ required && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { "aria-hidden": "true", className: "ddga-select__required", children: "*" })
708
+ ] }),
709
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_radix_ui4.Select.Root, { required, ...rootProps, children: [
710
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
711
+ import_radix_ui4.Select.Trigger,
712
+ {
713
+ ...controlProps,
714
+ "data-slot": "select-trigger",
715
+ className: cn(selectTriggerVariants({ size, error: hasError }), className),
716
+ children: [
717
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_radix_ui4.Select.Value, { placeholder }),
718
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_radix_ui4.Select.Icon, { asChild: true, className: "ddga-select__icon", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChevronDown, { "aria-hidden": "true" }) })
719
+ ]
720
+ }
721
+ ),
722
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_radix_ui4.Select.Portal, { container: portalContainer, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
723
+ import_radix_ui4.Select.Content,
724
+ {
725
+ "data-slot": "select-content",
726
+ className: "ddga-select-content",
727
+ position: "popper",
728
+ sideOffset: 4,
729
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_radix_ui4.Select.Viewport, { className: "ddga-select-viewport", children })
730
+ }
731
+ ) })
732
+ ] }),
733
+ hasErrorMessage && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(FieldMessage, { id: errorId, variant: "error", children: errorMessage }),
734
+ hasHelper && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(FieldMessage, { id: helperId, variant: "helper", children: helperText })
735
+ ] });
736
+ }
737
+ function SelectItem({ className, children, ...props }) {
738
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
739
+ import_radix_ui4.Select.Item,
740
+ {
741
+ ...props,
742
+ "data-slot": "select-item",
743
+ className: cn("ddga-select-item", className),
744
+ children: [
745
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_radix_ui4.Select.ItemText, { children }),
746
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_radix_ui4.Select.ItemIndicator, { className: "ddga-select-item__indicator", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Check, { className: "ddga-select-item__check", "aria-hidden": "true" }) })
747
+ ]
748
+ }
749
+ );
750
+ }
751
+
474
752
  // src/providers/DgaProvider.tsx
475
- var import_jsx_runtime7 = require("react/jsx-runtime");
753
+ var import_react6 = require("react");
754
+ var import_radix_ui5 = require("radix-ui");
755
+ var import_tokens = require("@dev-dga/tokens");
756
+ var import_jsx_runtime10 = require("react/jsx-runtime");
476
757
  function DgaProvider({
477
758
  dir = "ltr",
478
759
  locale,
479
760
  mode = "light",
761
+ theme,
480
762
  as: Component = "div",
481
763
  className,
482
764
  style: consumerStyle,
483
765
  children
484
766
  }) {
485
- const parentCtx = (0, import_react4.useContext)(DgaContext);
767
+ const parentCtx = (0, import_react6.useContext)(DgaContext);
486
768
  const isNested = parentCtx !== null;
487
- const rootRef = (0, import_react4.useRef)(null);
769
+ const [rootEl, setRootEl] = (0, import_react6.useState)(null);
488
770
  const resolvedLocale = locale ?? (dir === "rtl" ? "ar" : "en");
489
- const ctxValue = (0, import_react4.useMemo)(
490
- () => ({ dir, locale: resolvedLocale, mode, rootRef }),
491
- [dir, resolvedLocale, mode]
771
+ const ctxValue = (0, import_react6.useMemo)(
772
+ () => ({ dir, locale: resolvedLocale, mode, rootEl }),
773
+ [dir, resolvedLocale, mode, rootEl]
492
774
  );
493
- const inner = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_radix_ui2.Direction.Provider, { dir, children });
494
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(DgaContext.Provider, { value: ctxValue, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
775
+ const themeVars = (0, import_react6.useMemo)(() => theme ? (0, import_tokens.buildTheme)(theme) : null, [theme]);
776
+ const inner = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_radix_ui5.Direction.Provider, { dir, children });
777
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(DgaContext.Provider, { value: ctxValue, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
495
778
  Component,
496
779
  {
497
- ref: rootRef,
780
+ ref: setRootEl,
498
781
  dir,
499
782
  lang: resolvedLocale,
500
783
  "data-slot": "dga-root",
501
784
  "data-theme": mode,
502
785
  className,
503
- style: { colorScheme: mode, ...consumerStyle },
504
- children: isNested ? inner : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_radix_ui2.Tooltip.Provider, { delayDuration: 300, skipDelayDuration: 100, children: inner })
786
+ style: { colorScheme: mode, ...themeVars ?? {}, ...consumerStyle },
787
+ children: isNested ? inner : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_radix_ui5.Tooltip.Provider, { delayDuration: 300, skipDelayDuration: 100, children: inner })
505
788
  }
506
789
  ) });
507
790
  }
@@ -515,14 +798,25 @@ function useDir() {
515
798
  Button,
516
799
  Checkbox,
517
800
  DgaProvider,
801
+ FieldMessage,
518
802
  Input,
803
+ Radio,
804
+ RadioGroup,
805
+ Select,
806
+ SelectItem,
807
+ Switch,
519
808
  Textarea,
520
809
  buttonVariants,
521
810
  checkboxVariants,
522
811
  cn,
523
812
  inputVariants,
813
+ radioGroupVariants,
814
+ radioVariants,
815
+ selectTriggerVariants,
816
+ switchVariants,
524
817
  textareaVariants,
525
818
  useDga,
526
- useDir
819
+ useDir,
820
+ useFieldA11y
527
821
  });
528
822
  //# sourceMappingURL=index.cjs.map