@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.
- package/dist/cli/index.js +4 -0
- package/dist/components/button/button.d.ts +18 -0
- package/dist/components/button/button.js +40 -0
- package/dist/components/button/button.js.map +1 -0
- package/dist/components/button/button.types.d.ts +36 -0
- package/dist/components/button/button.types.js +1 -0
- package/dist/components/button/button.types.js.map +1 -0
- package/dist/components/button/button.variants.d.ts +32 -0
- package/dist/components/button/button.variants.js +45 -0
- package/dist/components/button/button.variants.js.map +1 -0
- package/dist/components/button/index.d.ts +8 -0
- package/dist/components/button/index.js +5 -0
- package/dist/components/button/index.js.map +1 -0
- package/dist/components/form-currency-input/form-currency-input.d.ts +12 -0
- package/dist/components/form-currency-input/form-currency-input.js +62 -0
- package/dist/components/form-currency-input/form-currency-input.js.map +1 -0
- package/dist/components/form-currency-input/form-currency-input.types.d.ts +32 -0
- package/dist/components/form-currency-input/form-currency-input.types.js +1 -0
- package/dist/components/form-currency-input/form-currency-input.types.js.map +1 -0
- package/dist/components/form-currency-input/index.d.ts +4 -0
- package/dist/components/form-currency-input/index.js +5 -0
- package/dist/components/form-currency-input/index.js.map +1 -0
- package/dist/components/form-number-input/form-number-input.d.ts +13 -0
- package/dist/components/form-number-input/form-number-input.js +75 -0
- package/dist/components/form-number-input/form-number-input.js.map +1 -0
- package/dist/components/form-number-input/form-number-input.types.d.ts +34 -0
- package/dist/components/form-number-input/form-number-input.types.js +1 -0
- package/dist/components/form-number-input/form-number-input.types.js.map +1 -0
- package/dist/components/form-number-input/index.d.ts +4 -0
- package/dist/components/form-number-input/index.js +5 -0
- package/dist/components/form-number-input/index.js.map +1 -0
- package/dist/components/form-percentage-input/form-percentage-input.d.ts +12 -0
- package/dist/components/form-percentage-input/form-percentage-input.js +65 -0
- package/dist/components/form-percentage-input/form-percentage-input.js.map +1 -0
- package/dist/components/form-percentage-input/form-percentage-input.types.d.ts +33 -0
- package/dist/components/form-percentage-input/form-percentage-input.types.js +1 -0
- package/dist/components/form-percentage-input/form-percentage-input.types.js.map +1 -0
- package/dist/components/form-percentage-input/index.d.ts +4 -0
- package/dist/components/form-percentage-input/index.js +5 -0
- package/dist/components/form-percentage-input/index.js.map +1 -0
- package/dist/components/form-text-input/form-text-input.d.ts +19 -0
- package/dist/components/form-text-input/form-text-input.js +64 -0
- package/dist/components/form-text-input/form-text-input.js.map +1 -0
- package/dist/components/form-text-input/form-text-input.types.d.ts +45 -0
- package/dist/components/form-text-input/form-text-input.types.js +1 -0
- package/dist/components/form-text-input/form-text-input.types.js.map +1 -0
- package/dist/components/form-text-input/index.d.ts +4 -0
- package/dist/components/form-text-input/index.js +5 -0
- package/dist/components/form-text-input/index.js.map +1 -0
- package/dist/components/icon-button/icon-button.d.ts +20 -0
- package/dist/components/icon-button/icon-button.js +32 -0
- package/dist/components/icon-button/icon-button.js.map +1 -0
- package/dist/components/icon-button/icon-button.types.d.ts +24 -0
- package/dist/components/icon-button/icon-button.types.js +1 -0
- package/dist/components/icon-button/icon-button.types.js.map +1 -0
- package/dist/components/icon-button/index.d.ts +8 -0
- package/dist/components/icon-button/index.js +5 -0
- package/dist/components/icon-button/index.js.map +1 -0
- package/dist/components/spinner/index.d.ts +2 -0
- package/dist/components/spinner/index.js +5 -0
- package/dist/components/spinner/index.js.map +1 -0
- package/dist/components/spinner/spinner.d.ts +15 -0
- package/dist/components/spinner/spinner.js +9 -0
- package/dist/components/spinner/spinner.js.map +1 -0
- package/dist/components/toast/index.d.ts +3 -0
- package/dist/components/toast/index.js +8 -0
- package/dist/components/toast/index.js.map +1 -0
- package/dist/components/toast/toast.d.ts +30 -0
- package/dist/components/toast/toast.js +75 -0
- package/dist/components/toast/toast.js.map +1 -0
- package/dist/components/toast/toast.types.d.ts +22 -0
- package/dist/components/toast/toast.types.js +1 -0
- package/dist/components/toast/toast.types.js.map +1 -0
- package/dist/components/toast/toast.variants.d.ts +23 -0
- package/dist/components/toast/toast.variants.js +19 -0
- package/dist/components/toast/toast.variants.js.map +1 -0
- package/dist/hooks/use-number-mask.d.ts +24 -0
- package/dist/hooks/use-number-mask.js +59 -0
- package/dist/hooks/use-number-mask.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/cn.d.ts +10 -0
- package/dist/lib/cn.js +8 -0
- package/dist/lib/cn.js.map +1 -0
- package/dist/lib/format/number.d.ts +6 -0
- package/dist/lib/format/number.js +26 -0
- package/dist/lib/format/number.js.map +1 -0
- package/dist/lib/format/percent.d.ts +15 -0
- package/dist/lib/format/percent.js +7 -0
- package/dist/lib/format/percent.js.map +1 -0
- package/dist/lib/sealed.d.ts +14 -0
- package/dist/lib/sealed.js +1 -0
- package/dist/lib/sealed.js.map +1 -0
- package/dist/primitives/field.d.ts +17 -0
- package/dist/primitives/field.js +74 -0
- package/dist/primitives/field.js.map +1 -0
- package/dist/primitives/form-field.d.ts +27 -0
- package/dist/primitives/form-field.js +27 -0
- package/dist/primitives/form-field.js.map +1 -0
- package/dist/primitives/input-group.d.ts +19 -0
- package/dist/primitives/input-group.js +87 -0
- package/dist/primitives/input-group.js.map +1 -0
- package/dist/primitives/input.d.ts +11 -0
- package/dist/primitives/input.js +22 -0
- package/dist/primitives/input.js.map +1 -0
- package/dist/primitives/label.d.ts +6 -0
- package/dist/primitives/label.js +19 -0
- package/dist/primitives/label.js.map +1 -0
- package/dist/primitives/spinner.d.ts +10 -0
- package/dist/primitives/spinner.js +19 -0
- package/dist/primitives/spinner.js.map +1 -0
- package/dist/primitives/toast.d.ts +31 -0
- package/dist/primitives/toast.js +102 -0
- package/dist/primitives/toast.js.map +1 -0
- package/dist/primitives/tooltip.d.ts +17 -0
- package/dist/primitives/tooltip.js +51 -0
- package/dist/primitives/tooltip.js.map +1 -0
- package/dist/styles.css +2 -0
- package/package.json +70 -3
- 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":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -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":[]}
|
package/dist/lib/cn.d.ts
ADDED
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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,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":[]}
|