@neuronix-tech/ui 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/dist/cli/index.js +4 -0
  2. package/dist/components/button/button.d.ts +18 -0
  3. package/dist/components/button/button.js +40 -0
  4. package/dist/components/button/button.js.map +1 -0
  5. package/dist/components/button/button.types.d.ts +36 -0
  6. package/dist/components/button/button.types.js +1 -0
  7. package/dist/components/button/button.types.js.map +1 -0
  8. package/dist/components/button/button.variants.d.ts +32 -0
  9. package/dist/components/button/button.variants.js +45 -0
  10. package/dist/components/button/button.variants.js.map +1 -0
  11. package/dist/components/button/index.d.ts +8 -0
  12. package/dist/components/button/index.js +5 -0
  13. package/dist/components/button/index.js.map +1 -0
  14. package/dist/components/form-currency-input/form-currency-input.d.ts +12 -0
  15. package/dist/components/form-currency-input/form-currency-input.js +62 -0
  16. package/dist/components/form-currency-input/form-currency-input.js.map +1 -0
  17. package/dist/components/form-currency-input/form-currency-input.types.d.ts +32 -0
  18. package/dist/components/form-currency-input/form-currency-input.types.js +1 -0
  19. package/dist/components/form-currency-input/form-currency-input.types.js.map +1 -0
  20. package/dist/components/form-currency-input/index.d.ts +4 -0
  21. package/dist/components/form-currency-input/index.js +5 -0
  22. package/dist/components/form-currency-input/index.js.map +1 -0
  23. package/dist/components/form-number-input/form-number-input.d.ts +13 -0
  24. package/dist/components/form-number-input/form-number-input.js +75 -0
  25. package/dist/components/form-number-input/form-number-input.js.map +1 -0
  26. package/dist/components/form-number-input/form-number-input.types.d.ts +34 -0
  27. package/dist/components/form-number-input/form-number-input.types.js +1 -0
  28. package/dist/components/form-number-input/form-number-input.types.js.map +1 -0
  29. package/dist/components/form-number-input/index.d.ts +4 -0
  30. package/dist/components/form-number-input/index.js +5 -0
  31. package/dist/components/form-number-input/index.js.map +1 -0
  32. package/dist/components/form-percentage-input/form-percentage-input.d.ts +12 -0
  33. package/dist/components/form-percentage-input/form-percentage-input.js +65 -0
  34. package/dist/components/form-percentage-input/form-percentage-input.js.map +1 -0
  35. package/dist/components/form-percentage-input/form-percentage-input.types.d.ts +33 -0
  36. package/dist/components/form-percentage-input/form-percentage-input.types.js +1 -0
  37. package/dist/components/form-percentage-input/form-percentage-input.types.js.map +1 -0
  38. package/dist/components/form-percentage-input/index.d.ts +4 -0
  39. package/dist/components/form-percentage-input/index.js +5 -0
  40. package/dist/components/form-percentage-input/index.js.map +1 -0
  41. package/dist/components/form-text-input/form-text-input.d.ts +19 -0
  42. package/dist/components/form-text-input/form-text-input.js +64 -0
  43. package/dist/components/form-text-input/form-text-input.js.map +1 -0
  44. package/dist/components/form-text-input/form-text-input.types.d.ts +45 -0
  45. package/dist/components/form-text-input/form-text-input.types.js +1 -0
  46. package/dist/components/form-text-input/form-text-input.types.js.map +1 -0
  47. package/dist/components/form-text-input/index.d.ts +4 -0
  48. package/dist/components/form-text-input/index.js +5 -0
  49. package/dist/components/form-text-input/index.js.map +1 -0
  50. package/dist/components/icon-button/icon-button.d.ts +20 -0
  51. package/dist/components/icon-button/icon-button.js +32 -0
  52. package/dist/components/icon-button/icon-button.js.map +1 -0
  53. package/dist/components/icon-button/icon-button.types.d.ts +24 -0
  54. package/dist/components/icon-button/icon-button.types.js +1 -0
  55. package/dist/components/icon-button/icon-button.types.js.map +1 -0
  56. package/dist/components/icon-button/index.d.ts +8 -0
  57. package/dist/components/icon-button/index.js +5 -0
  58. package/dist/components/icon-button/index.js.map +1 -0
  59. package/dist/components/spinner/index.d.ts +2 -0
  60. package/dist/components/spinner/index.js +5 -0
  61. package/dist/components/spinner/index.js.map +1 -0
  62. package/dist/components/spinner/spinner.d.ts +15 -0
  63. package/dist/components/spinner/spinner.js +9 -0
  64. package/dist/components/spinner/spinner.js.map +1 -0
  65. package/dist/components/toast/index.d.ts +3 -0
  66. package/dist/components/toast/index.js +8 -0
  67. package/dist/components/toast/index.js.map +1 -0
  68. package/dist/components/toast/toast.d.ts +30 -0
  69. package/dist/components/toast/toast.js +75 -0
  70. package/dist/components/toast/toast.js.map +1 -0
  71. package/dist/components/toast/toast.types.d.ts +22 -0
  72. package/dist/components/toast/toast.types.js +1 -0
  73. package/dist/components/toast/toast.types.js.map +1 -0
  74. package/dist/components/toast/toast.variants.d.ts +23 -0
  75. package/dist/components/toast/toast.variants.js +19 -0
  76. package/dist/components/toast/toast.variants.js.map +1 -0
  77. package/dist/hooks/use-number-mask.d.ts +24 -0
  78. package/dist/hooks/use-number-mask.js +59 -0
  79. package/dist/hooks/use-number-mask.js.map +1 -0
  80. package/dist/index.d.ts +23 -0
  81. package/dist/index.js +38 -0
  82. package/dist/index.js.map +1 -0
  83. package/dist/lib/cn.d.ts +10 -0
  84. package/dist/lib/cn.js +8 -0
  85. package/dist/lib/cn.js.map +1 -0
  86. package/dist/lib/format/number.d.ts +6 -0
  87. package/dist/lib/format/number.js +26 -0
  88. package/dist/lib/format/number.js.map +1 -0
  89. package/dist/lib/format/percent.d.ts +15 -0
  90. package/dist/lib/format/percent.js +7 -0
  91. package/dist/lib/format/percent.js.map +1 -0
  92. package/dist/lib/sealed.d.ts +14 -0
  93. package/dist/lib/sealed.js +1 -0
  94. package/dist/lib/sealed.js.map +1 -0
  95. package/dist/primitives/field.d.ts +17 -0
  96. package/dist/primitives/field.js +74 -0
  97. package/dist/primitives/field.js.map +1 -0
  98. package/dist/primitives/form-field.d.ts +27 -0
  99. package/dist/primitives/form-field.js +27 -0
  100. package/dist/primitives/form-field.js.map +1 -0
  101. package/dist/primitives/input-group.d.ts +19 -0
  102. package/dist/primitives/input-group.js +87 -0
  103. package/dist/primitives/input-group.js.map +1 -0
  104. package/dist/primitives/input.d.ts +11 -0
  105. package/dist/primitives/input.js +22 -0
  106. package/dist/primitives/input.js.map +1 -0
  107. package/dist/primitives/label.d.ts +6 -0
  108. package/dist/primitives/label.js +19 -0
  109. package/dist/primitives/label.js.map +1 -0
  110. package/dist/primitives/spinner.d.ts +10 -0
  111. package/dist/primitives/spinner.js +19 -0
  112. package/dist/primitives/spinner.js.map +1 -0
  113. package/dist/primitives/toast.d.ts +31 -0
  114. package/dist/primitives/toast.js +102 -0
  115. package/dist/primitives/toast.js.map +1 -0
  116. package/dist/primitives/tooltip.d.ts +17 -0
  117. package/dist/primitives/tooltip.js +51 -0
  118. package/dist/primitives/tooltip.js.map +1 -0
  119. package/dist/styles.css +2 -0
  120. package/package.json +70 -3
  121. package/README.md +0 -3
