@epilot/spark-ui 0.17.0 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/autocomplete.cjs +5 -6
- package/dist/components/autocomplete.cjs.map +1 -1
- package/dist/components/autocomplete.d.cts +1 -4
- package/dist/components/autocomplete.d.ts +1 -4
- package/dist/components/autocomplete.js +5 -6
- package/dist/components/autocomplete.js.map +1 -1
- package/dist/components/avatar.js +1 -1
- package/dist/components/badge.js +1 -1
- package/dist/components/box.js +1 -1
- package/dist/components/bubble.d.cts +1 -1
- package/dist/components/bubble.d.ts +1 -1
- package/dist/components/button-group.js +1 -1
- package/dist/components/button.d.cts +1 -1
- package/dist/components/button.d.ts +1 -1
- package/dist/components/button.js +1 -1
- package/dist/components/callout.d.cts +1 -1
- package/dist/components/callout.d.ts +1 -1
- package/dist/components/card.js +1 -1
- package/dist/components/checkbox.js +1 -1
- package/dist/components/icon.js +1 -1
- package/dist/components/info-block.d.cts +1 -1
- package/dist/components/info-block.d.ts +1 -1
- package/dist/components/input-date.cjs +141 -0
- package/dist/components/input-date.cjs.map +1 -0
- package/dist/components/input-date.d.cts +22 -0
- package/dist/components/input-date.d.ts +22 -0
- package/dist/components/input-date.js +138 -0
- package/dist/components/input-date.js.map +1 -0
- package/dist/components/input-otp.cjs +1 -1
- package/dist/components/input-otp.js +1 -1
- package/dist/components/input.cjs +1 -1
- package/dist/components/input.js +1 -1
- package/dist/components/message-scroller.cjs +1 -1
- package/dist/components/message-scroller.js +1 -1
- package/dist/components/page-header.d.cts +1 -1
- package/dist/components/page-header.d.ts +1 -1
- package/dist/components/pill.cjs +2 -2
- package/dist/components/pill.cjs.map +1 -1
- package/dist/components/pill.js +3 -3
- package/dist/components/pill.js.map +1 -1
- package/dist/components/radio.js +1 -1
- package/dist/components/select.cjs +1 -1
- package/dist/components/select.js +1 -1
- package/dist/components/switch.js +1 -1
- package/dist/components/textarea.cjs +1 -1
- package/dist/components/textarea.js +1 -1
- package/dist/components/theme.js +1 -1
- package/dist/styles/spark-default.css +4 -0
- package/package.json +2 -1
- package/dist/utils/date-mask.cjs +0 -45
- package/dist/utils/date-mask.cjs.map +0 -1
- package/dist/utils/date-mask.d.cts +0 -17
- package/dist/utils/date-mask.d.ts +0 -17
- package/dist/utils/date-mask.js +0 -41
- package/dist/utils/date-mask.js.map +0 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
import { cn } from "../utils/cn.js";
|
|
4
|
+
import { Button } from "./button.js";
|
|
5
|
+
import { InputGroup, InputGroupAddon, InputGroupInput } from "./input-group.js";
|
|
6
|
+
import { Calendar } from "./calendar.js";
|
|
7
|
+
import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "./popover.js";
|
|
8
|
+
import * as React$1 from "react";
|
|
9
|
+
import { CalendarMonth } from "@epilot/spark-icons";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
import { apply, formatDate, isDateMaskKey, parseDate } from "ictus";
|
|
12
|
+
//#region src/components/input-date.tsx
|
|
13
|
+
function useControllableState({ defaultProp, onChange, prop }) {
|
|
14
|
+
const [uncontrolled, setUncontrolled] = React$1.useState(defaultProp);
|
|
15
|
+
const isControlled = prop !== void 0;
|
|
16
|
+
return [isControlled ? prop : uncontrolled, React$1.useCallback((next) => {
|
|
17
|
+
if (!isControlled) setUncontrolled(next);
|
|
18
|
+
onChange?.(next);
|
|
19
|
+
}, [isControlled, onChange])];
|
|
20
|
+
}
|
|
21
|
+
const InputDate = React$1.forwardRef(function InputDate({ calendarLabel, calendarProps, className, defaultOpen = false, defaultValue = "", disabled, id, onDateChange, onKeyDown, onOpenChange, onValueChange, open: openProp, placeholder, readOnly, separator = ".", value: valueProp, ...props }, ref) {
|
|
22
|
+
const inputRef = React$1.useRef(null);
|
|
23
|
+
const [text, setText] = useControllableState({
|
|
24
|
+
defaultProp: defaultValue,
|
|
25
|
+
onChange: onValueChange,
|
|
26
|
+
prop: valueProp
|
|
27
|
+
});
|
|
28
|
+
const [open, setOpenState] = useControllableState({
|
|
29
|
+
defaultProp: defaultOpen,
|
|
30
|
+
onChange: onOpenChange,
|
|
31
|
+
prop: openProp
|
|
32
|
+
});
|
|
33
|
+
const selected = parseDate(text);
|
|
34
|
+
const [month, setMonth] = React$1.useState(() => selected ?? calendarProps?.month);
|
|
35
|
+
const setOpen = React$1.useCallback((next) => {
|
|
36
|
+
if (next && calendarProps?.month === void 0) setMonth(parseDate(text) ?? /* @__PURE__ */ new Date());
|
|
37
|
+
setOpenState(next);
|
|
38
|
+
}, [
|
|
39
|
+
calendarProps?.month,
|
|
40
|
+
setOpenState,
|
|
41
|
+
text
|
|
42
|
+
]);
|
|
43
|
+
const commit = React$1.useCallback((next) => {
|
|
44
|
+
if (next === text) return;
|
|
45
|
+
setText(next);
|
|
46
|
+
onDateChange?.(parseDate(next));
|
|
47
|
+
}, [
|
|
48
|
+
onDateChange,
|
|
49
|
+
setText,
|
|
50
|
+
text
|
|
51
|
+
]);
|
|
52
|
+
const setInputRef = React$1.useCallback((node) => {
|
|
53
|
+
inputRef.current = node;
|
|
54
|
+
if (typeof ref === "function") ref(node);
|
|
55
|
+
else if (ref) ref.current = node;
|
|
56
|
+
}, [ref]);
|
|
57
|
+
return /* @__PURE__ */ jsxs(Popover, {
|
|
58
|
+
onOpenChange: setOpen,
|
|
59
|
+
open,
|
|
60
|
+
children: [/* @__PURE__ */ jsxs(PopoverAnchor, {
|
|
61
|
+
render: /* @__PURE__ */ jsx(InputGroup, { className }),
|
|
62
|
+
children: [/* @__PURE__ */ jsx(InputGroupInput, {
|
|
63
|
+
autoComplete: "off",
|
|
64
|
+
...props,
|
|
65
|
+
className: cn(readOnly && "cursor-pointer"),
|
|
66
|
+
disabled,
|
|
67
|
+
id,
|
|
68
|
+
inputMode: readOnly ? void 0 : "numeric",
|
|
69
|
+
onChange: () => {},
|
|
70
|
+
onClick: readOnly && !disabled ? (event) => {
|
|
71
|
+
props.onClick?.(event);
|
|
72
|
+
if (!event.defaultPrevented) setOpen(true);
|
|
73
|
+
} : props.onClick,
|
|
74
|
+
onKeyDown: (event) => {
|
|
75
|
+
onKeyDown?.(event);
|
|
76
|
+
if (event.defaultPrevented) return;
|
|
77
|
+
if (event.key === "ArrowDown") {
|
|
78
|
+
event.preventDefault();
|
|
79
|
+
setOpen(true);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (readOnly || !isDateMaskKey(event.key)) return;
|
|
83
|
+
event.preventDefault();
|
|
84
|
+
const next = apply({
|
|
85
|
+
caret: event.currentTarget.selectionStart ?? 0,
|
|
86
|
+
key: event.key,
|
|
87
|
+
selectionEnd: event.currentTarget.selectionEnd ?? void 0,
|
|
88
|
+
separator,
|
|
89
|
+
value: event.currentTarget.value
|
|
90
|
+
});
|
|
91
|
+
commit(next.value);
|
|
92
|
+
const caret = next.caret;
|
|
93
|
+
const input = inputRef.current;
|
|
94
|
+
requestAnimationFrame(() => {
|
|
95
|
+
input?.setSelectionRange(caret, caret);
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
placeholder: placeholder ?? `DD${separator}MM${separator}YYYY`,
|
|
99
|
+
readOnly,
|
|
100
|
+
ref: setInputRef,
|
|
101
|
+
spellCheck: false,
|
|
102
|
+
value: text
|
|
103
|
+
}), /* @__PURE__ */ jsx(InputGroupAddon, {
|
|
104
|
+
align: "inline-end",
|
|
105
|
+
children: /* @__PURE__ */ jsx(PopoverTrigger, { render: /* @__PURE__ */ jsx(Button, {
|
|
106
|
+
"aria-label": calendarLabel,
|
|
107
|
+
color: "neutral",
|
|
108
|
+
disabled,
|
|
109
|
+
size: "icon-small",
|
|
110
|
+
styleVariant: "ghost",
|
|
111
|
+
children: /* @__PURE__ */ jsx(CalendarMonth, {})
|
|
112
|
+
}) })
|
|
113
|
+
})]
|
|
114
|
+
}), /* @__PURE__ */ jsx(PopoverContent, {
|
|
115
|
+
align: "start",
|
|
116
|
+
"aria-label": calendarLabel,
|
|
117
|
+
className: "w-auto p-0",
|
|
118
|
+
children: /* @__PURE__ */ jsx(Calendar, {
|
|
119
|
+
...calendarProps,
|
|
120
|
+
mode: "single",
|
|
121
|
+
month: calendarProps?.month ?? month,
|
|
122
|
+
onMonthChange: (next) => {
|
|
123
|
+
calendarProps?.onMonthChange?.(next);
|
|
124
|
+
if (calendarProps?.month === void 0) setMonth(next);
|
|
125
|
+
},
|
|
126
|
+
onSelect: (next) => {
|
|
127
|
+
commit(next ? formatDate(next, separator) : "");
|
|
128
|
+
setOpen(false);
|
|
129
|
+
},
|
|
130
|
+
selected
|
|
131
|
+
})
|
|
132
|
+
})]
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
//#endregion
|
|
136
|
+
export { InputDate };
|
|
137
|
+
|
|
138
|
+
//# sourceMappingURL=input-date.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-date.js","names":["React"],"sources":["../../src/components/input-date.tsx"],"sourcesContent":["'use client'\n\nimport { CalendarMonth } from '@epilot/spark-icons'\nimport { apply, formatDate, isDateMaskKey, parseDate } from 'ictus'\nimport * as React from 'react'\nimport { cn } from '../utils/cn'\nimport { Button } from './button'\nimport { Calendar, type CalendarProps } from './calendar'\nimport { InputGroup, InputGroupAddon, InputGroupInput } from './input-group'\nimport {\n Popover,\n PopoverAnchor,\n PopoverContent,\n PopoverTrigger,\n} from './popover'\n\nfunction useControllableState<T>({\n defaultProp,\n onChange,\n prop,\n}: {\n defaultProp: T\n onChange?: (value: T) => void\n prop?: T\n}) {\n const [uncontrolled, setUncontrolled] = React.useState(defaultProp)\n const isControlled = prop !== undefined\n const value = isControlled ? prop : uncontrolled\n\n const setValue = React.useCallback(\n (next: T) => {\n if (!isControlled) setUncontrolled(next)\n onChange?.(next)\n },\n [isControlled, onChange],\n )\n\n return [value, setValue] as const\n}\n\nexport type InputDateProps = Omit<\n React.ComponentProps<'input'>,\n 'children' | 'defaultValue' | 'onChange' | 'type' | 'value'\n> & {\n calendarLabel: string\n calendarProps?: Omit<CalendarProps, 'mode' | 'onSelect' | 'selected'>\n defaultOpen?: boolean\n defaultValue?: string\n onDateChange?: (date: Date | undefined) => void\n onOpenChange?: (open: boolean) => void\n onValueChange?: (value: string) => void\n open?: boolean\n separator?: string\n value?: string\n}\n\nconst InputDate = React.forwardRef<HTMLInputElement, InputDateProps>(\n function InputDate(\n {\n calendarLabel,\n calendarProps,\n className,\n defaultOpen = false,\n defaultValue = '',\n disabled,\n id,\n onDateChange,\n onKeyDown,\n onOpenChange,\n onValueChange,\n open: openProp,\n placeholder,\n readOnly,\n separator = '.',\n value: valueProp,\n ...props\n },\n ref,\n ) {\n const inputRef = React.useRef<HTMLInputElement>(null)\n const [text, setText] = useControllableState({\n defaultProp: defaultValue,\n onChange: onValueChange,\n prop: valueProp,\n })\n const [open, setOpenState] = useControllableState({\n defaultProp: defaultOpen,\n onChange: onOpenChange,\n prop: openProp,\n })\n const selected = parseDate(text)\n const [month, setMonth] = React.useState<Date | undefined>(\n () => selected ?? calendarProps?.month,\n )\n const setOpen = React.useCallback(\n (next: boolean) => {\n if (next && calendarProps?.month === undefined) {\n setMonth(parseDate(text) ?? new Date())\n }\n setOpenState(next)\n },\n [calendarProps?.month, setOpenState, text],\n )\n\n const commit = React.useCallback(\n (next: string) => {\n if (next === text) return\n setText(next)\n onDateChange?.(parseDate(next))\n },\n [onDateChange, setText, text],\n )\n\n const setInputRef = React.useCallback(\n (node: HTMLInputElement | null) => {\n inputRef.current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) ref.current = node\n },\n [ref],\n )\n\n return (\n <Popover onOpenChange={setOpen} open={open}>\n <PopoverAnchor render={<InputGroup className={className} />}>\n <InputGroupInput\n autoComplete=\"off\"\n {...props}\n className={cn(readOnly && 'cursor-pointer')}\n disabled={disabled}\n id={id}\n inputMode={readOnly ? undefined : 'numeric'}\n onChange={() => {}}\n onClick={\n readOnly && !disabled\n ? (event) => {\n props.onClick?.(event)\n if (!event.defaultPrevented) setOpen(true)\n }\n : props.onClick\n }\n onKeyDown={(event) => {\n onKeyDown?.(event)\n if (event.defaultPrevented) return\n if (event.key === 'ArrowDown') {\n event.preventDefault()\n setOpen(true)\n return\n }\n if (readOnly || !isDateMaskKey(event.key)) return\n event.preventDefault()\n const next = apply({\n caret: event.currentTarget.selectionStart ?? 0,\n key: event.key,\n selectionEnd: event.currentTarget.selectionEnd ?? undefined,\n separator,\n value: event.currentTarget.value,\n })\n commit(next.value)\n const caret = next.caret\n const input = inputRef.current\n requestAnimationFrame(() => {\n input?.setSelectionRange(caret, caret)\n })\n }}\n placeholder={placeholder ?? `DD${separator}MM${separator}YYYY`}\n readOnly={readOnly}\n ref={setInputRef}\n spellCheck={false}\n value={text}\n />\n <InputGroupAddon align=\"inline-end\">\n <PopoverTrigger\n render={\n <Button\n aria-label={calendarLabel}\n color=\"neutral\"\n disabled={disabled}\n size=\"icon-small\"\n styleVariant=\"ghost\"\n >\n <CalendarMonth />\n </Button>\n }\n />\n </InputGroupAddon>\n </PopoverAnchor>\n <PopoverContent\n align=\"start\"\n aria-label={calendarLabel}\n className=\"w-auto p-0\"\n >\n <Calendar\n {...calendarProps}\n mode=\"single\"\n month={calendarProps?.month ?? month}\n onMonthChange={(next) => {\n calendarProps?.onMonthChange?.(next)\n if (calendarProps?.month === undefined) setMonth(next)\n }}\n onSelect={(next) => {\n commit(next ? formatDate(next, separator) : '')\n setOpen(false)\n }}\n selected={selected}\n />\n </PopoverContent>\n </Popover>\n )\n },\n)\n\nexport { InputDate }\n"],"mappings":";;;;;;;;;;;;AAgBA,SAAS,qBAAwB,EAC/B,aACA,UACA,QAKC;CACD,MAAM,CAAC,cAAc,mBAAmBA,QAAM,SAAS,WAAW;CAClE,MAAM,eAAe,SAAS,KAAA;CAW9B,OAAO,CAVO,eAAe,OAAO,cAEnBA,QAAM,aACpB,SAAY;EACX,IAAI,CAAC,cAAc,gBAAgB,IAAI;EACvC,WAAW,IAAI;CACjB,GACA,CAAC,cAAc,QAAQ,CAGH,CAAC;AACzB;AAkBA,MAAM,YAAYA,QAAM,WACtB,SAAS,UACP,EACE,eACA,eACA,WACA,cAAc,OACd,eAAe,IACf,UACA,IACA,cACA,WACA,cACA,eACA,MAAM,UACN,aACA,UACA,YAAY,KACZ,OAAO,WACP,GAAG,SAEL,KACA;CACA,MAAM,WAAWA,QAAM,OAAyB,IAAI;CACpD,MAAM,CAAC,MAAM,WAAW,qBAAqB;EAC3C,aAAa;EACb,UAAU;EACV,MAAM;CACR,CAAC;CACD,MAAM,CAAC,MAAM,gBAAgB,qBAAqB;EAChD,aAAa;EACb,UAAU;EACV,MAAM;CACR,CAAC;CACD,MAAM,WAAW,UAAU,IAAI;CAC/B,MAAM,CAAC,OAAO,YAAYA,QAAM,eACxB,YAAY,eAAe,KACnC;CACA,MAAM,UAAUA,QAAM,aACnB,SAAkB;EACjB,IAAI,QAAQ,eAAe,UAAU,KAAA,GACnC,SAAS,UAAU,IAAI,qBAAK,IAAI,KAAK,CAAC;EAExC,aAAa,IAAI;CACnB,GACA;EAAC,eAAe;EAAO;EAAc;CAAI,CAC3C;CAEA,MAAM,SAASA,QAAM,aAClB,SAAiB;EAChB,IAAI,SAAS,MAAM;EACnB,QAAQ,IAAI;EACZ,eAAe,UAAU,IAAI,CAAC;CAChC,GACA;EAAC;EAAc;EAAS;CAAI,CAC9B;CAEA,MAAM,cAAcA,QAAM,aACvB,SAAkC;EACjC,SAAS,UAAU;EACnB,IAAI,OAAO,QAAQ,YAAY,IAAI,IAAI;OAClC,IAAI,KAAK,IAAI,UAAU;CAC9B,GACA,CAAC,GAAG,CACN;CAEA,OACE,qBAAC,SAAD;EAAS,cAAc;EAAe;YAAtC,CACE,qBAAC,eAAD;GAAe,QAAQ,oBAAC,YAAD,EAAuB,UAAY,CAAA;aAA1D,CACE,oBAAC,iBAAD;IACE,cAAa;IACb,GAAI;IACJ,WAAW,GAAG,YAAY,gBAAgB;IAChC;IACN;IACJ,WAAW,WAAW,KAAA,IAAY;IAClC,gBAAgB,CAAC;IACjB,SACE,YAAY,CAAC,YACR,UAAU;KACT,MAAM,UAAU,KAAK;KACrB,IAAI,CAAC,MAAM,kBAAkB,QAAQ,IAAI;IAC3C,IACA,MAAM;IAEZ,YAAY,UAAU;KACpB,YAAY,KAAK;KACjB,IAAI,MAAM,kBAAkB;KAC5B,IAAI,MAAM,QAAQ,aAAa;MAC7B,MAAM,eAAe;MACrB,QAAQ,IAAI;MACZ;KACF;KACA,IAAI,YAAY,CAAC,cAAc,MAAM,GAAG,GAAG;KAC3C,MAAM,eAAe;KACrB,MAAM,OAAO,MAAM;MACjB,OAAO,MAAM,cAAc,kBAAkB;MAC7C,KAAK,MAAM;MACX,cAAc,MAAM,cAAc,gBAAgB,KAAA;MAClD;MACA,OAAO,MAAM,cAAc;KAC7B,CAAC;KACD,OAAO,KAAK,KAAK;KACjB,MAAM,QAAQ,KAAK;KACnB,MAAM,QAAQ,SAAS;KACvB,4BAA4B;MAC1B,OAAO,kBAAkB,OAAO,KAAK;KACvC,CAAC;IACH;IACA,aAAa,eAAe,KAAK,UAAU,IAAI,UAAU;IAC/C;IACV,KAAK;IACL,YAAY;IACZ,OAAO;GACR,CAAA,GACD,oBAAC,iBAAD;IAAiB,OAAM;cACrB,oBAAC,gBAAD,EACE,QACE,oBAAC,QAAD;KACE,cAAY;KACZ,OAAM;KACI;KACV,MAAK;KACL,cAAa;eAEb,oBAAC,eAAD,CAAgB,CAAA;IACV,CAAA,EAEX,CAAA;GACc,CAAA,CACJ;MACf,oBAAC,gBAAD;GACE,OAAM;GACN,cAAY;GACZ,WAAU;aAEV,oBAAC,UAAD;IACE,GAAI;IACJ,MAAK;IACL,OAAO,eAAe,SAAS;IAC/B,gBAAgB,SAAS;KACvB,eAAe,gBAAgB,IAAI;KACnC,IAAI,eAAe,UAAU,KAAA,GAAW,SAAS,IAAI;IACvD;IACA,WAAW,SAAS;KAClB,OAAO,OAAO,WAAW,MAAM,SAAS,IAAI,EAAE;KAC9C,QAAQ,KAAK;IACf;IACU;GACX,CAAA;EACa,CAAA,CACT;;AAEb,CACF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const require_lib_input_box_variants = require("../lib/input-box-variants.cjs");
|
|
4
3
|
const require_components_field = require("./field.cjs");
|
|
4
|
+
const require_lib_input_box_variants = require("../lib/input-box-variants.cjs");
|
|
5
5
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
6
6
|
let class_variance_authority = require("class-variance-authority");
|
|
7
7
|
let _base_ui_react_otp_field = require("@base-ui/react/otp-field");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../utils/cn.js";
|
|
3
|
-
import { GHOST_UNDERLINE, boxChrome } from "../lib/input-box-variants.js";
|
|
4
3
|
import { useFieldControl } from "./field.js";
|
|
4
|
+
import { GHOST_UNDERLINE, boxChrome } from "../lib/input-box-variants.js";
|
|
5
5
|
import { jsx } from "react/jsx-runtime";
|
|
6
6
|
import { cva } from "class-variance-authority";
|
|
7
7
|
import { OTPField } from "@base-ui/react/otp-field";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const require_chunk = require("../chunk-DY84GLCh.cjs");
|
|
4
|
-
const require_lib_input_box_variants = require("../lib/input-box-variants.cjs");
|
|
5
4
|
const require_components_field = require("./field.cjs");
|
|
5
|
+
const require_lib_input_box_variants = require("../lib/input-box-variants.cjs");
|
|
6
6
|
let react = require("react");
|
|
7
7
|
react = require_chunk.__toESM(react);
|
|
8
8
|
let react_jsx_runtime = require("react/jsx-runtime");
|
package/dist/components/input.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../utils/cn.js";
|
|
3
|
-
import { GHOST_UNDERLINE, boxChrome } from "../lib/input-box-variants.js";
|
|
4
3
|
import { useFieldControl } from "./field.js";
|
|
4
|
+
import { GHOST_UNDERLINE, boxChrome } from "../lib/input-box-variants.js";
|
|
5
5
|
import * as React$1 from "react";
|
|
6
6
|
import { jsx } from "react/jsx-runtime";
|
|
7
7
|
import { cva } from "class-variance-authority";
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const require_chunk = require("../chunk-DY84GLCh.cjs");
|
|
4
4
|
const require_hooks_use_reduced_motion = require("../hooks/use-reduced-motion.cjs");
|
|
5
|
-
const require_lib_message_animations = require("../lib/message-animations.cjs");
|
|
6
5
|
const require_components_button = require("./button.cjs");
|
|
6
|
+
const require_lib_message_animations = require("../lib/message-animations.cjs");
|
|
7
7
|
let react = require("react");
|
|
8
8
|
react = require_chunk.__toESM(react);
|
|
9
9
|
let _epilot_spark_icons = require("@epilot/spark-icons");
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useReducedMotion } from "../hooks/use-reduced-motion.js";
|
|
3
3
|
import { cn } from "../utils/cn.js";
|
|
4
|
-
import { MESSAGE_ANIMATIONS } from "../lib/message-animations.js";
|
|
5
4
|
import { Button } from "./button.js";
|
|
5
|
+
import { MESSAGE_ANIMATIONS } from "../lib/message-animations.js";
|
|
6
6
|
import * as React$1 from "react";
|
|
7
7
|
import { ArrowDownward, ArrowUpward } from "@epilot/spark-icons";
|
|
8
8
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -6,7 +6,7 @@ import { useRender } from "@base-ui/react/use-render";
|
|
|
6
6
|
|
|
7
7
|
//#region src/components/page-header.d.ts
|
|
8
8
|
declare const PageHeader: React$1.ForwardRefExoticComponent<React$1.HTMLAttributes<HTMLElement> & React$1.RefAttributes<HTMLElement>>;
|
|
9
|
-
declare const PageHeaderBackButton: React$1.ForwardRefExoticComponent<Omit<ButtonProps, "
|
|
9
|
+
declare const PageHeaderBackButton: React$1.ForwardRefExoticComponent<Omit<ButtonProps, "styleVariant" | "color" | "size"> & React$1.RefAttributes<HTMLButtonElement>>;
|
|
10
10
|
declare const PageHeaderTitle: ({
|
|
11
11
|
className,
|
|
12
12
|
...props
|
|
@@ -6,7 +6,7 @@ import { useRender } from "@base-ui/react/use-render";
|
|
|
6
6
|
|
|
7
7
|
//#region src/components/page-header.d.ts
|
|
8
8
|
declare const PageHeader: React$1.ForwardRefExoticComponent<React$1.HTMLAttributes<HTMLElement> & React$1.RefAttributes<HTMLElement>>;
|
|
9
|
-
declare const PageHeaderBackButton: React$1.ForwardRefExoticComponent<Omit<ButtonProps, "
|
|
9
|
+
declare const PageHeaderBackButton: React$1.ForwardRefExoticComponent<Omit<ButtonProps, "styleVariant" | "color" | "size"> & React$1.RefAttributes<HTMLButtonElement>>;
|
|
10
10
|
declare const PageHeaderTitle: ({
|
|
11
11
|
className,
|
|
12
12
|
...props
|
package/dist/components/pill.cjs
CHANGED
|
@@ -42,7 +42,7 @@ const Pill = react.forwardRef(function Pill({ className, children, styleVariant,
|
|
|
42
42
|
"data-accent-color": color ? require_lib_accent_colors.accentByColor[color] : void 0,
|
|
43
43
|
...interaction,
|
|
44
44
|
ref,
|
|
45
|
-
className: (0, cnfast.cn)("inline-flex h-7 items-center gap-1 rounded-full px-2.5 text-xs font-medium select-none", "bg-(--spark-style-bg,var(--neutral-a3)) text-(--spark-style-fg,var(--neutral-a11))", focusable && "transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden motion-reduce:transition-none", clickable && "cursor-pointer hover:bg-(--spark-style-bg-hover)", disabled && "pointer-events-none opacity-50", className),
|
|
45
|
+
className: (0, cnfast.cn)("inline-flex h-7 items-center gap-1 rounded-full px-2.5 text-xs font-medium select-none", "bg-(--spark-style-bg,var(--neutral-a3)) text-(--spark-style-fg,var(--spark-badge-fg,var(--neutral-a11)))", "shadow-[inset_0_0_0_var(--spark-style-border-width,1px)_var(--spark-style-border,transparent)]", focusable && "transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden motion-reduce:transition-none", clickable && "cursor-pointer hover:bg-(--spark-style-bg-hover,var(--neutral-a4))", disabled && "pointer-events-none opacity-50", className),
|
|
46
46
|
...props,
|
|
47
47
|
children: [children, removable ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
48
48
|
className: "sr-only",
|
|
@@ -54,7 +54,7 @@ const Pill = react.forwardRef(function Pill({ className, children, styleVariant,
|
|
|
54
54
|
event.stopPropagation();
|
|
55
55
|
onRemove?.(event);
|
|
56
56
|
},
|
|
57
|
-
className: "-mr-1 inline-flex size-4 shrink-0 items-center justify-center rounded-full transition-colors hover:bg-(--spark-style-bg-hover) motion-reduce:transition-none",
|
|
57
|
+
className: "-mr-1 inline-flex size-4 shrink-0 items-center justify-center rounded-full transition-colors hover:bg-(--spark-style-bg-hover,var(--neutral-a4)) motion-reduce:transition-none",
|
|
58
58
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_epilot_spark_icons.Close, { className: "[--spark-icon-size:14px]" })
|
|
59
59
|
})] }) : null]
|
|
60
60
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pill.cjs","names":["React","accentByColor","Close"],"sources":["../../src/components/pill.tsx"],"sourcesContent":["import { Close } from '@epilot/spark-icons'\nimport * as React from 'react'\nimport { accentByColor } from '../lib/accent-colors'\nimport type { SparkAccentColor } from '../lib/accent-colors'\nimport type { SparkStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\n\ntype PillRemoveProps =\n | { onRemove?: undefined; removeLabel?: string }\n | {\n /** Fires on a pointer click of the (decorative) glyph, or Delete/Backspace while the pill has focus. */\n onRemove: (event: React.SyntheticEvent) => void\n /** Visually-hidden text folded into the pill's accessible name -- the glyph itself is `aria-hidden`. */\n removeLabel: string\n }\n\ntype PillProps = Omit<React.HTMLAttributes<HTMLSpanElement>, 'onClick'> &\n PillRemoveProps & {\n styleVariant?: SparkStyle\n color?: SparkAccentColor\n disabled?: boolean\n onClick?: (event: React.SyntheticEvent<HTMLSpanElement>) => void\n }\n\n/**\n * Like Badge, but interactive: `onClick` makes the whole pill a button\n * (Enter/Space), `onRemove` adds a Delete/Backspace-and-click affordance.\n * Both are optional -- with neither, it's a static label.\n */\nconst Pill = React.forwardRef<HTMLSpanElement, PillProps>(function Pill(\n {\n className,\n children,\n styleVariant,\n color,\n disabled = false,\n onClick,\n onRemove,\n removeLabel,\n onKeyDown,\n ...props\n },\n ref,\n) {\n const clickable = !disabled && onClick !== undefined\n const removable = !disabled && onRemove !== undefined\n const focusable = clickable || removable\n const hasLook = styleVariant !== undefined || color !== undefined\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLSpanElement>) => {\n onKeyDown?.(event)\n if (clickable && (event.key === 'Enter' || event.key === ' ')) {\n if (event.key === ' ') event.preventDefault()\n onClick?.(event)\n }\n if (removable && (event.key === 'Delete' || event.key === 'Backspace')) {\n onRemove?.(event)\n }\n }\n\n // button role and click handler travel together, so a clickable pill always\n // answers Enter/Space; a removable-only pill stays generic and answers Delete\n const interaction = {\n onKeyDown: handleKeyDown,\n ...(clickable && { role: 'button' as const, onClick }),\n ...(focusable && { tabIndex: 0 }),\n }\n\n return (\n <span\n aria-disabled={disabled || undefined}\n aria-keyshortcuts={removable ? 'Delete' : undefined}\n data-slot=\"pill\"\n data-style={hasLook ? (styleVariant ?? 'soft') : undefined}\n data-accent-color={color ? accentByColor[color] : undefined}\n {...interaction}\n ref={ref}\n className={cn(\n 'inline-flex h-7 items-center gap-1 rounded-full px-2.5 text-xs font-medium select-none',\n 'bg-(--spark-style-bg,var(--neutral-a3)) text-(--spark-style-fg,var(--neutral-a11))',\n focusable &&\n 'transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden motion-reduce:transition-none',\n clickable
|
|
1
|
+
{"version":3,"file":"pill.cjs","names":["React","accentByColor","Close"],"sources":["../../src/components/pill.tsx"],"sourcesContent":["import { Close } from '@epilot/spark-icons'\nimport * as React from 'react'\nimport { accentByColor } from '../lib/accent-colors'\nimport type { SparkAccentColor } from '../lib/accent-colors'\nimport type { SparkStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\n\ntype PillRemoveProps =\n | { onRemove?: undefined; removeLabel?: string }\n | {\n /** Fires on a pointer click of the (decorative) glyph, or Delete/Backspace while the pill has focus. */\n onRemove: (event: React.SyntheticEvent) => void\n /** Visually-hidden text folded into the pill's accessible name -- the glyph itself is `aria-hidden`. */\n removeLabel: string\n }\n\ntype PillProps = Omit<React.HTMLAttributes<HTMLSpanElement>, 'onClick'> &\n PillRemoveProps & {\n styleVariant?: SparkStyle\n color?: SparkAccentColor\n disabled?: boolean\n onClick?: (event: React.SyntheticEvent<HTMLSpanElement>) => void\n }\n\n/**\n * Like Badge, but interactive: `onClick` makes the whole pill a button\n * (Enter/Space), `onRemove` adds a Delete/Backspace-and-click affordance.\n * Both are optional -- with neither, it's a static label.\n */\nconst Pill = React.forwardRef<HTMLSpanElement, PillProps>(function Pill(\n {\n className,\n children,\n styleVariant,\n color,\n disabled = false,\n onClick,\n onRemove,\n removeLabel,\n onKeyDown,\n ...props\n },\n ref,\n) {\n const clickable = !disabled && onClick !== undefined\n const removable = !disabled && onRemove !== undefined\n const focusable = clickable || removable\n const hasLook = styleVariant !== undefined || color !== undefined\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLSpanElement>) => {\n onKeyDown?.(event)\n if (clickable && (event.key === 'Enter' || event.key === ' ')) {\n if (event.key === ' ') event.preventDefault()\n onClick?.(event)\n }\n if (removable && (event.key === 'Delete' || event.key === 'Backspace')) {\n onRemove?.(event)\n }\n }\n\n // button role and click handler travel together, so a clickable pill always\n // answers Enter/Space; a removable-only pill stays generic and answers Delete\n const interaction = {\n onKeyDown: handleKeyDown,\n ...(clickable && { role: 'button' as const, onClick }),\n ...(focusable && { tabIndex: 0 }),\n }\n\n return (\n <span\n aria-disabled={disabled || undefined}\n aria-keyshortcuts={removable ? 'Delete' : undefined}\n data-slot=\"pill\"\n data-style={hasLook ? (styleVariant ?? 'soft') : undefined}\n data-accent-color={color ? accentByColor[color] : undefined}\n {...interaction}\n ref={ref}\n className={cn(\n 'inline-flex h-7 items-center gap-1 rounded-full px-2.5 text-xs font-medium select-none',\n // shared chip ink with Badge: solid leaves --spark-style-fg unset, so without the\n // --spark-badge-fg link solid text falls through to a11 on the step-9 fill (fails AA)\n 'bg-(--spark-style-bg,var(--neutral-a3)) text-(--spark-style-fg,var(--spark-badge-fg,var(--neutral-a11)))',\n // surface/outline are defined by their edge; painted inset like Button's so it\n // costs no layout, unlike Badge's real border\n 'shadow-[inset_0_0_0_var(--spark-style-border-width,1px)_var(--spark-style-border,transparent)]',\n focusable &&\n 'transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden motion-reduce:transition-none',\n clickable &&\n 'cursor-pointer hover:bg-(--spark-style-bg-hover,var(--neutral-a4))',\n disabled && 'pointer-events-none opacity-50',\n className,\n )}\n {...props}\n >\n {children}\n {removable ? (\n <>\n {/* aria-hidden hides the glyph itself; this carries removeLabel into the pill's own accessible name */}\n <span className=\"sr-only\">{removeLabel}</span>\n <span\n aria-hidden=\"true\"\n data-slot=\"pill-remove\"\n onClick={(event) => {\n event.stopPropagation()\n onRemove?.(event)\n }}\n className=\"-mr-1 inline-flex size-4 shrink-0 items-center justify-center rounded-full transition-colors hover:bg-(--spark-style-bg-hover,var(--neutral-a4)) motion-reduce:transition-none\"\n >\n <Close className=\"[--spark-icon-size:14px]\" />\n </span>\n </>\n ) : null}\n </span>\n )\n})\n\nPill.displayName = 'Pill'\n\nexport { Pill }\nexport type { PillProps }\n"],"mappings":";;;;;;;;;;;;;;;AA6BA,MAAM,OAAOA,MAAM,WAAuC,SAAS,KACjE,EACE,WACA,UACA,cACA,OACA,WAAW,OACX,SACA,UACA,aACA,WACA,GAAG,SAEL,KACA;CACA,MAAM,YAAY,CAAC,YAAY,YAAY,KAAA;CAC3C,MAAM,YAAY,CAAC,YAAY,aAAa,KAAA;CAC5C,MAAM,YAAY,aAAa;CAC/B,MAAM,UAAU,iBAAiB,KAAA,KAAa,UAAU,KAAA;CAExD,MAAM,iBAAiB,UAAgD;EACrE,YAAY,KAAK;EACjB,IAAI,cAAc,MAAM,QAAQ,WAAW,MAAM,QAAQ,MAAM;GAC7D,IAAI,MAAM,QAAQ,KAAK,MAAM,eAAe;GAC5C,UAAU,KAAK;EACjB;EACA,IAAI,cAAc,MAAM,QAAQ,YAAY,MAAM,QAAQ,cACxD,WAAW,KAAK;CAEpB;CAIA,MAAM,cAAc;EAClB,WAAW;EACX,GAAI,aAAa;GAAE,MAAM;GAAmB;EAAQ;EACpD,GAAI,aAAa,EAAE,UAAU,EAAE;CACjC;CAEA,OACE,iBAAA,GAAA,kBAAA,MAAC,QAAD;EACE,iBAAe,YAAY,KAAA;EAC3B,qBAAmB,YAAY,WAAW,KAAA;EAC1C,aAAU;EACV,cAAY,UAAW,gBAAgB,SAAU,KAAA;EACjD,qBAAmB,QAAQC,0BAAAA,cAAc,SAAS,KAAA;EAClD,GAAI;EACC;EACL,YAAA,GAAA,OAAA,IACE,0FAGA,4GAGA,kGACA,aACE,iIACF,aACE,sEACF,YAAY,kCACZ,SACF;EACA,GAAI;YAvBN,CAyBG,UACA,YACC,iBAAA,GAAA,kBAAA,MAAA,kBAAA,UAAA,EAAA,UAAA,CAEE,iBAAA,GAAA,kBAAA,KAAC,QAAD;GAAM,WAAU;aAAW;EAAkB,CAAA,GAC7C,iBAAA,GAAA,kBAAA,KAAC,QAAD;GACE,eAAY;GACZ,aAAU;GACV,UAAU,UAAU;IAClB,MAAM,gBAAgB;IACtB,WAAW,KAAK;GAClB;GACA,WAAU;aAEV,iBAAA,GAAA,kBAAA,KAACC,oBAAAA,OAAD,EAAO,WAAU,2BAA4B,CAAA;EACzC,CAAA,CACN,EAAA,CAAA,IACA,IACA;;AAEV,CAAC;AAED,KAAK,cAAc"}
|
package/dist/components/pill.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { accentByColor } from "../lib/accent-colors.js";
|
|
3
2
|
import { cn } from "../utils/cn.js";
|
|
3
|
+
import { accentByColor } from "../lib/accent-colors.js";
|
|
4
4
|
import * as React$1 from "react";
|
|
5
5
|
import { Close } from "@epilot/spark-icons";
|
|
6
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -39,7 +39,7 @@ const Pill = React$1.forwardRef(function Pill({ className, children, styleVarian
|
|
|
39
39
|
"data-accent-color": color ? accentByColor[color] : void 0,
|
|
40
40
|
...interaction,
|
|
41
41
|
ref,
|
|
42
|
-
className: cn("inline-flex h-7 items-center gap-1 rounded-full px-2.5 text-xs font-medium select-none", "bg-(--spark-style-bg,var(--neutral-a3)) text-(--spark-style-fg,var(--neutral-a11))", focusable && "transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden motion-reduce:transition-none", clickable && "cursor-pointer hover:bg-(--spark-style-bg-hover)", disabled && "pointer-events-none opacity-50", className),
|
|
42
|
+
className: cn("inline-flex h-7 items-center gap-1 rounded-full px-2.5 text-xs font-medium select-none", "bg-(--spark-style-bg,var(--neutral-a3)) text-(--spark-style-fg,var(--spark-badge-fg,var(--neutral-a11)))", "shadow-[inset_0_0_0_var(--spark-style-border-width,1px)_var(--spark-style-border,transparent)]", focusable && "transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden motion-reduce:transition-none", clickable && "cursor-pointer hover:bg-(--spark-style-bg-hover,var(--neutral-a4))", disabled && "pointer-events-none opacity-50", className),
|
|
43
43
|
...props,
|
|
44
44
|
children: [children, removable ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
45
45
|
className: "sr-only",
|
|
@@ -51,7 +51,7 @@ const Pill = React$1.forwardRef(function Pill({ className, children, styleVarian
|
|
|
51
51
|
event.stopPropagation();
|
|
52
52
|
onRemove?.(event);
|
|
53
53
|
},
|
|
54
|
-
className: "-mr-1 inline-flex size-4 shrink-0 items-center justify-center rounded-full transition-colors hover:bg-(--spark-style-bg-hover) motion-reduce:transition-none",
|
|
54
|
+
className: "-mr-1 inline-flex size-4 shrink-0 items-center justify-center rounded-full transition-colors hover:bg-(--spark-style-bg-hover,var(--neutral-a4)) motion-reduce:transition-none",
|
|
55
55
|
children: /* @__PURE__ */ jsx(Close, { className: "[--spark-icon-size:14px]" })
|
|
56
56
|
})] }) : null]
|
|
57
57
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pill.js","names":["React"],"sources":["../../src/components/pill.tsx"],"sourcesContent":["import { Close } from '@epilot/spark-icons'\nimport * as React from 'react'\nimport { accentByColor } from '../lib/accent-colors'\nimport type { SparkAccentColor } from '../lib/accent-colors'\nimport type { SparkStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\n\ntype PillRemoveProps =\n | { onRemove?: undefined; removeLabel?: string }\n | {\n /** Fires on a pointer click of the (decorative) glyph, or Delete/Backspace while the pill has focus. */\n onRemove: (event: React.SyntheticEvent) => void\n /** Visually-hidden text folded into the pill's accessible name -- the glyph itself is `aria-hidden`. */\n removeLabel: string\n }\n\ntype PillProps = Omit<React.HTMLAttributes<HTMLSpanElement>, 'onClick'> &\n PillRemoveProps & {\n styleVariant?: SparkStyle\n color?: SparkAccentColor\n disabled?: boolean\n onClick?: (event: React.SyntheticEvent<HTMLSpanElement>) => void\n }\n\n/**\n * Like Badge, but interactive: `onClick` makes the whole pill a button\n * (Enter/Space), `onRemove` adds a Delete/Backspace-and-click affordance.\n * Both are optional -- with neither, it's a static label.\n */\nconst Pill = React.forwardRef<HTMLSpanElement, PillProps>(function Pill(\n {\n className,\n children,\n styleVariant,\n color,\n disabled = false,\n onClick,\n onRemove,\n removeLabel,\n onKeyDown,\n ...props\n },\n ref,\n) {\n const clickable = !disabled && onClick !== undefined\n const removable = !disabled && onRemove !== undefined\n const focusable = clickable || removable\n const hasLook = styleVariant !== undefined || color !== undefined\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLSpanElement>) => {\n onKeyDown?.(event)\n if (clickable && (event.key === 'Enter' || event.key === ' ')) {\n if (event.key === ' ') event.preventDefault()\n onClick?.(event)\n }\n if (removable && (event.key === 'Delete' || event.key === 'Backspace')) {\n onRemove?.(event)\n }\n }\n\n // button role and click handler travel together, so a clickable pill always\n // answers Enter/Space; a removable-only pill stays generic and answers Delete\n const interaction = {\n onKeyDown: handleKeyDown,\n ...(clickable && { role: 'button' as const, onClick }),\n ...(focusable && { tabIndex: 0 }),\n }\n\n return (\n <span\n aria-disabled={disabled || undefined}\n aria-keyshortcuts={removable ? 'Delete' : undefined}\n data-slot=\"pill\"\n data-style={hasLook ? (styleVariant ?? 'soft') : undefined}\n data-accent-color={color ? accentByColor[color] : undefined}\n {...interaction}\n ref={ref}\n className={cn(\n 'inline-flex h-7 items-center gap-1 rounded-full px-2.5 text-xs font-medium select-none',\n 'bg-(--spark-style-bg,var(--neutral-a3)) text-(--spark-style-fg,var(--neutral-a11))',\n focusable &&\n 'transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden motion-reduce:transition-none',\n clickable
|
|
1
|
+
{"version":3,"file":"pill.js","names":["React"],"sources":["../../src/components/pill.tsx"],"sourcesContent":["import { Close } from '@epilot/spark-icons'\nimport * as React from 'react'\nimport { accentByColor } from '../lib/accent-colors'\nimport type { SparkAccentColor } from '../lib/accent-colors'\nimport type { SparkStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\n\ntype PillRemoveProps =\n | { onRemove?: undefined; removeLabel?: string }\n | {\n /** Fires on a pointer click of the (decorative) glyph, or Delete/Backspace while the pill has focus. */\n onRemove: (event: React.SyntheticEvent) => void\n /** Visually-hidden text folded into the pill's accessible name -- the glyph itself is `aria-hidden`. */\n removeLabel: string\n }\n\ntype PillProps = Omit<React.HTMLAttributes<HTMLSpanElement>, 'onClick'> &\n PillRemoveProps & {\n styleVariant?: SparkStyle\n color?: SparkAccentColor\n disabled?: boolean\n onClick?: (event: React.SyntheticEvent<HTMLSpanElement>) => void\n }\n\n/**\n * Like Badge, but interactive: `onClick` makes the whole pill a button\n * (Enter/Space), `onRemove` adds a Delete/Backspace-and-click affordance.\n * Both are optional -- with neither, it's a static label.\n */\nconst Pill = React.forwardRef<HTMLSpanElement, PillProps>(function Pill(\n {\n className,\n children,\n styleVariant,\n color,\n disabled = false,\n onClick,\n onRemove,\n removeLabel,\n onKeyDown,\n ...props\n },\n ref,\n) {\n const clickable = !disabled && onClick !== undefined\n const removable = !disabled && onRemove !== undefined\n const focusable = clickable || removable\n const hasLook = styleVariant !== undefined || color !== undefined\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLSpanElement>) => {\n onKeyDown?.(event)\n if (clickable && (event.key === 'Enter' || event.key === ' ')) {\n if (event.key === ' ') event.preventDefault()\n onClick?.(event)\n }\n if (removable && (event.key === 'Delete' || event.key === 'Backspace')) {\n onRemove?.(event)\n }\n }\n\n // button role and click handler travel together, so a clickable pill always\n // answers Enter/Space; a removable-only pill stays generic and answers Delete\n const interaction = {\n onKeyDown: handleKeyDown,\n ...(clickable && { role: 'button' as const, onClick }),\n ...(focusable && { tabIndex: 0 }),\n }\n\n return (\n <span\n aria-disabled={disabled || undefined}\n aria-keyshortcuts={removable ? 'Delete' : undefined}\n data-slot=\"pill\"\n data-style={hasLook ? (styleVariant ?? 'soft') : undefined}\n data-accent-color={color ? accentByColor[color] : undefined}\n {...interaction}\n ref={ref}\n className={cn(\n 'inline-flex h-7 items-center gap-1 rounded-full px-2.5 text-xs font-medium select-none',\n // shared chip ink with Badge: solid leaves --spark-style-fg unset, so without the\n // --spark-badge-fg link solid text falls through to a11 on the step-9 fill (fails AA)\n 'bg-(--spark-style-bg,var(--neutral-a3)) text-(--spark-style-fg,var(--spark-badge-fg,var(--neutral-a11)))',\n // surface/outline are defined by their edge; painted inset like Button's so it\n // costs no layout, unlike Badge's real border\n 'shadow-[inset_0_0_0_var(--spark-style-border-width,1px)_var(--spark-style-border,transparent)]',\n focusable &&\n 'transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden motion-reduce:transition-none',\n clickable &&\n 'cursor-pointer hover:bg-(--spark-style-bg-hover,var(--neutral-a4))',\n disabled && 'pointer-events-none opacity-50',\n className,\n )}\n {...props}\n >\n {children}\n {removable ? (\n <>\n {/* aria-hidden hides the glyph itself; this carries removeLabel into the pill's own accessible name */}\n <span className=\"sr-only\">{removeLabel}</span>\n <span\n aria-hidden=\"true\"\n data-slot=\"pill-remove\"\n onClick={(event) => {\n event.stopPropagation()\n onRemove?.(event)\n }}\n className=\"-mr-1 inline-flex size-4 shrink-0 items-center justify-center rounded-full transition-colors hover:bg-(--spark-style-bg-hover,var(--neutral-a4)) motion-reduce:transition-none\"\n >\n <Close className=\"[--spark-icon-size:14px]\" />\n </span>\n </>\n ) : null}\n </span>\n )\n})\n\nPill.displayName = 'Pill'\n\nexport { Pill }\nexport type { PillProps }\n"],"mappings":";;;;;;;;;;;;AA6BA,MAAM,OAAOA,QAAM,WAAuC,SAAS,KACjE,EACE,WACA,UACA,cACA,OACA,WAAW,OACX,SACA,UACA,aACA,WACA,GAAG,SAEL,KACA;CACA,MAAM,YAAY,CAAC,YAAY,YAAY,KAAA;CAC3C,MAAM,YAAY,CAAC,YAAY,aAAa,KAAA;CAC5C,MAAM,YAAY,aAAa;CAC/B,MAAM,UAAU,iBAAiB,KAAA,KAAa,UAAU,KAAA;CAExD,MAAM,iBAAiB,UAAgD;EACrE,YAAY,KAAK;EACjB,IAAI,cAAc,MAAM,QAAQ,WAAW,MAAM,QAAQ,MAAM;GAC7D,IAAI,MAAM,QAAQ,KAAK,MAAM,eAAe;GAC5C,UAAU,KAAK;EACjB;EACA,IAAI,cAAc,MAAM,QAAQ,YAAY,MAAM,QAAQ,cACxD,WAAW,KAAK;CAEpB;CAIA,MAAM,cAAc;EAClB,WAAW;EACX,GAAI,aAAa;GAAE,MAAM;GAAmB;EAAQ;EACpD,GAAI,aAAa,EAAE,UAAU,EAAE;CACjC;CAEA,OACE,qBAAC,QAAD;EACE,iBAAe,YAAY,KAAA;EAC3B,qBAAmB,YAAY,WAAW,KAAA;EAC1C,aAAU;EACV,cAAY,UAAW,gBAAgB,SAAU,KAAA;EACjD,qBAAmB,QAAQ,cAAc,SAAS,KAAA;EAClD,GAAI;EACC;EACL,WAAW,GACT,0FAGA,4GAGA,kGACA,aACE,iIACF,aACE,sEACF,YAAY,kCACZ,SACF;EACA,GAAI;YAvBN,CAyBG,UACA,YACC,qBAAA,UAAA,EAAA,UAAA,CAEE,oBAAC,QAAD;GAAM,WAAU;aAAW;EAAkB,CAAA,GAC7C,oBAAC,QAAD;GACE,eAAY;GACZ,aAAU;GACV,UAAU,UAAU;IAClB,MAAM,gBAAgB;IACtB,WAAW,KAAK;GAClB;GACA,WAAU;aAEV,oBAAC,OAAD,EAAO,WAAU,2BAA4B,CAAA;EACzC,CAAA,CACN,EAAA,CAAA,IACA,IACA;;AAEV,CAAC;AAED,KAAK,cAAc"}
|
package/dist/components/radio.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { accentByColor } from "../lib/accent-colors.js";
|
|
3
2
|
import { cn } from "../utils/cn.js";
|
|
3
|
+
import { accentByColor } from "../lib/accent-colors.js";
|
|
4
4
|
import "react";
|
|
5
5
|
import { jsx } from "react/jsx-runtime";
|
|
6
6
|
import { Radio as Radio$1 } from "@base-ui/react/radio";
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"use client";
|
|
3
3
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
4
|
const require_chunk = require("../chunk-DY84GLCh.cjs");
|
|
5
|
-
const require_lib_input_box_variants = require("../lib/input-box-variants.cjs");
|
|
6
5
|
const require_components_field = require("./field.cjs");
|
|
6
|
+
const require_lib_input_box_variants = require("../lib/input-box-variants.cjs");
|
|
7
7
|
let react = require("react");
|
|
8
8
|
react = require_chunk.__toESM(react);
|
|
9
9
|
let _epilot_spark_icons = require("@epilot/spark-icons");
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
"use client";
|
|
3
3
|
import { cn } from "../utils/cn.js";
|
|
4
|
-
import { boxChrome } from "../lib/input-box-variants.js";
|
|
5
4
|
import { useFieldControl } from "./field.js";
|
|
5
|
+
import { boxChrome } from "../lib/input-box-variants.js";
|
|
6
6
|
import "react";
|
|
7
7
|
import { Check, KeyboardArrowDown } from "@epilot/spark-icons";
|
|
8
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { accentByColor } from "../lib/accent-colors.js";
|
|
3
2
|
import { cn } from "../utils/cn.js";
|
|
3
|
+
import { accentByColor } from "../lib/accent-colors.js";
|
|
4
4
|
import { jsx } from "react/jsx-runtime";
|
|
5
5
|
import { Switch as Switch$1 } from "@base-ui/react/switch";
|
|
6
6
|
//#region src/components/switch.tsx
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const require_chunk = require("../chunk-DY84GLCh.cjs");
|
|
4
|
-
const require_lib_input_box_variants = require("../lib/input-box-variants.cjs");
|
|
5
4
|
const require_components_field = require("./field.cjs");
|
|
5
|
+
const require_lib_input_box_variants = require("../lib/input-box-variants.cjs");
|
|
6
6
|
let react = require("react");
|
|
7
7
|
react = require_chunk.__toESM(react);
|
|
8
8
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../utils/cn.js";
|
|
3
|
-
import { GHOST_UNDERLINE, boxChrome } from "../lib/input-box-variants.js";
|
|
4
3
|
import { useFieldControl } from "./field.js";
|
|
4
|
+
import { GHOST_UNDERLINE, boxChrome } from "../lib/input-box-variants.js";
|
|
5
5
|
import * as React$1 from "react";
|
|
6
6
|
import { jsx } from "react/jsx-runtime";
|
|
7
7
|
import { cva } from "class-variance-authority";
|
package/dist/components/theme.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
"use client";
|
|
3
|
+
import { cn } from "../utils/cn.js";
|
|
3
4
|
import { isSparkNeutralFamily } from "../lib/theme-types.js";
|
|
4
5
|
import { CURATED_ACCENTS, accentSparkVars, appearanceForBackground, generateAccentScale, getMatchingNeutral, invertAccentScale, isParsableColor, neutralFamilyVars, neutralSparkVars, resolveAutoNeutral } from "../lib/accent-theme.js";
|
|
5
|
-
import { cn } from "../utils/cn.js";
|
|
6
6
|
import * as React$1 from "react";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
import { mergeProps } from "@base-ui/react/merge-props";
|
|
@@ -1146,6 +1146,10 @@
|
|
|
1146
1146
|
* is unset — i.e. the solid style. High-contrast needs no badge rule: HC re-declares
|
|
1147
1147
|
* --spark-style-fg (step-1 on the step-12 solid fill; a12 on the faint non-solid
|
|
1148
1148
|
* fills), which wins the fallback chain outright.
|
|
1149
|
+
*
|
|
1150
|
+
* Pill reads this same token (it is "Badge, but interactive", with an identical fill
|
|
1151
|
+
* chain and the same TEXT-on-solid contrast need). Keep it that way: a solid Pill that
|
|
1152
|
+
* skips this link falls through to a11 ink on the step-9 fill and fails AA.
|
|
1149
1153
|
*/
|
|
1150
1154
|
[data-style='solid'],
|
|
1151
1155
|
:where([data-style='solid'] .spark-theme) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@epilot/spark-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.1",
|
|
4
4
|
"description": "React UI component library from Spark SDK",
|
|
5
5
|
"homepage": "https://spark-ui.epilot.io/docs/ui",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
"class-variance-authority": "^0.7.1",
|
|
66
66
|
"cnfast": "^0.0.8",
|
|
67
67
|
"colorjs.io": "^0.5.2",
|
|
68
|
+
"ictus": "^0.1.2",
|
|
68
69
|
"vaul": "^1.1.2",
|
|
69
70
|
"@epilot/spark-icons": "^0.0.2"
|
|
70
71
|
},
|
package/dist/utils/date-mask.cjs
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
//#region src/utils/date-mask.ts
|
|
4
|
-
function maskDateInput(value, { separator = ".", deleting = false } = {}) {
|
|
5
|
-
const digits = value.replace(/\D/g, "").slice(0, 8);
|
|
6
|
-
let out = digits.slice(0, 2);
|
|
7
|
-
if (digits.length > 2) out += separator + digits.slice(2, 4);
|
|
8
|
-
if (digits.length > 4) out += separator + digits.slice(4, 8);
|
|
9
|
-
const groupComplete = digits.length === 2 || digits.length === 4;
|
|
10
|
-
const keepTrailing = deleting ? /[./-]$/.test(value) : true;
|
|
11
|
-
if (groupComplete && keepTrailing) out += separator;
|
|
12
|
-
return out;
|
|
13
|
-
}
|
|
14
|
-
function parseTypedDate(value) {
|
|
15
|
-
const parts = value.trim().split(/[./-]/).map(Number);
|
|
16
|
-
if (parts.length !== 3 || parts.some(Number.isNaN)) return void 0;
|
|
17
|
-
const [day, month, year] = parts;
|
|
18
|
-
const parsed = new Date(year, month - 1, day);
|
|
19
|
-
return parsed.getFullYear() === year && parsed.getMonth() === month - 1 && parsed.getDate() === day ? parsed : void 0;
|
|
20
|
-
}
|
|
21
|
-
function maskedDateCaret(masked, digitsBefore) {
|
|
22
|
-
let digits = 0;
|
|
23
|
-
let index = 0;
|
|
24
|
-
while (index < masked.length && digits < digitsBefore) {
|
|
25
|
-
if (/\d/.test(masked[index])) digits++;
|
|
26
|
-
index++;
|
|
27
|
-
}
|
|
28
|
-
while (index < masked.length && /\D/.test(masked[index])) index++;
|
|
29
|
-
return index;
|
|
30
|
-
}
|
|
31
|
-
function formatTypedDate(date, separator = ".") {
|
|
32
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
33
|
-
return [
|
|
34
|
-
pad(date.getDate()),
|
|
35
|
-
pad(date.getMonth() + 1),
|
|
36
|
-
date.getFullYear()
|
|
37
|
-
].join(separator);
|
|
38
|
-
}
|
|
39
|
-
//#endregion
|
|
40
|
-
exports.formatTypedDate = formatTypedDate;
|
|
41
|
-
exports.maskDateInput = maskDateInput;
|
|
42
|
-
exports.maskedDateCaret = maskedDateCaret;
|
|
43
|
-
exports.parseTypedDate = parseTypedDate;
|
|
44
|
-
|
|
45
|
-
//# sourceMappingURL=date-mask.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"date-mask.cjs","names":[],"sources":["../../src/utils/date-mask.ts"],"sourcesContent":["// Helpers for a typeable date field: mask keystrokes into DD.MM.YYYY, parse the\n// masked value into a Date, and format a picked Date back into the masked value.\n\n// While typing, a completed group (2 or 4 digits) gets a trailing separator so\n// the next group flows. While deleting, the trailing separator is kept only if\n// the raw value still ends with one — so backspace lands on `DD.MM.` first, then\n// removes the separator on the next press, one character at a time.\nexport function maskDateInput(\n value: string,\n { separator = '.', deleting = false }: MaskOptions = {},\n): string {\n const digits = value.replace(/\\D/g, '').slice(0, 8)\n let out = digits.slice(0, 2)\n if (digits.length > 2) out += separator + digits.slice(2, 4)\n if (digits.length > 4) out += separator + digits.slice(4, 8)\n const groupComplete = digits.length === 2 || digits.length === 4\n const keepTrailing = deleting ? /[./-]$/.test(value) : true\n if (groupComplete && keepTrailing) out += separator\n return out\n}\n\n// Accepts any of `.` `/` `-` as separators; rejects impossible dates (32.01.2020).\nexport function parseTypedDate(value: string): Date | undefined {\n const parts = value.trim().split(/[./-]/).map(Number)\n if (parts.length !== 3 || parts.some(Number.isNaN)) return undefined\n const [day, month, year] = parts as [number, number, number]\n const parsed = new Date(year, month - 1, day)\n const valid =\n parsed.getFullYear() === year &&\n parsed.getMonth() === month - 1 &&\n parsed.getDate() === day\n return valid ? parsed : undefined\n}\n\n// Caret position after TYPING: after the typed digit, flowing past a\n// separator into the next group. (For deletions, keep the raw caret instead —\n// the mask's separator positions are fixed, so it maps directly and backspace\n// can cross separators without looping.)\nexport function maskedDateCaret(masked: string, digitsBefore: number): number {\n let digits = 0\n let index = 0\n while (index < masked.length && digits < digitsBefore) {\n if (/\\d/.test(masked[index]!)) digits++\n index++\n }\n while (index < masked.length && /\\D/.test(masked[index]!)) index++\n return index\n}\n\nexport function formatTypedDate(date: Date, separator = '.'): string {\n const pad = (n: number) => String(n).padStart(2, '0')\n return [\n pad(date.getDate()),\n pad(date.getMonth() + 1),\n date.getFullYear(),\n ].join(separator)\n}\n\ninterface MaskOptions {\n separator?: string\n deleting?: boolean\n}\n"],"mappings":";;;AAOA,SAAgB,cACd,OACA,EAAE,YAAY,KAAK,WAAW,UAAuB,CAAC,GAC9C;CACR,MAAM,SAAS,MAAM,QAAQ,OAAO,EAAE,EAAE,MAAM,GAAG,CAAC;CAClD,IAAI,MAAM,OAAO,MAAM,GAAG,CAAC;CAC3B,IAAI,OAAO,SAAS,GAAG,OAAO,YAAY,OAAO,MAAM,GAAG,CAAC;CAC3D,IAAI,OAAO,SAAS,GAAG,OAAO,YAAY,OAAO,MAAM,GAAG,CAAC;CAC3D,MAAM,gBAAgB,OAAO,WAAW,KAAK,OAAO,WAAW;CAC/D,MAAM,eAAe,WAAW,SAAS,KAAK,KAAK,IAAI;CACvD,IAAI,iBAAiB,cAAc,OAAO;CAC1C,OAAO;AACT;AAGA,SAAgB,eAAe,OAAiC;CAC9D,MAAM,QAAQ,MAAM,KAAK,EAAE,MAAM,OAAO,EAAE,IAAI,MAAM;CACpD,IAAI,MAAM,WAAW,KAAK,MAAM,KAAK,OAAO,KAAK,GAAG,OAAO,KAAA;CAC3D,MAAM,CAAC,KAAK,OAAO,QAAQ;CAC3B,MAAM,SAAS,IAAI,KAAK,MAAM,QAAQ,GAAG,GAAG;CAK5C,OAHE,OAAO,YAAY,MAAM,QACzB,OAAO,SAAS,MAAM,QAAQ,KAC9B,OAAO,QAAQ,MAAM,MACR,SAAS,KAAA;AAC1B;AAMA,SAAgB,gBAAgB,QAAgB,cAA8B;CAC5E,IAAI,SAAS;CACb,IAAI,QAAQ;CACZ,OAAO,QAAQ,OAAO,UAAU,SAAS,cAAc;EACrD,IAAI,KAAK,KAAK,OAAO,MAAO,GAAG;EAC/B;CACF;CACA,OAAO,QAAQ,OAAO,UAAU,KAAK,KAAK,OAAO,MAAO,GAAG;CAC3D,OAAO;AACT;AAEA,SAAgB,gBAAgB,MAAY,YAAY,KAAa;CACnE,MAAM,OAAO,MAAc,OAAO,CAAC,EAAE,SAAS,GAAG,GAAG;CACpD,OAAO;EACL,IAAI,KAAK,QAAQ,CAAC;EAClB,IAAI,KAAK,SAAS,IAAI,CAAC;EACvB,KAAK,YAAY;CACnB,EAAE,KAAK,SAAS;AAClB"}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
//#region src/utils/date-mask.d.ts
|
|
4
|
-
declare function maskDateInput(value: string, {
|
|
5
|
-
separator,
|
|
6
|
-
deleting
|
|
7
|
-
}?: MaskOptions): string;
|
|
8
|
-
declare function parseTypedDate(value: string): Date | undefined;
|
|
9
|
-
declare function maskedDateCaret(masked: string, digitsBefore: number): number;
|
|
10
|
-
declare function formatTypedDate(date: Date, separator?: string): string;
|
|
11
|
-
interface MaskOptions {
|
|
12
|
-
separator?: string;
|
|
13
|
-
deleting?: boolean;
|
|
14
|
-
}
|
|
15
|
-
//#endregion
|
|
16
|
-
export { formatTypedDate, maskDateInput, maskedDateCaret, parseTypedDate };
|
|
17
|
-
//# sourceMappingURL=date-mask.d.cts.map
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
//#region src/utils/date-mask.d.ts
|
|
4
|
-
declare function maskDateInput(value: string, {
|
|
5
|
-
separator,
|
|
6
|
-
deleting
|
|
7
|
-
}?: MaskOptions): string;
|
|
8
|
-
declare function parseTypedDate(value: string): Date | undefined;
|
|
9
|
-
declare function maskedDateCaret(masked: string, digitsBefore: number): number;
|
|
10
|
-
declare function formatTypedDate(date: Date, separator?: string): string;
|
|
11
|
-
interface MaskOptions {
|
|
12
|
-
separator?: string;
|
|
13
|
-
deleting?: boolean;
|
|
14
|
-
}
|
|
15
|
-
//#endregion
|
|
16
|
-
export { formatTypedDate, maskDateInput, maskedDateCaret, parseTypedDate };
|
|
17
|
-
//# sourceMappingURL=date-mask.d.ts.map
|
package/dist/utils/date-mask.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
//#region src/utils/date-mask.ts
|
|
3
|
-
function maskDateInput(value, { separator = ".", deleting = false } = {}) {
|
|
4
|
-
const digits = value.replace(/\D/g, "").slice(0, 8);
|
|
5
|
-
let out = digits.slice(0, 2);
|
|
6
|
-
if (digits.length > 2) out += separator + digits.slice(2, 4);
|
|
7
|
-
if (digits.length > 4) out += separator + digits.slice(4, 8);
|
|
8
|
-
const groupComplete = digits.length === 2 || digits.length === 4;
|
|
9
|
-
const keepTrailing = deleting ? /[./-]$/.test(value) : true;
|
|
10
|
-
if (groupComplete && keepTrailing) out += separator;
|
|
11
|
-
return out;
|
|
12
|
-
}
|
|
13
|
-
function parseTypedDate(value) {
|
|
14
|
-
const parts = value.trim().split(/[./-]/).map(Number);
|
|
15
|
-
if (parts.length !== 3 || parts.some(Number.isNaN)) return void 0;
|
|
16
|
-
const [day, month, year] = parts;
|
|
17
|
-
const parsed = new Date(year, month - 1, day);
|
|
18
|
-
return parsed.getFullYear() === year && parsed.getMonth() === month - 1 && parsed.getDate() === day ? parsed : void 0;
|
|
19
|
-
}
|
|
20
|
-
function maskedDateCaret(masked, digitsBefore) {
|
|
21
|
-
let digits = 0;
|
|
22
|
-
let index = 0;
|
|
23
|
-
while (index < masked.length && digits < digitsBefore) {
|
|
24
|
-
if (/\d/.test(masked[index])) digits++;
|
|
25
|
-
index++;
|
|
26
|
-
}
|
|
27
|
-
while (index < masked.length && /\D/.test(masked[index])) index++;
|
|
28
|
-
return index;
|
|
29
|
-
}
|
|
30
|
-
function formatTypedDate(date, separator = ".") {
|
|
31
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
32
|
-
return [
|
|
33
|
-
pad(date.getDate()),
|
|
34
|
-
pad(date.getMonth() + 1),
|
|
35
|
-
date.getFullYear()
|
|
36
|
-
].join(separator);
|
|
37
|
-
}
|
|
38
|
-
//#endregion
|
|
39
|
-
export { formatTypedDate, maskDateInput, maskedDateCaret, parseTypedDate };
|
|
40
|
-
|
|
41
|
-
//# sourceMappingURL=date-mask.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"date-mask.js","names":[],"sources":["../../src/utils/date-mask.ts"],"sourcesContent":["// Helpers for a typeable date field: mask keystrokes into DD.MM.YYYY, parse the\n// masked value into a Date, and format a picked Date back into the masked value.\n\n// While typing, a completed group (2 or 4 digits) gets a trailing separator so\n// the next group flows. While deleting, the trailing separator is kept only if\n// the raw value still ends with one — so backspace lands on `DD.MM.` first, then\n// removes the separator on the next press, one character at a time.\nexport function maskDateInput(\n value: string,\n { separator = '.', deleting = false }: MaskOptions = {},\n): string {\n const digits = value.replace(/\\D/g, '').slice(0, 8)\n let out = digits.slice(0, 2)\n if (digits.length > 2) out += separator + digits.slice(2, 4)\n if (digits.length > 4) out += separator + digits.slice(4, 8)\n const groupComplete = digits.length === 2 || digits.length === 4\n const keepTrailing = deleting ? /[./-]$/.test(value) : true\n if (groupComplete && keepTrailing) out += separator\n return out\n}\n\n// Accepts any of `.` `/` `-` as separators; rejects impossible dates (32.01.2020).\nexport function parseTypedDate(value: string): Date | undefined {\n const parts = value.trim().split(/[./-]/).map(Number)\n if (parts.length !== 3 || parts.some(Number.isNaN)) return undefined\n const [day, month, year] = parts as [number, number, number]\n const parsed = new Date(year, month - 1, day)\n const valid =\n parsed.getFullYear() === year &&\n parsed.getMonth() === month - 1 &&\n parsed.getDate() === day\n return valid ? parsed : undefined\n}\n\n// Caret position after TYPING: after the typed digit, flowing past a\n// separator into the next group. (For deletions, keep the raw caret instead —\n// the mask's separator positions are fixed, so it maps directly and backspace\n// can cross separators without looping.)\nexport function maskedDateCaret(masked: string, digitsBefore: number): number {\n let digits = 0\n let index = 0\n while (index < masked.length && digits < digitsBefore) {\n if (/\\d/.test(masked[index]!)) digits++\n index++\n }\n while (index < masked.length && /\\D/.test(masked[index]!)) index++\n return index\n}\n\nexport function formatTypedDate(date: Date, separator = '.'): string {\n const pad = (n: number) => String(n).padStart(2, '0')\n return [\n pad(date.getDate()),\n pad(date.getMonth() + 1),\n date.getFullYear(),\n ].join(separator)\n}\n\ninterface MaskOptions {\n separator?: string\n deleting?: boolean\n}\n"],"mappings":";;AAOA,SAAgB,cACd,OACA,EAAE,YAAY,KAAK,WAAW,UAAuB,CAAC,GAC9C;CACR,MAAM,SAAS,MAAM,QAAQ,OAAO,EAAE,EAAE,MAAM,GAAG,CAAC;CAClD,IAAI,MAAM,OAAO,MAAM,GAAG,CAAC;CAC3B,IAAI,OAAO,SAAS,GAAG,OAAO,YAAY,OAAO,MAAM,GAAG,CAAC;CAC3D,IAAI,OAAO,SAAS,GAAG,OAAO,YAAY,OAAO,MAAM,GAAG,CAAC;CAC3D,MAAM,gBAAgB,OAAO,WAAW,KAAK,OAAO,WAAW;CAC/D,MAAM,eAAe,WAAW,SAAS,KAAK,KAAK,IAAI;CACvD,IAAI,iBAAiB,cAAc,OAAO;CAC1C,OAAO;AACT;AAGA,SAAgB,eAAe,OAAiC;CAC9D,MAAM,QAAQ,MAAM,KAAK,EAAE,MAAM,OAAO,EAAE,IAAI,MAAM;CACpD,IAAI,MAAM,WAAW,KAAK,MAAM,KAAK,OAAO,KAAK,GAAG,OAAO,KAAA;CAC3D,MAAM,CAAC,KAAK,OAAO,QAAQ;CAC3B,MAAM,SAAS,IAAI,KAAK,MAAM,QAAQ,GAAG,GAAG;CAK5C,OAHE,OAAO,YAAY,MAAM,QACzB,OAAO,SAAS,MAAM,QAAQ,KAC9B,OAAO,QAAQ,MAAM,MACR,SAAS,KAAA;AAC1B;AAMA,SAAgB,gBAAgB,QAAgB,cAA8B;CAC5E,IAAI,SAAS;CACb,IAAI,QAAQ;CACZ,OAAO,QAAQ,OAAO,UAAU,SAAS,cAAc;EACrD,IAAI,KAAK,KAAK,OAAO,MAAO,GAAG;EAC/B;CACF;CACA,OAAO,QAAQ,OAAO,UAAU,KAAK,KAAK,OAAO,MAAO,GAAG;CAC3D,OAAO;AACT;AAEA,SAAgB,gBAAgB,MAAY,YAAY,KAAa;CACnE,MAAM,OAAO,MAAc,OAAO,CAAC,EAAE,SAAS,GAAG,GAAG;CACpD,OAAO;EACL,IAAI,KAAK,QAAQ,CAAC;EAClB,IAAI,KAAK,SAAS,IAAI,CAAC;EACvB,KAAK,YAAY;CACnB,EAAE,KAAK,SAAS;AAClB"}
|