@@ -0,0 +1,59 @@
1
+ "use client";
2
+ import { useLayoutEffect, useRef, useState } from "react";
3
+ import { groupNumeric, stripGroups } from "../lib/format/number.js";
4
+ function parseNumeric(raw) {
5
+ if (raw === "" || raw === "-" || raw === ".") return null;
6
+ const n = Number(raw);
7
+ return Number.isFinite(n) ? n : null;
8
+ }
9
+ const countSignificant = (s) => (s.match(/[\d.-]/g) ?? []).length;
10
+ function caretAfterSignificant(formatted, anchor) {
11
+ if (anchor <= 0) return 0;
12
+ let seen = 0;
13
+ for (let i = 0; i < formatted.length; i += 1) {
14
+ if (formatted[i] !== ",") {
15
+ seen += 1;
16
+ if (seen === anchor) return i + 1;
17
+ }
18
+ }
19
+ return formatted.length;
20
+ }
21
+ function useNumberMask({
22
+ value,
23
+ sanitize,
24
+ format,
25
+ onValueChange,
26
+ group = true
27
+ }) {
28
+ const ref = useRef(null);
29
+ const caret = useRef(null);
30
+ const mask = (raw) => group ? groupNumeric(raw) : raw;
31
+ const toDisplay = (v) => mask(v === null ? "" : format(v));
32
+ const [display, setDisplay] = useState(() => toDisplay(value));
33
+ const [lastExternal, setLastExternal] = useState(value);
34
+ if (value !== lastExternal) {
35
+ setLastExternal(value);
36
+ if (parseNumeric(stripGroups(display)) !== value) setDisplay(toDisplay(value));
37
+ }
38
+ useLayoutEffect(() => {
39
+ if (caret.current !== null && ref.current) {
40
+ ref.current.setSelectionRange(caret.current, caret.current);
41
+ caret.current = null;
42
+ }
43
+ });
44
+ const onChange = (event) => {
45
+ const raw = event.target.value;
46
+ const anchor = countSignificant(raw.slice(0, event.target.selectionStart ?? raw.length));
47
+ const cleaned = sanitize(stripGroups(raw));
48
+ const next = mask(cleaned);
49
+ setDisplay(next);
50
+ caret.current = next === display ? null : caretAfterSignificant(next, anchor);
51
+ onValueChange(parseNumeric(cleaned));
52
+ };
53
+ const reformat = (v = value) => setDisplay(toDisplay(v));
54
+ return { ref, display, onChange, reformat };
55
+ }
56
+ export {
57
+ useNumberMask
58
+ };
59
+ //# sourceMappingURL=use-number-mask.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/hooks/use-number-mask.ts"],"sourcesContent":["\"use client\"\n\nimport { useLayoutEffect, useRef, useState } from \"react\"\n\nimport { groupNumeric, stripGroups } from \"../lib/format/number.js\"\n\nfunction parseNumeric(raw: string): number | null {\n if (raw === \"\" || raw === \"-\" || raw === \".\") return null\n const n = Number(raw)\n return Number.isFinite(n) ? n : null\n}\n\nconst countSignificant = (s: string) => (s.match(/[\\d.-]/g) ?? []).length\n\n// Caret index just after the `anchor`-th significant (non-comma) char.\nfunction caretAfterSignificant(formatted: string, anchor: number): number {\n if (anchor <= 0) return 0\n let seen = 0\n for (let i = 0; i < formatted.length; i += 1) {\n if (formatted[i] !== \",\") {\n seen += 1\n if (seen === anchor) return i + 1\n }\n }\n return formatted.length\n}\n\ninterface UseNumberMaskArgs {\n value: number | null\n sanitize: (raw: string) => string\n format: (value: number) => string\n onValueChange: (value: number | null) => void\n group?: boolean\n}\n\n/**\n * Live-formatting numeric input mask (ported from nx-automations): groups\n * thousands as the user types and keeps the caret stable when separators\n * shift. The form state stores `number | null` — the grouped string is\n * display only. Blur normalization (padding, clamping) is the caller's job\n * via `reformat`.\n */\nexport function useNumberMask({\n value,\n sanitize,\n format,\n onValueChange,\n group = true,\n}: UseNumberMaskArgs) {\n const ref = useRef<HTMLInputElement>(null)\n const caret = useRef<number | null>(null)\n\n const mask = (raw: string) => (group ? groupNumeric(raw) : raw)\n const toDisplay = (v: number | null) => mask(v === null ? \"\" : format(v))\n\n const [display, setDisplay] = useState(() => toDisplay(value))\n const [lastExternal, setLastExternal] = useState(value)\n\n // Re-sync from the form (reset / edit seeding) unless the user is mid-edit\n // of the same numeric value.\n if (value !== lastExternal) {\n setLastExternal(value)\n if (parseNumeric(stripGroups(display)) !== value) setDisplay(toDisplay(value))\n }\n\n useLayoutEffect(() => {\n if (caret.current !== null && ref.current) {\n ref.current.setSelectionRange(caret.current, caret.current)\n caret.current = null\n }\n })\n\n const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n const raw = event.target.value\n const anchor = countSignificant(raw.slice(0, event.target.selectionStart ?? raw.length))\n const cleaned = sanitize(stripGroups(raw))\n const next = mask(cleaned)\n setDisplay(next)\n // A rejected keystroke leaves `next` === `display`: no re-render happens,\n // so null the caret rather than strand a stale index for a later render.\n caret.current = next === display ? null : caretAfterSignificant(next, anchor)\n onValueChange(parseNumeric(cleaned))\n }\n\n const reformat = (v: number | null = value) => setDisplay(toDisplay(v))\n\n return { ref, display, onChange, reformat }\n}\n"],"mappings":";AAEA,SAAS,iBAAiB,QAAQ,gBAAgB;AAElD,SAAS,cAAc,mBAAmB;AAE1C,SAAS,aAAa,KAA4B;AAChD,MAAI,QAAQ,MAAM,QAAQ,OAAO,QAAQ,IAAK,QAAO;AACrD,QAAM,IAAI,OAAO,GAAG;AACpB,SAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAClC;AAEA,MAAM,mBAAmB,CAAC,OAAe,EAAE,MAAM,SAAS,KAAK,CAAC,GAAG;AAGnE,SAAS,sBAAsB,WAAmB,QAAwB;AACxE,MAAI,UAAU,EAAG,QAAO;AACxB,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK,GAAG;AAC5C,QAAI,UAAU,CAAC,MAAM,KAAK;AACxB,cAAQ;AACR,UAAI,SAAS,OAAQ,QAAO,IAAI;AAAA,IAClC;AAAA,EACF;AACA,SAAO,UAAU;AACnB;AAiBO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AACV,GAAsB;AACpB,QAAM,MAAM,OAAyB,IAAI;AACzC,QAAM,QAAQ,OAAsB,IAAI;AAExC,QAAM,OAAO,CAAC,QAAiB,QAAQ,aAAa,GAAG,IAAI;AAC3D,QAAM,YAAY,CAAC,MAAqB,KAAK,MAAM,OAAO,KAAK,OAAO,CAAC,CAAC;AAExE,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,MAAM,UAAU,KAAK,CAAC;AAC7D,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,KAAK;AAItD,MAAI,UAAU,cAAc;AAC1B,oBAAgB,KAAK;AACrB,QAAI,aAAa,YAAY,OAAO,CAAC,MAAM,MAAO,YAAW,UAAU,KAAK,CAAC;AAAA,EAC/E;AAEA,kBAAgB,MAAM;AACpB,QAAI,MAAM,YAAY,QAAQ,IAAI,SAAS;AACzC,UAAI,QAAQ,kBAAkB,MAAM,SAAS,MAAM,OAAO;AAC1D,YAAM,UAAU;AAAA,IAClB;AAAA,EACF,CAAC;AAED,QAAM,WAAW,CAAC,UAA+C;AAC/D,UAAM,MAAM,MAAM,OAAO;AACzB,UAAM,SAAS,iBAAiB,IAAI,MAAM,GAAG,MAAM,OAAO,kBAAkB,IAAI,MAAM,CAAC;AACvF,UAAM,UAAU,SAAS,YAAY,GAAG,CAAC;AACzC,UAAM,OAAO,KAAK,OAAO;AACzB,eAAW,IAAI;AAGf,UAAM,UAAU,SAAS,UAAU,OAAO,sBAAsB,MAAM,MAAM;AAC5E,kBAAc,aAAa,OAAO,CAAC;AAAA,EACrC;AAEA,QAAM,WAAW,CAAC,IAAmB,UAAU,WAAW,UAAU,CAAC,CAAC;AAEtE,SAAO,EAAE,KAAK,SAAS,UAAU,SAAS;AAC5C;","names":[]}
@@ -0,0 +1,23 @@
1
+ export { Button } from './components/button/button.js';
2
+ export { ButtonProps } from './components/button/button.types.js';
3
+ export { FormCurrencyInput } from './components/form-currency-input/form-currency-input.js';
4
+ export { CurrencyCode, FormCurrencyInputProps } from './components/form-currency-input/form-currency-input.types.js';
5
+ export { FormNumberInput } from './components/form-number-input/form-number-input.js';
6
+ export { FormNumberInputProps } from './components/form-number-input/form-number-input.types.js';
7
+ export { FormPercentageInput } from './components/form-percentage-input/form-percentage-input.js';
8
+ export { FormPercentageInputProps } from './components/form-percentage-input/form-percentage-input.types.js';
9
+ export { fractionToPercent, percentToFraction } from './lib/format/percent.js';
10
+ export { FormTextInput } from './components/form-text-input/form-text-input.js';
11
+ export { FormTextInputProps, TextInputType } from './components/form-text-input/form-text-input.types.js';
12
+ export { IconButton } from './components/icon-button/icon-button.js';
13
+ export { IconButtonProps } from './components/icon-button/icon-button.types.js';
14
+ export { Spinner, SpinnerProps } from './components/spinner/spinner.js';
15
+ export { Toaster, errorToast, successToast, warningToast } from './components/toast/toast.js';
16
+ export { ToastActionOptions, ToastOptions } from './components/toast/toast.types.js';
17
+ import 'react';
18
+ import '@base-ui/react/button';
19
+ import './components/button/button.variants.js';
20
+ import 'class-variance-authority/types';
21
+ import 'class-variance-authority';
22
+ import './lib/sealed.js';
23
+ import 'react-hook-form';
package/dist/index.js ADDED
@@ -0,0 +1,38 @@
1
+ import { Button } from "./components/button/index.js";
2
+ import {
3
+ FormCurrencyInput
4
+ } from "./components/form-currency-input/index.js";
5
+ import {
6
+ FormNumberInput
7
+ } from "./components/form-number-input/index.js";
8
+ import {
9
+ FormPercentageInput
10
+ } from "./components/form-percentage-input/index.js";
11
+ import { percentToFraction, fractionToPercent } from "./lib/format/percent.js";
12
+ import {
13
+ FormTextInput
14
+ } from "./components/form-text-input/index.js";
15
+ import { IconButton } from "./components/icon-button/index.js";
16
+ import { Spinner } from "./components/spinner/index.js";
17
+ import {
18
+ Toaster,
19
+ successToast,
20
+ warningToast,
21
+ errorToast
22
+ } from "./components/toast/index.js";
23
+ export {
24
+ Button,
25
+ FormCurrencyInput,
26
+ FormNumberInput,
27
+ FormPercentageInput,
28
+ FormTextInput,
29
+ IconButton,
30
+ Spinner,
31
+ Toaster,
32
+ errorToast,
33
+ fractionToPercent,
34
+ percentToFraction,
35
+ successToast,
36
+ warningToast
37
+ };
38
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @neuronix-tech/ui — sealed catalog barrel. Only finished catalog components are\n * exported here; primitives stay internal by design.\n */\nexport { Button, type ButtonProps } from \"./components/button/index.js\"\nexport {\n FormCurrencyInput,\n type FormCurrencyInputProps,\n type CurrencyCode,\n} from \"./components/form-currency-input/index.js\"\nexport {\n FormNumberInput,\n type FormNumberInputProps,\n} from \"./components/form-number-input/index.js\"\nexport {\n FormPercentageInput,\n type FormPercentageInputProps,\n} from \"./components/form-percentage-input/index.js\"\nexport { percentToFraction, fractionToPercent } from \"./lib/format/percent.js\"\nexport {\n FormTextInput,\n type FormTextInputProps,\n type TextInputType,\n} from \"./components/form-text-input/index.js\"\nexport { IconButton, type IconButtonProps } from \"./components/icon-button/index.js\"\nexport { Spinner, type SpinnerProps } from \"./components/spinner/index.js\"\nexport {\n Toaster,\n successToast,\n warningToast,\n errorToast,\n type ToastOptions,\n type ToastActionOptions,\n} from \"./components/toast/index.js\"\n"],"mappings":"AAIA,SAAS,cAAgC;AACzC;AAAA,EACE;AAAA,OAGK;AACP;AAAA,EACE;AAAA,OAEK;AACP;AAAA,EACE;AAAA,OAEK;AACP,SAAS,mBAAmB,yBAAyB;AACrD;AAAA,EACE;AAAA,OAGK;AACP,SAAS,kBAAwC;AACjD,SAAS,eAAkC;AAC3C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;","names":[]}
@@ -0,0 +1,10 @@
1
+ import { ClassValue } from 'clsx';
2
+
3
+ /**
4
+ * Internal class combiner. Deliberately clsx-only — no tailwind-merge: consumers
5
+ * cannot pass className, so every class string is authored in this package and
6
+ * conflicts are resolved at authoring time, not runtime.
7
+ */
8
+ declare function cn(...inputs: ClassValue[]): string;
9
+
10
+ export { cn };
package/dist/lib/cn.js ADDED
@@ -0,0 +1,8 @@
1
+ import { clsx } from "clsx";
2
+ function cn(...inputs) {
3
+ return clsx(inputs);
4
+ }
5
+ export {
6
+ cn
7
+ };
8
+ //# sourceMappingURL=cn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/cn.ts"],"sourcesContent":["import { clsx, type ClassValue } from \"clsx\"\n\n/**\n * Internal class combiner. Deliberately clsx-only — no tailwind-merge: consumers\n * cannot pass className, so every class string is authored in this package and\n * conflicts are resolved at authoring time, not runtime.\n */\nexport function cn(...inputs: ClassValue[]): string {\n return clsx(inputs)\n}\n"],"mappings":"AAAA,SAAS,YAA6B;AAO/B,SAAS,MAAM,QAA8B;AAClD,SAAO,KAAK,MAAM;AACpB;","names":[]}
@@ -0,0 +1,6 @@
1
+ declare const stripGroups: (s: string) => string;
2
+ declare function groupNumeric(raw: string): string;
3
+ declare function sanitizeDecimal(raw: string, maxDecimals?: number): string;
4
+ declare const asFiniteNumber: (value: unknown) => number | null;
5
+
6
+ export { asFiniteNumber, groupNumeric, sanitizeDecimal, stripGroups };
@@ -0,0 +1,26 @@
1
+ const stripGroups = (s) => s.replace(/,/g, "");
2
+ function groupNumeric(raw) {
3
+ if (raw === "" || raw === "-") return raw;
4
+ const negative = raw.startsWith("-");
5
+ const body = negative ? raw.slice(1) : raw;
6
+ const dot = body.indexOf(".");
7
+ const intPart = dot === -1 ? body : body.slice(0, dot);
8
+ const rest = dot === -1 ? "" : body.slice(dot);
9
+ const grouped = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
10
+ return `${negative ? "-" : ""}${grouped}${rest}`;
11
+ }
12
+ function sanitizeDecimal(raw, maxDecimals = 2) {
13
+ const s = raw.replace(/[^\d.]/g, "");
14
+ const dot = s.indexOf(".");
15
+ if (dot === -1) return s;
16
+ const dec = s.slice(dot + 1).replace(/\./g, "").slice(0, maxDecimals);
17
+ return `${s.slice(0, dot)}.${dec}`;
18
+ }
19
+ const asFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
20
+ export {
21
+ asFiniteNumber,
22
+ groupNumeric,
23
+ sanitizeDecimal,
24
+ stripGroups
25
+ };
26
+ //# sourceMappingURL=number.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/format/number.ts"],"sourcesContent":["// Live-input helpers — operate on partial numeric strings while typing, which\n// Intl.NumberFormat can't (it needs a complete number). Used by the numeric\n// form inputs' mask (see hooks/use-number-mask). Ported from nx-automations.\n\nexport const stripGroups = (s: string) => s.replace(/,/g, \"\")\n\n// Assumes an already-sanitized string (digits, optional leading \"-\", one \".\").\nexport function groupNumeric(raw: string): string {\n if (raw === \"\" || raw === \"-\") return raw\n const negative = raw.startsWith(\"-\")\n const body = negative ? raw.slice(1) : raw\n const dot = body.indexOf(\".\")\n const intPart = dot === -1 ? body : body.slice(0, dot)\n const rest = dot === -1 ? \"\" : body.slice(dot)\n const grouped = intPart.replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\")\n return `${negative ? \"-\" : \"\"}${grouped}${rest}`\n}\n\nexport function sanitizeDecimal(raw: string, maxDecimals = 2): string {\n const s = raw.replace(/[^\\d.]/g, \"\")\n const dot = s.indexOf(\".\")\n if (dot === -1) return s\n const dec = s\n .slice(dot + 1)\n .replace(/\\./g, \"\")\n .slice(0, maxDecimals)\n return `${s.slice(0, dot)}.${dec}`\n}\n\nexport const asFiniteNumber = (value: unknown): number | null =>\n typeof value === \"number\" && Number.isFinite(value) ? value : null\n"],"mappings":"AAIO,MAAM,cAAc,CAAC,MAAc,EAAE,QAAQ,MAAM,EAAE;AAGrD,SAAS,aAAa,KAAqB;AAChD,MAAI,QAAQ,MAAM,QAAQ,IAAK,QAAO;AACtC,QAAM,WAAW,IAAI,WAAW,GAAG;AACnC,QAAM,OAAO,WAAW,IAAI,MAAM,CAAC,IAAI;AACvC,QAAM,MAAM,KAAK,QAAQ,GAAG;AAC5B,QAAM,UAAU,QAAQ,KAAK,OAAO,KAAK,MAAM,GAAG,GAAG;AACrD,QAAM,OAAO,QAAQ,KAAK,KAAK,KAAK,MAAM,GAAG;AAC7C,QAAM,UAAU,QAAQ,QAAQ,yBAAyB,GAAG;AAC5D,SAAO,GAAG,WAAW,MAAM,EAAE,GAAG,OAAO,GAAG,IAAI;AAChD;AAEO,SAAS,gBAAgB,KAAa,cAAc,GAAW;AACpE,QAAM,IAAI,IAAI,QAAQ,WAAW,EAAE;AACnC,QAAM,MAAM,EAAE,QAAQ,GAAG;AACzB,MAAI,QAAQ,GAAI,QAAO;AACvB,QAAM,MAAM,EACT,MAAM,MAAM,CAAC,EACb,QAAQ,OAAO,EAAE,EACjB,MAAM,GAAG,WAAW;AACvB,SAAO,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,IAAI,GAAG;AAClC;AAEO,MAAM,iBAAiB,CAAC,UAC7B,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,IAAI,QAAQ;","names":[]}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Percentage boundary converters.
3
+ *
4
+ * FormPercentageInput stores PERCENT POINTS in form state (user types 15 →
5
+ * state holds 15) — the UI convention (what you see is what's stored). When a
6
+ * backend or formula expects a FRACTION (0.15), convert explicitly at the
7
+ * boundary with these — one greppable call instead of scattered `/100`s.
8
+ *
9
+ * api.discountRate = percentToFraction(values.discount) // 15 → 0.15
10
+ * defaultValues: { discount: fractionToPercent(api.rate) } // 0.15 → 15
11
+ */
12
+ declare const percentToFraction: (points: number | null) => number | null;
13
+ declare const fractionToPercent: (fraction: number | null) => number | null;
14
+
15
+ export { fractionToPercent, percentToFraction };
@@ -0,0 +1,7 @@
1
+ const percentToFraction = (points) => points === null ? null : points / 100;
2
+ const fractionToPercent = (fraction) => fraction === null ? null : fraction * 100;
3
+ export {
4
+ fractionToPercent,
5
+ percentToFraction
6
+ };
7
+ //# sourceMappingURL=percent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/format/percent.ts"],"sourcesContent":["/**\n * Percentage boundary converters.\n *\n * FormPercentageInput stores PERCENT POINTS in form state (user types 15 →\n * state holds 15) — the UI convention (what you see is what's stored). When a\n * backend or formula expects a FRACTION (0.15), convert explicitly at the\n * boundary with these — one greppable call instead of scattered `/100`s.\n *\n * api.discountRate = percentToFraction(values.discount) // 15 → 0.15\n * defaultValues: { discount: fractionToPercent(api.rate) } // 0.15 → 15\n */\nexport const percentToFraction = (points: number | null): number | null =>\n points === null ? null : points / 100\n\nexport const fractionToPercent = (fraction: number | null): number | null =>\n fraction === null ? null : fraction * 100\n"],"mappings":"AAWO,MAAM,oBAAoB,CAAC,WAChC,WAAW,OAAO,OAAO,SAAS;AAE7B,MAAM,oBAAoB,CAAC,aAChC,aAAa,OAAO,OAAO,WAAW;","names":[]}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The sealing type: strips styling escape hatches from a props contract.
3
+ *
4
+ * Neuronix UI components are Legos — devs pick variants and sizes from a fixed
5
+ * menu and make no other UI decisions. `className`/`style` are removed at the
6
+ * type level, and components never spread unknown props onto styled nodes, so
7
+ * JS-level smuggling is inert too. Layout placement belongs to the layout
8
+ * components (Box/Stack/Grid/GridItem), never to per-component overrides.
9
+ *
10
+ * `data-*` attributes still pass through for testing and analytics.
11
+ */
12
+ type Sealed<T> = Omit<T, "className" | "style">;
13
+
14
+ export type { Sealed };
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=sealed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,17 @@
1
+ import * as react from 'react';
2
+ import { LabelPrimitive } from './label.js';
3
+
4
+ /**
5
+ * Internal field skeleton (ported subset of nx-automations ui/field.tsx —
6
+ * vertical orientation only; more variants come with future components).
7
+ * Every form input renders: Field → FieldLabel → control → FieldError.
8
+ */
9
+ declare function Field({ className, ...props }: React.ComponentProps<"div">): react.JSX.Element;
10
+ declare function FieldLabel({ className, ...props }: React.ComponentProps<typeof LabelPrimitive>): react.JSX.Element;
11
+ declare function FieldError({ className, children, errors, ...props }: React.ComponentProps<"div"> & {
12
+ errors?: Array<{
13
+ message?: string;
14
+ } | undefined>;
15
+ }): react.JSX.Element | null;
16
+
17
+ export { Field, FieldError, FieldLabel };
@@ -0,0 +1,74 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { useMemo } from "react";
4
+ import { cn } from "../lib/cn.js";
5
+ import { LabelPrimitive } from "./label.js";
6
+ function Field({ className, ...props }) {
7
+ return /* @__PURE__ */ jsx(
8
+ "div",
9
+ {
10
+ role: "group",
11
+ "data-slot": "field",
12
+ "data-orientation": "vertical",
13
+ className: cn(
14
+ "nxui:group/field nxui:flex nxui:w-full nxui:gap-2 nxui:flex-col nxui:*:w-full nxui:[&>.sr-only]:w-auto nxui:data-[invalid=true]:text-destructive",
15
+ className
16
+ ),
17
+ ...props
18
+ }
19
+ );
20
+ }
21
+ function FieldLabel({ className, ...props }) {
22
+ return /* @__PURE__ */ jsx(
23
+ LabelPrimitive,
24
+ {
25
+ "data-slot": "field-label",
26
+ className: cn(
27
+ "nxui:group/field-label nxui:peer/field-label nxui:flex nxui:w-fit nxui:gap-2 nxui:leading-snug nxui:group-data-[disabled=true]/field:opacity-50",
28
+ className
29
+ ),
30
+ ...props
31
+ }
32
+ );
33
+ }
34
+ function FieldError({
35
+ className,
36
+ children,
37
+ errors,
38
+ ...props
39
+ }) {
40
+ const content = useMemo(() => {
41
+ if (children) {
42
+ return children;
43
+ }
44
+ if (!errors?.length) {
45
+ return null;
46
+ }
47
+ const uniqueErrors = [...new Map(errors.map((error) => [error?.message, error])).values()];
48
+ if (uniqueErrors?.length == 1) {
49
+ return uniqueErrors[0]?.message;
50
+ }
51
+ return /* @__PURE__ */ jsx("ul", { className: "nxui:ml-4 nxui:flex nxui:list-disc nxui:flex-col nxui:gap-1", children: uniqueErrors.map(
52
+ (error, index) => error?.message && /* @__PURE__ */ jsx("li", { children: error.message }, index)
53
+ ) });
54
+ }, [children, errors]);
55
+ if (!content) {
56
+ return null;
57
+ }
58
+ return /* @__PURE__ */ jsx(
59
+ "div",
60
+ {
61
+ role: "alert",
62
+ "data-slot": "field-error",
63
+ className: cn("nxui:text-body/relaxed nxui:font-normal nxui:text-destructive", className),
64
+ ...props,
65
+ children: content
66
+ }
67
+ );
68
+ }
69
+ export {
70
+ Field,
71
+ FieldError,
72
+ FieldLabel
73
+ };
74
+ //# sourceMappingURL=field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/primitives/field.tsx"],"sourcesContent":["\"use client\"\n\nimport { useMemo } from \"react\"\n\nimport { cn } from \"../lib/cn.js\"\nimport { LabelPrimitive } from \"./label.js\"\n\n/**\n * Internal field skeleton (ported subset of nx-automations ui/field.tsx —\n * vertical orientation only; more variants come with future components).\n * Every form input renders: Field → FieldLabel → control → FieldError.\n */\n\nfunction Field({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n role=\"group\"\n data-slot=\"field\"\n data-orientation=\"vertical\"\n className={cn(\n \"nxui:group/field nxui:flex nxui:w-full nxui:gap-2 nxui:flex-col nxui:*:w-full nxui:[&>.sr-only]:w-auto nxui:data-[invalid=true]:text-destructive\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction FieldLabel({ className, ...props }: React.ComponentProps<typeof LabelPrimitive>) {\n return (\n <LabelPrimitive\n data-slot=\"field-label\"\n className={cn(\n \"nxui:group/field-label nxui:peer/field-label nxui:flex nxui:w-fit nxui:gap-2 nxui:leading-snug nxui:group-data-[disabled=true]/field:opacity-50\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction FieldError({\n className,\n children,\n errors,\n ...props\n}: React.ComponentProps<\"div\"> & {\n errors?: Array<{ message?: string } | undefined>\n}) {\n const content = useMemo(() => {\n if (children) {\n return children\n }\n if (!errors?.length) {\n return null\n }\n const uniqueErrors = [...new Map(errors.map((error) => [error?.message, error])).values()]\n if (uniqueErrors?.length == 1) {\n return uniqueErrors[0]?.message\n }\n return (\n <ul className=\"nxui:ml-4 nxui:flex nxui:list-disc nxui:flex-col nxui:gap-1\">\n {uniqueErrors.map(\n (error, index) => error?.message && <li key={index}>{error.message}</li>\n )}\n </ul>\n )\n }, [children, errors])\n\n if (!content) {\n return null\n }\n\n return (\n <div\n role=\"alert\"\n data-slot=\"field-error\"\n className={cn(\"nxui:text-body/relaxed nxui:font-normal nxui:text-destructive\", className)}\n {...props}\n >\n {content}\n </div>\n )\n}\n\nexport { Field, FieldLabel, FieldError }\n"],"mappings":";AAeI;AAbJ,SAAS,eAAe;AAExB,SAAS,UAAU;AACnB,SAAS,sBAAsB;AAQ/B,SAAS,MAAM,EAAE,WAAW,GAAG,MAAM,GAAgC;AACnE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,aAAU;AAAA,MACV,oBAAiB;AAAA,MACjB,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,WAAW,EAAE,WAAW,GAAG,MAAM,GAAgD;AACxF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAEG;AACD,QAAM,UAAU,QAAQ,MAAM;AAC5B,QAAI,UAAU;AACZ,aAAO;AAAA,IACT;AACA,QAAI,CAAC,QAAQ,QAAQ;AACnB,aAAO;AAAA,IACT;AACA,UAAM,eAAe,CAAC,GAAG,IAAI,IAAI,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,SAAS,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC;AACzF,QAAI,cAAc,UAAU,GAAG;AAC7B,aAAO,aAAa,CAAC,GAAG;AAAA,IAC1B;AACA,WACE,oBAAC,QAAG,WAAU,+DACX,uBAAa;AAAA,MACZ,CAAC,OAAO,UAAU,OAAO,WAAW,oBAAC,QAAgB,gBAAM,WAAd,KAAsB;AAAA,IACrE,GACF;AAAA,EAEJ,GAAG,CAAC,UAAU,MAAM,CAAC;AAErB,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,EACT;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,aAAU;AAAA,MACV,WAAW,GAAG,iEAAiE,SAAS;AAAA,MACvF,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ;","names":[]}
@@ -0,0 +1,27 @@
1
+ import * as react from 'react';
2
+
3
+ /**
4
+ * Shared pieces of the Form* input skeleton, so every input in the family has
5
+ * an identical label (asterisk, hint) and loading treatment without each
6
+ * component re-implementing them.
7
+ */
8
+ interface FormFieldLabelProps {
9
+ htmlFor: string;
10
+ label: string;
11
+ optional: boolean;
12
+ labelHint?: string;
13
+ }
14
+ /**
15
+ * The uniform Form* label: required-by-default asterisk and the optional
16
+ * dotted-underline hint tooltip. A single inline wrapper keeps the label's
17
+ * flex gap from ever separating the asterisk from the text.
18
+ */
19
+ declare function FormFieldLabel({ htmlFor, label, optional, labelHint }: FormFieldLabelProps): react.JSX.Element;
20
+ /**
21
+ * Spinner overlay for a control whose value is being seeded asynchronously.
22
+ * Render inside the control's `nxui:relative` wrapper; pair with disabling the
23
+ * control itself.
24
+ */
25
+ declare function ControlLoadingOverlay(): react.JSX.Element;
26
+
27
+ export { ControlLoadingOverlay, FormFieldLabel };
@@ -0,0 +1,27 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { FieldLabel } from "./field.js";
3
+ import { SpinnerPrimitive } from "./spinner.js";
4
+ import { TooltipContent, TooltipRoot, TooltipTrigger } from "./tooltip.js";
5
+ function FormFieldLabel({ htmlFor, label, optional, labelHint }) {
6
+ return /* @__PURE__ */ jsx(FieldLabel, { htmlFor, children: /* @__PURE__ */ jsxs("span", { children: [
7
+ labelHint ? /* @__PURE__ */ jsxs(TooltipRoot, { children: [
8
+ /* @__PURE__ */ jsx(
9
+ TooltipTrigger,
10
+ {
11
+ render: /* @__PURE__ */ jsx("span", { className: "nxui:underline nxui:decoration-dotted nxui:underline-offset-4" }),
12
+ children: label
13
+ }
14
+ ),
15
+ /* @__PURE__ */ jsx(TooltipContent, { side: "bottom", className: "nxui:max-w-xs nxui:text-left", children: labelHint })
16
+ ] }) : label,
17
+ !optional && " *"
18
+ ] }) });
19
+ }
20
+ function ControlLoadingOverlay() {
21
+ return /* @__PURE__ */ jsx("div", { className: "nxui:absolute nxui:inset-0 nxui:m-0.5 nxui:flex nxui:items-center nxui:justify-center nxui:bg-background", children: /* @__PURE__ */ jsx(SpinnerPrimitive, {}) });
22
+ }
23
+ export {
24
+ ControlLoadingOverlay,
25
+ FormFieldLabel
26
+ };
27
+ //# sourceMappingURL=form-field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/primitives/form-field.tsx"],"sourcesContent":["import { FieldLabel } from \"./field.js\"\nimport { SpinnerPrimitive } from \"./spinner.js\"\nimport { TooltipContent, TooltipRoot, TooltipTrigger } from \"./tooltip.js\"\n\n/**\n * Shared pieces of the Form* input skeleton, so every input in the family has\n * an identical label (asterisk, hint) and loading treatment without each\n * component re-implementing them.\n */\n\ninterface FormFieldLabelProps {\n htmlFor: string\n label: string\n optional: boolean\n labelHint?: string\n}\n\n/**\n * The uniform Form* label: required-by-default asterisk and the optional\n * dotted-underline hint tooltip. A single inline wrapper keeps the label's\n * flex gap from ever separating the asterisk from the text.\n */\nfunction FormFieldLabel({ htmlFor, label, optional, labelHint }: FormFieldLabelProps) {\n return (\n <FieldLabel htmlFor={htmlFor}>\n <span>\n {labelHint ? (\n <TooltipRoot>\n <TooltipTrigger\n render={\n <span className=\"nxui:underline nxui:decoration-dotted nxui:underline-offset-4\" />\n }\n >\n {label}\n </TooltipTrigger>\n <TooltipContent side=\"bottom\" className=\"nxui:max-w-xs nxui:text-left\">\n {labelHint}\n </TooltipContent>\n </TooltipRoot>\n ) : (\n label\n )}\n {!optional && \" *\"}\n </span>\n </FieldLabel>\n )\n}\n\n/**\n * Spinner overlay for a control whose value is being seeded asynchronously.\n * Render inside the control's `nxui:relative` wrapper; pair with disabling the\n * control itself.\n */\nfunction ControlLoadingOverlay() {\n return (\n <div className=\"nxui:absolute nxui:inset-0 nxui:m-0.5 nxui:flex nxui:items-center nxui:justify-center nxui:bg-background\">\n <SpinnerPrimitive />\n </div>\n )\n}\n\nexport { FormFieldLabel, ControlLoadingOverlay }\n"],"mappings":"AA2BU,SAGM,KAHN;AA3BV,SAAS,kBAAkB;AAC3B,SAAS,wBAAwB;AACjC,SAAS,gBAAgB,aAAa,sBAAsB;AAoB5D,SAAS,eAAe,EAAE,SAAS,OAAO,UAAU,UAAU,GAAwB;AACpF,SACE,oBAAC,cAAW,SACV,+BAAC,UACE;AAAA,gBACC,qBAAC,eACC;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,QACE,oBAAC,UAAK,WAAU,iEAAgE;AAAA,UAGjF;AAAA;AAAA,MACH;AAAA,MACA,oBAAC,kBAAe,MAAK,UAAS,WAAU,gCACrC,qBACH;AAAA,OACF,IAEA;AAAA,IAED,CAAC,YAAY;AAAA,KAChB,GACF;AAEJ;AAOA,SAAS,wBAAwB;AAC/B,SACE,oBAAC,SAAI,WAAU,4GACb,8BAAC,oBAAiB,GACpB;AAEJ;","names":[]}
@@ -0,0 +1,19 @@
1
+ import * as class_variance_authority_types from 'class-variance-authority/types';
2
+ import * as react from 'react';
3
+ import { VariantProps } from 'class-variance-authority';
4
+
5
+ /**
6
+ * Internal input-group primitives (ported subset of nx-automations
7
+ * ui/input-group.tsx — the pieces the form inputs need: group shell, inline
8
+ * addons, text adornment, and the borderless inner input; kbd/button/textarea
9
+ * variants come with the components that need them).
10
+ */
11
+ declare function InputGroup({ className, ...props }: React.ComponentProps<"div">): react.JSX.Element;
12
+ declare const inputGroupAddonVariants: (props?: ({
13
+ align?: "inline-end" | "inline-start" | null | undefined;
14
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
15
+ declare function InputGroupAddon({ className, align, ...props }: React.ComponentProps<"div"> & VariantProps<typeof inputGroupAddonVariants>): react.JSX.Element;
16
+ declare function InputGroupText({ className, ...props }: React.ComponentProps<"span">): react.JSX.Element;
17
+ declare function InputGroupInput({ className, ...props }: React.ComponentProps<"input">): react.JSX.Element;
18
+
19
+ export { InputGroup, InputGroupAddon, InputGroupInput, InputGroupText };
@@ -0,0 +1,87 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { cva } from "class-variance-authority";
4
+ import { cn } from "../lib/cn.js";
5
+ import { InputPrimitive } from "./input.js";
6
+ function InputGroup({ className, ...props }) {
7
+ return /* @__PURE__ */ jsx(
8
+ "div",
9
+ {
10
+ "data-slot": "input-group",
11
+ role: "group",
12
+ className: cn(
13
+ "nxui:group/input-group nxui:relative nxui:flex nxui:h-control-md nxui:w-full nxui:min-w-0 nxui:items-center nxui:rounded-md nxui:border nxui:border-input nxui:bg-input/20 nxui:transition-colors nxui:outline-none nxui:has-[[data-slot=input-group-control]:focus-visible]:border-ring nxui:has-[[data-slot=input-group-control]:focus-visible]:ring-2 nxui:has-[[data-slot=input-group-control]:focus-visible]:ring-ring/30 nxui:has-[[data-slot][aria-invalid=true]]:border-destructive nxui:has-[[data-slot][aria-invalid=true]]:ring-2 nxui:has-[[data-slot][aria-invalid=true]]:ring-destructive/20 nxui:dark:bg-input/30 nxui:dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 nxui:has-[>[data-align=inline-end]]:[&>input]:pr-1.5 nxui:has-[>[data-align=inline-start]]:[&>input]:pl-1.5",
14
+ className
15
+ ),
16
+ ...props
17
+ }
18
+ );
19
+ }
20
+ const inputGroupAddonVariants = cva(
21
+ "nxui:flex nxui:h-auto nxui:cursor-text nxui:items-center nxui:justify-center nxui:gap-1 nxui:py-2 nxui:text-body/relaxed nxui:font-medium nxui:text-muted-foreground nxui:select-none nxui:group-data-[disabled=true]/input-group:opacity-50 nxui:[&>svg:not([class*=size-])]:size-3.5",
22
+ {
23
+ variants: {
24
+ align: {
25
+ "inline-start": "nxui:order-first nxui:pl-2",
26
+ "inline-end": "nxui:order-last nxui:pr-2"
27
+ }
28
+ },
29
+ defaultVariants: {
30
+ align: "inline-start"
31
+ }
32
+ }
33
+ );
34
+ function InputGroupAddon({
35
+ className,
36
+ align = "inline-start",
37
+ ...props
38
+ }) {
39
+ return /* @__PURE__ */ jsx(
40
+ "div",
41
+ {
42
+ role: "group",
43
+ "data-slot": "input-group-addon",
44
+ "data-align": align,
45
+ className: cn(inputGroupAddonVariants({ align }), className),
46
+ onClick: (e) => {
47
+ if (e.target.closest("button")) {
48
+ return;
49
+ }
50
+ e.currentTarget.parentElement?.querySelector("input")?.focus();
51
+ },
52
+ ...props
53
+ }
54
+ );
55
+ }
56
+ function InputGroupText({ className, ...props }) {
57
+ return /* @__PURE__ */ jsx(
58
+ "span",
59
+ {
60
+ className: cn(
61
+ "nxui:flex nxui:items-center nxui:gap-2 nxui:text-body/relaxed nxui:text-muted-foreground nxui:[&_svg]:pointer-events-none nxui:[&_svg:not([class*=size-])]:size-4",
62
+ className
63
+ ),
64
+ ...props
65
+ }
66
+ );
67
+ }
68
+ function InputGroupInput({ className, ...props }) {
69
+ return /* @__PURE__ */ jsx(
70
+ InputPrimitive,
71
+ {
72
+ "data-slot": "input-group-control",
73
+ className: cn(
74
+ "nxui:flex-1 nxui:rounded-none nxui:border-0 nxui:bg-transparent nxui:shadow-none nxui:ring-0 nxui:focus-visible:ring-0 nxui:aria-invalid:ring-0 nxui:dark:bg-transparent",
75
+ className
76
+ ),
77
+ ...props
78
+ }
79
+ );
80
+ }
81
+ export {
82
+ InputGroup,
83
+ InputGroupAddon,
84
+ InputGroupInput,
85
+ InputGroupText
86
+ };
87
+ //# sourceMappingURL=input-group.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/primitives/input-group.tsx"],"sourcesContent":["\"use client\"\n\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"../lib/cn.js\"\nimport { InputPrimitive } from \"./input.js\"\n\n/**\n * Internal input-group primitives (ported subset of nx-automations\n * ui/input-group.tsx — the pieces the form inputs need: group shell, inline\n * addons, text adornment, and the borderless inner input; kbd/button/textarea\n * variants come with the components that need them).\n */\n\nfunction InputGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"input-group\"\n role=\"group\"\n className={cn(\n \"nxui:group/input-group nxui:relative nxui:flex nxui:h-control-md nxui:w-full nxui:min-w-0 nxui:items-center nxui:rounded-md nxui:border nxui:border-input nxui:bg-input/20 nxui:transition-colors nxui:outline-none nxui:has-[[data-slot=input-group-control]:focus-visible]:border-ring nxui:has-[[data-slot=input-group-control]:focus-visible]:ring-2 nxui:has-[[data-slot=input-group-control]:focus-visible]:ring-ring/30 nxui:has-[[data-slot][aria-invalid=true]]:border-destructive nxui:has-[[data-slot][aria-invalid=true]]:ring-2 nxui:has-[[data-slot][aria-invalid=true]]:ring-destructive/20 nxui:dark:bg-input/30 nxui:dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 nxui:has-[>[data-align=inline-end]]:[&>input]:pr-1.5 nxui:has-[>[data-align=inline-start]]:[&>input]:pl-1.5\",\n className\n )}\n {...props}\n />\n )\n}\n\nconst inputGroupAddonVariants = cva(\n \"nxui:flex nxui:h-auto nxui:cursor-text nxui:items-center nxui:justify-center nxui:gap-1 nxui:py-2 nxui:text-body/relaxed nxui:font-medium nxui:text-muted-foreground nxui:select-none nxui:group-data-[disabled=true]/input-group:opacity-50 nxui:[&>svg:not([class*=size-])]:size-3.5\",\n {\n variants: {\n align: {\n \"inline-start\": \"nxui:order-first nxui:pl-2\",\n \"inline-end\": \"nxui:order-last nxui:pr-2\",\n },\n },\n defaultVariants: {\n align: \"inline-start\",\n },\n }\n)\n\nfunction InputGroupAddon({\n className,\n align = \"inline-start\",\n ...props\n}: React.ComponentProps<\"div\"> & VariantProps<typeof inputGroupAddonVariants>) {\n return (\n <div\n role=\"group\"\n data-slot=\"input-group-addon\"\n data-align={align}\n className={cn(inputGroupAddonVariants({ align }), className)}\n onClick={(e) => {\n if ((e.target as HTMLElement).closest(\"button\")) {\n return\n }\n e.currentTarget.parentElement?.querySelector(\"input\")?.focus()\n }}\n {...props}\n />\n )\n}\n\nfunction InputGroupText({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n className={cn(\n \"nxui:flex nxui:items-center nxui:gap-2 nxui:text-body/relaxed nxui:text-muted-foreground nxui:[&_svg]:pointer-events-none nxui:[&_svg:not([class*=size-])]:size-4\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction InputGroupInput({ className, ...props }: React.ComponentProps<\"input\">) {\n return (\n <InputPrimitive\n data-slot=\"input-group-control\"\n className={cn(\n \"nxui:flex-1 nxui:rounded-none nxui:border-0 nxui:bg-transparent nxui:shadow-none nxui:ring-0 nxui:focus-visible:ring-0 nxui:aria-invalid:ring-0 nxui:dark:bg-transparent\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport { InputGroup, InputGroupAddon, InputGroupText, InputGroupInput }\n"],"mappings":";AAgBI;AAdJ,SAAS,WAA8B;AAEvC,SAAS,UAAU;AACnB,SAAS,sBAAsB;AAS/B,SAAS,WAAW,EAAE,WAAW,GAAG,MAAM,GAAgC;AACxE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,MAAK;AAAA,MACL,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,MAAM,0BAA0B;AAAA,EAC9B;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,OAAO;AAAA,QACL,gBAAgB;AAAA,QAChB,cAAc;AAAA,MAChB;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,OAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,SAAS,gBAAgB;AAAA,EACvB;AAAA,EACA,QAAQ;AAAA,EACR,GAAG;AACL,GAA+E;AAC7E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,aAAU;AAAA,MACV,cAAY;AAAA,MACZ,WAAW,GAAG,wBAAwB,EAAE,MAAM,CAAC,GAAG,SAAS;AAAA,MAC3D,SAAS,CAAC,MAAM;AACd,YAAK,EAAE,OAAuB,QAAQ,QAAQ,GAAG;AAC/C;AAAA,QACF;AACA,UAAE,cAAc,eAAe,cAAc,OAAO,GAAG,MAAM;AAAA,MAC/D;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,eAAe,EAAE,WAAW,GAAG,MAAM,GAAiC;AAC7E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,gBAAgB,EAAE,WAAW,GAAG,MAAM,GAAkC;AAC/E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
@@ -0,0 +1,11 @@
1
+ import * as react from 'react';
2
+
3
+ /**
4
+ * Internal input primitive (ported from nx-automations ui/input.tsx).
5
+ * Density-tokenized: h-7 → h-control-md, text sizes → body tokens. The larger
6
+ * mobile text size (text-body-lg below md) mirrors the source's text-sm and
7
+ * keeps mobile browsers from zooming on focus.
8
+ */
9
+ declare function InputPrimitive({ className, type, ...props }: React.ComponentProps<"input">): react.JSX.Element;
10
+
11
+ export { InputPrimitive };
@@ -0,0 +1,22 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { Input as InputPrimitiveBase } from "@base-ui/react/input";
4
+ import { cn } from "../lib/cn.js";
5
+ function InputPrimitive({ className, type, ...props }) {
6
+ return /* @__PURE__ */ jsx(
7
+ InputPrimitiveBase,
8
+ {
9
+ type,
10
+ "data-slot": "input",
11
+ className: cn(
12
+ "nxui:h-control-md nxui:w-full nxui:min-w-0 nxui:rounded-md nxui:border nxui:border-input nxui:bg-input/20 nxui:px-2 nxui:py-0.5 nxui:text-body-lg nxui:transition-colors nxui:outline-none nxui:file:inline-flex nxui:file:h-control-sm nxui:file:border-0 nxui:file:bg-transparent nxui:file:text-body/relaxed nxui:file:font-medium nxui:file:text-foreground nxui:placeholder:text-muted-foreground nxui:focus-visible:border-ring nxui:focus-visible:ring-2 nxui:focus-visible:ring-ring/30 nxui:disabled:pointer-events-none nxui:disabled:cursor-not-allowed nxui:disabled:opacity-50 nxui:aria-invalid:border-destructive nxui:aria-invalid:ring-2 nxui:aria-invalid:ring-destructive/20 nxui:md:text-body/relaxed nxui:dark:bg-input/30 nxui:dark:aria-invalid:border-destructive/50 nxui:dark:aria-invalid:ring-destructive/40",
13
+ className
14
+ ),
15
+ ...props
16
+ }
17
+ );
18
+ }
19
+ export {
20
+ InputPrimitive
21
+ };
22
+ //# sourceMappingURL=input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/primitives/input.tsx"],"sourcesContent":["\"use client\"\n\nimport { Input as InputPrimitiveBase } from \"@base-ui/react/input\"\n\nimport { cn } from \"../lib/cn.js\"\n\n/**\n * Internal input primitive (ported from nx-automations ui/input.tsx).\n * Density-tokenized: h-7 → h-control-md, text sizes → body tokens. The larger\n * mobile text size (text-body-lg below md) mirrors the source's text-sm and\n * keeps mobile browsers from zooming on focus.\n */\nfunction InputPrimitive({ className, type, ...props }: React.ComponentProps<\"input\">) {\n return (\n <InputPrimitiveBase\n type={type}\n data-slot=\"input\"\n className={cn(\n \"nxui:h-control-md nxui:w-full nxui:min-w-0 nxui:rounded-md nxui:border nxui:border-input nxui:bg-input/20 nxui:px-2 nxui:py-0.5 nxui:text-body-lg nxui:transition-colors nxui:outline-none nxui:file:inline-flex nxui:file:h-control-sm nxui:file:border-0 nxui:file:bg-transparent nxui:file:text-body/relaxed nxui:file:font-medium nxui:file:text-foreground nxui:placeholder:text-muted-foreground nxui:focus-visible:border-ring nxui:focus-visible:ring-2 nxui:focus-visible:ring-ring/30 nxui:disabled:pointer-events-none nxui:disabled:cursor-not-allowed nxui:disabled:opacity-50 nxui:aria-invalid:border-destructive nxui:aria-invalid:ring-2 nxui:aria-invalid:ring-destructive/20 nxui:md:text-body/relaxed nxui:dark:bg-input/30 nxui:dark:aria-invalid:border-destructive/50 nxui:dark:aria-invalid:ring-destructive/40\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport { InputPrimitive }\n"],"mappings":";AAcI;AAZJ,SAAS,SAAS,0BAA0B;AAE5C,SAAS,UAAU;AAQnB,SAAS,eAAe,EAAE,WAAW,MAAM,GAAG,MAAM,GAAkC;AACpF,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
@@ -0,0 +1,6 @@
1
+ import * as react from 'react';
2
+
3
+ /** Internal label primitive (ported from nx-automations ui/label.tsx). */
4
+ declare function LabelPrimitive({ className, ...props }: React.ComponentProps<"label">): react.JSX.Element;
5
+
6
+ export { LabelPrimitive };
@@ -0,0 +1,19 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { cn } from "../lib/cn.js";
3
+ function LabelPrimitive({ className, ...props }) {
4
+ return /* @__PURE__ */ jsx(
5
+ "label",
6
+ {
7
+ "data-slot": "label",
8
+ className: cn(
9
+ "nxui:flex nxui:items-center nxui:gap-2 nxui:text-body/relaxed nxui:leading-none nxui:font-medium nxui:select-none nxui:group-data-[disabled=true]:pointer-events-none nxui:group-data-[disabled=true]:opacity-50 nxui:peer-disabled:cursor-not-allowed nxui:peer-disabled:opacity-50",
10
+ className
11
+ ),
12
+ ...props
13
+ }
14
+ );
15
+ }
16
+ export {
17
+ LabelPrimitive
18
+ };
19
+ //# sourceMappingURL=label.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/primitives/label.tsx"],"sourcesContent":["import { cn } from \"../lib/cn.js\"\n\n/** Internal label primitive (ported from nx-automations ui/label.tsx). */\nfunction LabelPrimitive({ className, ...props }: React.ComponentProps<\"label\">) {\n return (\n <label\n data-slot=\"label\"\n className={cn(\n \"nxui:flex nxui:items-center nxui:gap-2 nxui:text-body/relaxed nxui:leading-none nxui:font-medium nxui:select-none nxui:group-data-[disabled=true]:pointer-events-none nxui:group-data-[disabled=true]:opacity-50 nxui:peer-disabled:cursor-not-allowed nxui:peer-disabled:opacity-50\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport { LabelPrimitive }\n"],"mappings":"AAKI;AALJ,SAAS,UAAU;AAGnB,SAAS,eAAe,EAAE,WAAW,GAAG,MAAM,GAAkC;AAC9E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}