@exsto/ds 0.0.1-rc.22 → 0.0.1-rc.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -0
- package/dist/color-field.js +3 -0
- package/dist/date-time.js +2 -0
- package/dist/ds/src/color-field.d.ts +9 -0
- package/dist/ds/src/date-time.d.ts +8 -0
- package/dist/source-hash.txt +1 -1
- package/dist/styles.css +216 -7
- package/dist/ui/src/components/edyn/animated-number/animated-number.js +5 -4
- package/dist/ui/src/components/edyn/animated-number/animated-number.js.map +1 -1
- package/dist/ui/src/components/edyn/color-field/color-field.d.ts +47 -0
- package/dist/ui/src/components/edyn/color-field/color-field.js +200 -0
- package/dist/ui/src/components/edyn/color-field/color-field.js.map +1 -0
- package/dist/ui/src/components/edyn/color-field/color-field.state.test.d.ts +6 -0
- package/dist/ui/src/components/edyn/color-field/color-value.d.ts +14 -0
- package/dist/ui/src/components/edyn/color-field/color-value.js +103 -0
- package/dist/ui/src/components/edyn/color-field/color-value.js.map +1 -0
- package/dist/ui/src/components/edyn/color-field/color-value.test.d.ts +1 -0
- package/dist/ui/src/components/edyn/color-field/index.d.ts +2 -0
- package/dist/ui/src/components/edyn/date-picker/date-picker.js +1 -0
- package/dist/ui/src/components/edyn/date-picker/date-picker.js.map +1 -1
- package/dist/ui/src/components/edyn/date-range-picker/date-range-picker.js +1 -0
- package/dist/ui/src/components/edyn/date-range-picker/date-range-picker.js.map +1 -1
- package/dist/ui/src/components/edyn/date-time/date-time-value.d.ts +23 -0
- package/dist/ui/src/components/edyn/date-time/date-time-value.js +26 -0
- package/dist/ui/src/components/edyn/date-time/date-time-value.js.map +1 -0
- package/dist/ui/src/components/edyn/date-time/date-time-value.test.d.ts +1 -0
- package/dist/ui/src/components/edyn/date-time/date-time.d.ts +73 -0
- package/dist/ui/src/components/edyn/date-time/date-time.js +119 -0
- package/dist/ui/src/components/edyn/date-time/date-time.js.map +1 -0
- package/dist/ui/src/components/edyn/date-time/date-time.state.test.d.ts +6 -0
- package/dist/ui/src/components/edyn/date-time/index.d.ts +1 -0
- package/dist/ui/src/components/edyn/multi-select/multi-select.js +2 -0
- package/dist/ui/src/components/edyn/multi-select/multi-select.js.map +1 -1
- package/dist/ui/src/components/edyn/rich-text-editor/toolbar.js +1 -0
- package/dist/ui/src/components/edyn/rich-text-editor/toolbar.js.map +1 -1
- package/dist/ui/src/components/edyn/time-field/time-field.js +7 -3
- package/dist/ui/src/components/edyn/time-field/time-field.js.map +1 -1
- package/dist/ui/src/components/ui/checkbox/checkbox.d.ts +1 -0
- package/dist/ui/src/components/ui/checkbox/checkbox.js +3 -2
- package/dist/ui/src/components/ui/checkbox/checkbox.js.map +1 -1
- package/dist/ui/src/components/ui/command/command.d.ts +1 -16
- package/dist/ui/src/components/ui/command/command.js +5 -23
- package/dist/ui/src/components/ui/command/command.js.map +1 -1
- package/dist/ui/src/components/ui/indicator/indicator.d.ts +1 -1
- package/dist/ui/src/components/ui/input-group/input-group.d.ts +1 -1
- package/dist/ui/src/components/ui/scroll-area/popup-list-scroll.d.ts +3 -1
- package/dist/ui/src/components/ui/scroll-area/popup-list-scroll.js +2 -1
- package/dist/ui/src/components/ui/scroll-area/popup-list-scroll.js.map +1 -1
- package/dist/ui/src/components/ui/sidebar/sidebar.js +1 -1
- package/dist/ui/src/components/ui/sidebar/sidebar.js.map +1 -1
- package/dist/ui/src/components/ui/tabs/tabs.js +16 -0
- package/dist/ui/src/components/ui/tabs/tabs.js.map +1 -1
- package/package.json +9 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"time-field.js","names":[],"sources":["../../../../../../../ui/src/components/edyn/time-field/time-field.tsx"],"sourcesContent":["import { InputGroup, InputGroupAddon } from '@edyn/ui/components/ui/input-group';\nimport { cn } from '@edyn/ui/lib/utils';\nimport { IconClock } from '@tabler/icons-react';\nimport { type ChangeEvent, type ComponentPropsWithoutRef, type KeyboardEvent, useMemo, useRef, useState } from 'react';\n\nexport type TTimeFieldGranularity = 'minute' | 'second';\n\nexport type TTimeFieldHourCycle = 12 | 24;\n\nexport type TTimeFieldSegment = 'hour' | 'minute' | 'second' | 'period';\n\nexport type TTimeFieldPeriod = 'AM' | 'PM';\n\n/** The segments as they are shown: the hour in the field's own cycle (0 to 23, or 1 to 12 beside a period), each empty until entered. */\nexport interface ITimeParts {\n hour: number | null;\n minute: number | null;\n second: number | null;\n period: TTimeFieldPeriod | null;\n}\n\nexport const EMPTY_TIME: ITimeParts = { hour: null, minute: null, second: null, period: null };\n\nexport interface ITimeFieldLabels {\n hour: string;\n minute: string;\n second: string;\n /** The AM/PM segment. */\n period: string;\n /** Read for a segment with nothing in it. */\n empty: string;\n}\n\nexport const DEFAULT_TIME_FIELD_LABELS: ITimeFieldLabels = {\n hour: 'Hours',\n minute: 'Minutes',\n second: 'Seconds',\n period: 'AM or PM',\n empty: 'Empty',\n};\n\n/** What an empty segment shows. Letters, as a DatePicker's pattern is, never a dash. */\nconst SEGMENT_PLACEHOLDER: Record<TTimeFieldSegment, string> = { hour: 'HH', minute: 'MM', second: 'SS', period: 'AM' };\n\nconst TIME = /^(\\d{2}):(\\d{2})(?::(\\d{2}))?$/;\n\nconst two = (value: number): string => String(value).padStart(2, '0');\n\n/** 12 where the reader's locale writes AM and PM, 24 where it does not. */\nexport const localeHourCycle = (): TTimeFieldHourCycle =>\n new Intl.DateTimeFormat(undefined, { hour: 'numeric' }).resolvedOptions().hour12 ? 12 : 24;\n\n/** `HH:mm` or `HH:mm:ss` (24-hour, as `<input type=\"time\">` writes it) to the segments; anything else is no time. */\nexport const parseTime = (value: string, hourCycle: TTimeFieldHourCycle): ITimeParts => {\n const match = TIME.exec(value);\n if (match === null) {\n return EMPTY_TIME;\n }\n const hour24 = Number(match[1]);\n const minute = Number(match[2]);\n const second = match[3] === undefined ? 0 : Number(match[3]);\n if (hour24 > 23 || minute > 59 || second > 59) {\n return EMPTY_TIME;\n }\n if (hourCycle === 24) {\n return { hour: hour24, minute, second, period: null };\n }\n return { hour: hour24 % 12 === 0 ? 12 : hour24 % 12, minute, second, period: hour24 >= 12 ? 'PM' : 'AM' };\n};\n\n/** The segments to `HH:mm` or `HH:mm:ss`, or '' until every segment the field shows is in. */\nexport const formatTime = (parts: ITimeParts, hourCycle: TTimeFieldHourCycle, granularity: TTimeFieldGranularity): string => {\n const { hour, minute, second, period } = parts;\n if (hour === null || minute === null || (granularity === 'second' && second === null)) {\n return '';\n }\n let hour24 = hour;\n if (hourCycle === 12) {\n // 0 is only ever a first digit still waiting for its second.\n if (period === null || hour === 0) {\n return '';\n }\n hour24 = (hour % 12) + (period === 'PM' ? 12 : 0);\n }\n return granularity === 'second' ? `${two(hour24)}:${two(minute)}:${two(second ?? 0)}` : `${two(hour24)}:${two(minute)}`;\n};\n\nexport interface IRange {\n min: number;\n max: number;\n}\n\nexport const rangeOf = (segment: Exclude<TTimeFieldSegment, 'period'>, hourCycle: TTimeFieldHourCycle): IRange => {\n if (segment === 'hour') {\n return hourCycle === 12 ? { min: 1, max: 12 } : { min: 0, max: 23 };\n }\n return { min: 0, max: 59 };\n};\n\n/** One step up or down inside a range, wrapping at the ends. A segment with nothing in it starts at the end the step is heading away from. */\nexport const stepValue = (current: number | null, delta: number, { min, max }: IRange): number => {\n if (current === null) {\n return delta > 0 ? min : max;\n }\n const size = max - min + 1;\n return ((((current - min + delta) % size) + size) % size) + min;\n};\n\n/**\n * Types a digit into a segment. The second digit joins the first when it makes a number the segment can hold; otherwise it starts\n * over. The segment is done, and focus moves on, at two digits or as soon as no second digit could fit (a 3 in the hours, a 6 in the\n * minutes).\n */\nexport const typeDigit = (buffer: string, digit: string, { min, max }: IRange): { value: number; buffer: string; done: boolean } => {\n let text = buffer + digit;\n if (text.length > 2 || Number(text) > max) {\n text = digit;\n }\n const value = Number(text);\n const done = text.length === 2 || value * 10 > max;\n return { value: done && value < min ? min : value, buffer: done ? '' : text, done };\n};\n\nconst PAGE_STEP: Record<Exclude<TTimeFieldSegment, 'period'>, number> = { hour: 3, minute: 10, second: 10 };\n\nexport interface IEdit {\n parts: ITimeParts;\n /** The digits typed so far into this segment and not yet complete. */\n buffer: string;\n /** Move to the next segment: this one is finished. */\n advance: boolean;\n}\n\nconst withSegment = (parts: ITimeParts, segment: Exclude<TTimeFieldSegment, 'period'>, value: number | null): ITimeParts => ({\n ...parts,\n [segment]: value,\n});\n\n/** What a key does to a segment, or null when the key is not one the segment handles (Tab, a letter in the hours...). */\nexport const editSegment = (\n parts: ITimeParts,\n segment: TTimeFieldSegment,\n key: string,\n hourCycle: TTimeFieldHourCycle,\n buffer: string,\n): IEdit | null => {\n if (segment === 'period') {\n const current = parts.period;\n const set = (period: TTimeFieldPeriod | null): IEdit => ({ parts: { ...parts, period }, buffer: '', advance: false });\n switch (key) {\n case 'a':\n case 'A':\n return { ...set('AM'), advance: true };\n case 'p':\n case 'P':\n return { ...set('PM'), advance: true };\n case 'ArrowUp':\n case 'ArrowDown':\n return set(current === null ? (key === 'ArrowUp' ? 'AM' : 'PM') : current === 'AM' ? 'PM' : 'AM');\n case 'Home':\n return set('AM');\n case 'End':\n return set('PM');\n case 'Backspace':\n case 'Delete':\n return set(null);\n default:\n return null;\n }\n }\n\n const range = rangeOf(segment, hourCycle);\n const current = parts[segment];\n const set = (value: number | null, advance = false): IEdit => ({ parts: withSegment(parts, segment, value), buffer: '', advance });\n\n if (/^[0-9]$/.test(key)) {\n const typed = typeDigit(buffer, key, range);\n return { parts: withSegment(parts, segment, typed.value), buffer: typed.buffer, advance: typed.done };\n }\n switch (key) {\n case 'ArrowUp':\n return set(stepValue(current, 1, range));\n case 'ArrowDown':\n return set(stepValue(current, -1, range));\n case 'PageUp':\n return set(stepValue(current, PAGE_STEP[segment], range));\n case 'PageDown':\n return set(stepValue(current, -PAGE_STEP[segment], range));\n case 'Home':\n return set(range.min);\n case 'End':\n return set(range.max);\n case 'Backspace':\n case 'Delete':\n return set(null);\n default:\n return null;\n }\n};\n\nexport interface ITimeFieldProps extends Omit<ComponentPropsWithoutRef<'div'>, 'defaultValue' | 'onChange' | 'children' | 'role'> {\n /** The time as `HH:mm` or `HH:mm:ss`, 24-hour whatever the field shows, as `<input type=\"time\">` writes it. '' is no time. */\n value?: string;\n defaultValue?: string;\n /** Called with the time as soon as every segment is in, and with '' when one of a full time is cleared. */\n onValueChange?: (value: string) => void;\n /** `second` adds a seconds segment and a `HH:mm:ss` value. */\n granularity?: TTimeFieldGranularity;\n /** 12 shows an AM/PM segment, 24 does not. The default is what the reader's locale writes. */\n hourCycle?: TTimeFieldHourCycle;\n /** The earliest and the latest time, `HH:mm`. A time outside them is marked invalid; it is not refused. */\n min?: string;\n max?: string;\n disabled?: boolean;\n /** Shown, focusable and readable, not changeable. */\n readOnly?: boolean;\n invalid?: boolean;\n /** Submits the time with a form, under this name, as `HH:mm` or `HH:mm:ss`. */\n name?: string;\n labels?: Partial<ITimeFieldLabels>;\n}\n\nconst SEGMENT_CLASS =\n 'h-6 rounded-sm bg-transparent text-center text-sm tabular-nums caret-transparent outline-none selection:bg-transparent placeholder:text-muted-foreground focus:bg-foreground/10 disabled:cursor-not-allowed';\n\n/**\n * A time of day typed in segments, in the kit's field: hours, minutes, optional seconds, and AM/PM where the reader's locale uses it.\n * It exists so an app does not fall back to `<input type=\"time\">`, whose look differs between browsers and from the rest of the kit.\n *\n * Each segment is a spinbutton: type digits (the focus moves on by itself), ArrowUp and ArrowDown step with wrap-around, PageUp and\n * PageDown step by more, Home and End jump to the ends, Backspace empties, ArrowLeft and ArrowRight move between segments, and A or P\n * set the period. The group is named by `aria-label` or `aria-labelledby`; a screen reader reads each segment by its own name and value.\n */\nexport const TimeField = ({\n value,\n defaultValue = '',\n onValueChange,\n granularity = 'minute',\n hourCycle: hourCycleProp,\n min,\n max,\n disabled = false,\n readOnly = false,\n invalid = false,\n name,\n labels: labelOverrides,\n className,\n ...rest\n}: ITimeFieldProps) => {\n const labels = useMemo(() => ({ ...DEFAULT_TIME_FIELD_LABELS, ...labelOverrides }), [labelOverrides]);\n const hourCycle = useMemo(() => hourCycleProp ?? localeHourCycle(), [hourCycleProp]);\n const [parts, setParts] = useState<ITimeParts>(() => parseTime(value ?? defaultValue, hourCycle));\n const [seen, setSeen] = useState(value);\n const refSegments = useRef<Array<HTMLInputElement | null>>([]);\n const refBuffer = useRef<{ segment: TTimeFieldSegment; text: string }>({ segment: 'hour', text: '' });\n const formatted = formatTime(parts, hourCycle, granularity);\n // What the parent was last told, and the segments as the last key left them (state is a render behind inside one event).\n const refReported = useRef(formatted);\n const refParts = useRef(parts);\n refParts.current = parts;\n // A time handed in from outside replaces what is shown, unless it is the very time that is shown: clearing one segment of a full\n // time reports '', and taking that back as a new value would wipe the segments still being edited.\n if (value !== seen) {\n setSeen(value);\n if (value !== undefined && value !== formatted) {\n setParts(parseTime(value, hourCycle));\n refReported.current = value;\n }\n }\n\n const segments: TTimeFieldSegment[] = [\n 'hour',\n 'minute',\n ...(granularity === 'second' ? (['second'] as const) : []),\n ...(hourCycle === 12 ? (['period'] as const) : []),\n ];\n\n const focusSegment = (index: number): void => {\n const target = refSegments.current[index];\n if (target !== null && target !== undefined) {\n target.focus();\n target.select();\n }\n };\n\n const report = (next: ITimeParts): void => {\n const text = formatTime(next, hourCycle, granularity);\n if (text !== refReported.current) {\n refReported.current = text;\n onValueChange?.(text);\n }\n };\n\n const edit = (segment: TTimeFieldSegment, key: string): boolean => {\n const buffer = refBuffer.current.segment === segment ? refBuffer.current.text : '';\n const result = editSegment(refParts.current, segment, key, hourCycle, buffer);\n if (result === null) {\n return false;\n }\n refBuffer.current = { segment, text: result.buffer };\n refParts.current = result.parts;\n setParts(result.parts);\n // A segment still waiting for its second digit is not a time yet worth reporting (a 3 may become 30); it is reported when it is\n // done, or when the focus leaves it.\n if (result.buffer === '') {\n report(result.parts);\n }\n if (result.advance) {\n focusSegment(segments.indexOf(segment) + 1);\n }\n return true;\n };\n\n const onKeyDown = (segment: TTimeFieldSegment, event: KeyboardEvent<HTMLInputElement>): void => {\n if (event.altKey || event.ctrlKey || event.metaKey) {\n return;\n }\n const index = segments.indexOf(segment);\n if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {\n event.preventDefault();\n focusSegment(index + (event.key === 'ArrowRight' ? 1 : -1));\n return;\n }\n if (disabled || readOnly) {\n return;\n }\n if (edit(segment, event.key)) {\n event.preventDefault();\n }\n };\n\n // Leaving a segment ends what was being typed into it: coming back and typing a 2 must not make 12 of an old 1. A 0 left alone in a\n // 12-hour hour is the start of a number that never came, so it is emptied.\n const onBlur = (segment: TTimeFieldSegment): void => {\n const unfinished = refBuffer.current.segment === segment && refBuffer.current.text !== '';\n refBuffer.current = { segment, text: '' };\n let settled = refParts.current;\n if (segment === 'hour' && hourCycle === 12 && settled.hour === 0) {\n settled = { ...settled, hour: null };\n refParts.current = settled;\n setParts(settled);\n }\n if (unfinished) {\n report(settled);\n }\n };\n\n // A phone's keyboard does not send key events a page can use; what it types arrives as a change to the input.\n const onChange = (segment: TTimeFieldSegment, event: ChangeEvent<HTMLInputElement>): void => {\n if (disabled || readOnly) {\n return;\n }\n const typed = event.target.value.slice(-1);\n edit(segment, typed);\n };\n\n const outOfRange =\n formatted !== '' && ((min !== undefined && formatted.slice(0, min.length) < min) || (max !== undefined && formatted.slice(0, max.length) > max));\n const flagged = invalid || outOfRange;\n\n const display = (segment: TTimeFieldSegment): string => {\n if (segment === 'period') {\n return parts.period ?? '';\n }\n const current = parts[segment];\n return current === null ? '' : two(current);\n };\n\n return (\n <InputGroup role=\"group\" className={cn('w-fit', className)} {...rest}>\n <InputGroupAddon>\n <IconClock aria-hidden=\"true\" />\n </InputGroupAddon>\n <div className=\"flex items-center gap-0.5 pr-2\">\n {segments.map((segment, index) => {\n const numeric = segment !== 'period';\n const range = numeric ? rangeOf(segment, hourCycle) : { min: 0, max: 1 };\n const current = numeric ? parts[segment] : parts.period === null ? null : parts.period === 'AM' ? 0 : 1;\n return (\n <span key={segment} className=\"flex items-center gap-0.5\">\n {index > 0 && segment !== 'period' && (\n <span aria-hidden=\"true\" className=\"text-muted-foreground\">\n :\n </span>\n )}\n <input\n ref={(node) => {\n refSegments.current[index] = node;\n }}\n data-slot=\"input-group-control\"\n data-segment={segment}\n role=\"spinbutton\"\n inputMode={numeric ? 'numeric' : 'text'}\n autoComplete=\"off\"\n autoCorrect=\"off\"\n spellCheck={false}\n value={display(segment)}\n placeholder={SEGMENT_PLACEHOLDER[segment]}\n disabled={disabled}\n readOnly={readOnly}\n aria-label={labels[segment]}\n aria-readonly={readOnly || undefined}\n aria-invalid={flagged || undefined}\n aria-valuemin={range.min}\n aria-valuemax={range.max}\n aria-valuenow={current ?? undefined}\n aria-valuetext={current === null ? labels.empty : segment === 'period' ? (parts.period ?? labels.empty) : two(current)}\n className={cn(SEGMENT_CLASS, segment === 'period' ? 'w-7' : 'w-5')}\n onKeyDown={(event) => onKeyDown(segment, event)}\n onChange={(event) => onChange(segment, event)}\n onFocus={(event) => event.currentTarget.select()}\n onBlur={() => onBlur(segment)}\n />\n </span>\n );\n })}\n </div>\n {name !== undefined && <input type=\"hidden\" name={name} value={formatted} disabled={disabled} />}\n </InputGroup>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"time-field.js","names":[],"sources":["../../../../../../../ui/src/components/edyn/time-field/time-field.tsx"],"sourcesContent":["import { InputGroup, InputGroupAddon } from '@edyn/ui/components/ui/input-group';\nimport { cn } from '@edyn/ui/lib/utils';\nimport { IconClock } from '@tabler/icons-react';\nimport { type ChangeEvent, type ComponentPropsWithoutRef, type KeyboardEvent, useMemo, useRef, useState } from 'react';\n\nexport type TTimeFieldGranularity = 'minute' | 'second';\n\nexport type TTimeFieldHourCycle = 12 | 24;\n\nexport type TTimeFieldSegment = 'hour' | 'minute' | 'second' | 'period';\n\nexport type TTimeFieldPeriod = 'AM' | 'PM';\n\n/** The segments as they are shown: the hour in the field's own cycle (0 to 23, or 1 to 12 beside a period), each empty until entered. */\nexport interface ITimeParts {\n hour: number | null;\n minute: number | null;\n second: number | null;\n period: TTimeFieldPeriod | null;\n}\n\nexport const EMPTY_TIME: ITimeParts = { hour: null, minute: null, second: null, period: null };\n\nexport interface ITimeFieldLabels {\n hour: string;\n minute: string;\n second: string;\n /** The AM/PM segment. */\n period: string;\n /** Read for a segment with nothing in it. */\n empty: string;\n}\n\nexport const DEFAULT_TIME_FIELD_LABELS: ITimeFieldLabels = {\n hour: 'Hours',\n minute: 'Minutes',\n second: 'Seconds',\n period: 'AM or PM',\n empty: 'Empty',\n};\n\n/** What an empty segment shows. Letters, as a DatePicker's pattern is, never a dash. */\nconst SEGMENT_PLACEHOLDER: Record<TTimeFieldSegment, string> = { hour: 'HH', minute: 'MM', second: 'SS', period: 'AM' };\n\nconst TIME = /^(\\d{2}):(\\d{2})(?::(\\d{2}))?$/;\n\nconst two = (value: number): string => String(value).padStart(2, '0');\n\n/** 12 where the reader's locale writes AM and PM, 24 where it does not. */\nexport const localeHourCycle = (): TTimeFieldHourCycle =>\n new Intl.DateTimeFormat(undefined, { hour: 'numeric' }).resolvedOptions().hour12 ? 12 : 24;\n\n/** `HH:mm` or `HH:mm:ss` (24-hour, as `<input type=\"time\">` writes it) to the segments; anything else is no time. */\nexport const parseTime = (value: string, hourCycle: TTimeFieldHourCycle): ITimeParts => {\n const match = TIME.exec(value);\n if (match === null) {\n return EMPTY_TIME;\n }\n const hour24 = Number(match[1]);\n const minute = Number(match[2]);\n const second = match[3] === undefined ? 0 : Number(match[3]);\n if (hour24 > 23 || minute > 59 || second > 59) {\n return EMPTY_TIME;\n }\n if (hourCycle === 24) {\n return { hour: hour24, minute, second, period: null };\n }\n return { hour: hour24 % 12 === 0 ? 12 : hour24 % 12, minute, second, period: hour24 >= 12 ? 'PM' : 'AM' };\n};\n\n/** The segments to `HH:mm` or `HH:mm:ss`, or '' until every segment the field shows is in. */\nexport const formatTime = (parts: ITimeParts, hourCycle: TTimeFieldHourCycle, granularity: TTimeFieldGranularity): string => {\n const { hour, minute, second, period } = parts;\n if (hour === null || minute === null || (granularity === 'second' && second === null)) {\n return '';\n }\n let hour24 = hour;\n if (hourCycle === 12) {\n // 0 is only ever a first digit still waiting for its second.\n if (period === null || hour === 0) {\n return '';\n }\n hour24 = (hour % 12) + (period === 'PM' ? 12 : 0);\n }\n return granularity === 'second' ? `${two(hour24)}:${two(minute)}:${two(second ?? 0)}` : `${two(hour24)}:${two(minute)}`;\n};\n\nexport interface IRange {\n min: number;\n max: number;\n}\n\nexport const rangeOf = (segment: Exclude<TTimeFieldSegment, 'period'>, hourCycle: TTimeFieldHourCycle): IRange => {\n if (segment === 'hour') {\n return hourCycle === 12 ? { min: 1, max: 12 } : { min: 0, max: 23 };\n }\n return { min: 0, max: 59 };\n};\n\n/** One step up or down inside a range, wrapping at the ends. A segment with nothing in it starts at the end the step is heading away from. */\nexport const stepValue = (current: number | null, delta: number, { min, max }: IRange): number => {\n if (current === null) {\n return delta > 0 ? min : max;\n }\n const size = max - min + 1;\n return ((((current - min + delta) % size) + size) % size) + min;\n};\n\n/**\n * Types a digit into a segment. The second digit joins the first when it makes a number the segment can hold; otherwise it starts\n * over. The segment is done, and focus moves on, at two digits or as soon as no second digit could fit (a 3 in the hours, a 6 in the\n * minutes).\n */\nexport const typeDigit = (buffer: string, digit: string, { min, max }: IRange): { value: number; buffer: string; done: boolean } => {\n let text = buffer + digit;\n if (text.length > 2 || Number(text) > max) {\n text = digit;\n }\n const value = Number(text);\n const done = text.length === 2 || value * 10 > max;\n return { value: done && value < min ? min : value, buffer: done ? '' : text, done };\n};\n\nconst PAGE_STEP: Record<Exclude<TTimeFieldSegment, 'period'>, number> = { hour: 3, minute: 10, second: 10 };\n\nexport interface IEdit {\n parts: ITimeParts;\n /** The digits typed so far into this segment and not yet complete. */\n buffer: string;\n /** Move to the next segment: this one is finished. */\n advance: boolean;\n}\n\nconst withSegment = (parts: ITimeParts, segment: Exclude<TTimeFieldSegment, 'period'>, value: number | null): ITimeParts => ({\n ...parts,\n [segment]: value,\n});\n\n/** What a key does to a segment, or null when the key is not one the segment handles (Tab, a letter in the hours...). */\nexport const editSegment = (\n parts: ITimeParts,\n segment: TTimeFieldSegment,\n key: string,\n hourCycle: TTimeFieldHourCycle,\n buffer: string,\n): IEdit | null => {\n if (segment === 'period') {\n const current = parts.period;\n const set = (period: TTimeFieldPeriod | null): IEdit => ({ parts: { ...parts, period }, buffer: '', advance: false });\n switch (key) {\n case 'a':\n case 'A':\n return { ...set('AM'), advance: true };\n case 'p':\n case 'P':\n return { ...set('PM'), advance: true };\n case 'ArrowUp':\n case 'ArrowDown':\n return set(current === null ? (key === 'ArrowUp' ? 'AM' : 'PM') : current === 'AM' ? 'PM' : 'AM');\n case 'Home':\n return set('AM');\n case 'End':\n return set('PM');\n case 'Backspace':\n case 'Delete':\n return set(null);\n default:\n return null;\n }\n }\n\n const range = rangeOf(segment, hourCycle);\n const current = parts[segment];\n const set = (value: number | null, advance = false): IEdit => ({ parts: withSegment(parts, segment, value), buffer: '', advance });\n\n if (/^[0-9]$/.test(key)) {\n const typed = typeDigit(buffer, key, range);\n return { parts: withSegment(parts, segment, typed.value), buffer: typed.buffer, advance: typed.done };\n }\n switch (key) {\n case 'ArrowUp':\n return set(stepValue(current, 1, range));\n case 'ArrowDown':\n return set(stepValue(current, -1, range));\n case 'PageUp':\n return set(stepValue(current, PAGE_STEP[segment], range));\n case 'PageDown':\n return set(stepValue(current, -PAGE_STEP[segment], range));\n case 'Home':\n return set(range.min);\n case 'End':\n return set(range.max);\n case 'Backspace':\n case 'Delete':\n return set(null);\n default:\n return null;\n }\n};\n\nexport interface ITimeFieldProps extends Omit<ComponentPropsWithoutRef<'div'>, 'defaultValue' | 'onChange' | 'children' | 'role'> {\n /** The time as `HH:mm` or `HH:mm:ss`, 24-hour whatever the field shows, as `<input type=\"time\">` writes it. '' is no time. */\n value?: string;\n defaultValue?: string;\n /** Called with the time as soon as every segment is in, and with '' when one of a full time is cleared. */\n onValueChange?: (value: string) => void;\n /** `second` adds a seconds segment and a `HH:mm:ss` value. */\n granularity?: TTimeFieldGranularity;\n /** 12 shows an AM/PM segment, 24 does not. The default is what the reader's locale writes. */\n hourCycle?: TTimeFieldHourCycle;\n /** The earliest and the latest time, `HH:mm`. A time outside them is marked invalid; it is not refused. */\n min?: string;\n max?: string;\n disabled?: boolean;\n /** Shown, focusable and readable, not changeable. */\n readOnly?: boolean;\n invalid?: boolean;\n /** Submits the time with a form, under this name, as `HH:mm` or `HH:mm:ss`. */\n name?: string;\n labels?: Partial<ITimeFieldLabels>;\n}\n\nconst SEGMENT_CLASS =\n 'h-6 rounded-sm bg-transparent text-center text-sm tabular-nums caret-transparent outline-none selection:bg-transparent placeholder:text-muted-foreground focus:bg-foreground/10 disabled:cursor-not-allowed';\n\n/**\n * A time of day typed in segments, in the kit's field: hours, minutes, optional seconds, and AM/PM where the reader's locale uses it.\n * It exists so an app does not fall back to `<input type=\"time\">`, whose look differs between browsers and from the rest of the kit.\n *\n * Each segment is a spinbutton: type digits (the focus moves on by itself), ArrowUp and ArrowDown step with wrap-around, PageUp and\n * PageDown step by more, Home and End jump to the ends, Backspace empties, ArrowLeft and ArrowRight move between segments, and A or P\n * set the period. The group is named by `aria-label` or `aria-labelledby`; a screen reader reads each segment by its own name and value.\n */\nexport const TimeField = ({\n value,\n defaultValue = '',\n onValueChange,\n granularity = 'minute',\n hourCycle: hourCycleProp,\n min,\n max,\n disabled = false,\n readOnly = false,\n invalid = false,\n name,\n labels: labelOverrides,\n className,\n ...rest\n}: ITimeFieldProps) => {\n const labels = useMemo(() => ({ ...DEFAULT_TIME_FIELD_LABELS, ...labelOverrides }), [labelOverrides]);\n const hourCycle = useMemo(() => hourCycleProp ?? localeHourCycle(), [hourCycleProp]);\n const [parts, setParts] = useState<ITimeParts>(() => parseTime(value ?? defaultValue, hourCycle));\n const [seen, setSeen] = useState(value);\n const refSegments = useRef<Array<HTMLInputElement | null>>([]);\n const refBuffer = useRef<{ segment: TTimeFieldSegment; text: string }>({ segment: 'hour', text: '' });\n const formatted = formatTime(parts, hourCycle, granularity);\n // What the parent was last told, and the segments as the last key left them (state is a render behind inside one event).\n const refReported = useRef(formatted);\n const refParts = useRef(parts);\n refParts.current = parts;\n // A time handed in from outside replaces what is shown, unless it is the very time that is shown: clearing one segment of a full\n // time reports '', and taking that back as a new value would wipe the segments still being edited.\n if (value !== seen) {\n setSeen(value);\n if (value !== undefined && value !== formatted) {\n setParts(parseTime(value, hourCycle));\n refReported.current = value;\n }\n }\n\n const segments: TTimeFieldSegment[] = [\n 'hour',\n 'minute',\n ...(granularity === 'second' ? (['second'] as const) : []),\n ...(hourCycle === 12 ? (['period'] as const) : []),\n ];\n\n const focusSegment = (index: number): void => {\n const target = refSegments.current[index];\n if (target !== null && target !== undefined) {\n target.focus();\n target.select();\n }\n };\n\n const report = (next: ITimeParts): void => {\n const text = formatTime(next, hourCycle, granularity);\n if (text !== refReported.current) {\n refReported.current = text;\n onValueChange?.(text);\n }\n };\n\n const edit = (segment: TTimeFieldSegment, key: string): boolean => {\n const buffer = refBuffer.current.segment === segment ? refBuffer.current.text : '';\n const result = editSegment(refParts.current, segment, key, hourCycle, buffer);\n if (result === null) {\n return false;\n }\n refBuffer.current = { segment, text: result.buffer };\n refParts.current = result.parts;\n setParts(result.parts);\n // A segment still waiting for its second digit is not a time yet worth reporting (a 3 may become 30); it is reported when it is\n // done, or when the focus leaves it.\n if (result.buffer === '') {\n report(result.parts);\n }\n if (result.advance) {\n focusSegment(segments.indexOf(segment) + 1);\n }\n return true;\n };\n\n const onKeyDown = (segment: TTimeFieldSegment, event: KeyboardEvent<HTMLInputElement>): void => {\n if (event.altKey || event.ctrlKey || event.metaKey) {\n return;\n }\n const index = segments.indexOf(segment);\n if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {\n event.preventDefault();\n focusSegment(index + (event.key === 'ArrowRight' ? 1 : -1));\n return;\n }\n if (disabled || readOnly) {\n return;\n }\n if (edit(segment, event.key)) {\n event.preventDefault();\n }\n };\n\n // Leaving a segment ends what was being typed into it: coming back and typing a 2 must not make 12 of an old 1. A 0 left alone in a\n // 12-hour hour is the start of a number that never came, so it is emptied.\n const onBlur = (segment: TTimeFieldSegment): void => {\n const unfinished = refBuffer.current.segment === segment && refBuffer.current.text !== '';\n refBuffer.current = { segment, text: '' };\n let settled = refParts.current;\n if (segment === 'hour' && hourCycle === 12 && settled.hour === 0) {\n settled = { ...settled, hour: null };\n refParts.current = settled;\n setParts(settled);\n }\n if (unfinished) {\n report(settled);\n }\n };\n\n // A phone's keyboard does not send key events a page can use; what it types arrives as a change to the input.\n const onChange = (segment: TTimeFieldSegment, event: ChangeEvent<HTMLInputElement>): void => {\n if (disabled || readOnly) {\n return;\n }\n const typed = event.target.value.slice(-1);\n edit(segment, typed);\n };\n\n const outOfRange =\n formatted !== '' && ((min !== undefined && formatted.slice(0, min.length) < min) || (max !== undefined && formatted.slice(0, max.length) > max));\n const flagged = invalid || outOfRange;\n\n const display = (segment: TTimeFieldSegment): string => {\n if (segment === 'period') {\n return parts.period ?? '';\n }\n const current = parts[segment];\n return current === null ? '' : two(current);\n };\n\n return (\n <InputGroup role=\"group\" className={cn('w-fit', className)} {...rest}>\n <div className=\"flex items-center gap-0.5 pr-3.25 pl-2.5\">\n {segments.map((segment, index) => {\n const numeric = segment !== 'period';\n const range = numeric ? rangeOf(segment, hourCycle) : { min: 0, max: 1 };\n const current = numeric ? parts[segment] : parts.period === null ? null : parts.period === 'AM' ? 0 : 1;\n return (\n <span key={segment} className=\"flex items-center gap-0.5\">\n {index > 0 && segment !== 'period' && (\n <span aria-hidden=\"true\" className=\"text-muted-foreground\">\n :\n </span>\n )}\n <input\n ref={(node) => {\n refSegments.current[index] = node;\n }}\n data-slot=\"input-group-control\"\n data-segment={segment}\n role=\"spinbutton\"\n inputMode={numeric ? 'numeric' : 'text'}\n autoComplete=\"off\"\n autoCorrect=\"off\"\n spellCheck={false}\n value={display(segment)}\n placeholder={SEGMENT_PLACEHOLDER[segment]}\n disabled={disabled}\n readOnly={readOnly}\n aria-label={labels[segment]}\n aria-readonly={readOnly || undefined}\n aria-invalid={flagged || undefined}\n aria-valuemin={range.min}\n aria-valuemax={range.max}\n aria-valuenow={current ?? undefined}\n aria-valuetext={current === null ? labels.empty : segment === 'period' ? (parts.period ?? labels.empty) : two(current)}\n className={cn(SEGMENT_CLASS, index === 0 ? 'w-6 text-left' : 'w-7')}\n onKeyDown={(event) => onKeyDown(segment, event)}\n onChange={(event) => onChange(segment, event)}\n onFocus={(event) => event.currentTarget.select()}\n onBlur={() => onBlur(segment)}\n />\n </span>\n );\n })}\n </div>\n <InputGroupAddon align=\"inline-end\" className=\"pr-1.75\">\n <IconClock aria-hidden=\"true\" />\n </InputGroupAddon>\n {name !== undefined && <input type=\"hidden\" name={name} value={formatted} disabled={disabled} />}\n </InputGroup>\n );\n};\n"],"mappings":";;;;;;AAqBA,IAAa,aAAyB;CAAE,MAAM;CAAM,QAAQ;CAAM,QAAQ;CAAM,QAAQ;AAAK;AAY7F,IAAa,4BAA8C;CACzD,MAAM;CACN,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,OAAO;AACT;;AAGA,IAAM,sBAAyD;CAAE,MAAM;CAAM,QAAQ;CAAM,QAAQ;CAAM,QAAQ;AAAK;AAEtH,IAAM,OAAO;AAEb,IAAM,OAAO,UAA0B,OAAO,KAAK,CAAC,CAAC,SAAS,GAAG,GAAG;;AAGpE,IAAa,wBACX,IAAI,KAAK,eAAe,KAAA,GAAW,EAAE,MAAM,UAAU,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,SAAS,KAAK;;AAG1F,IAAa,aAAa,OAAe,cAA+C;CACtF,MAAM,QAAQ,KAAK,KAAK,KAAK;CAC7B,IAAI,UAAU,MACZ,OAAO;CAET,MAAM,SAAS,OAAO,MAAM,EAAE;CAC9B,MAAM,SAAS,OAAO,MAAM,EAAE;CAC9B,MAAM,SAAS,MAAM,OAAO,KAAA,IAAY,IAAI,OAAO,MAAM,EAAE;CAC3D,IAAI,SAAS,MAAM,SAAS,MAAM,SAAS,IACzC,OAAO;CAET,IAAI,cAAc,IAChB,OAAO;EAAE,MAAM;EAAQ;EAAQ;EAAQ,QAAQ;CAAK;CAEtD,OAAO;EAAE,MAAM,SAAS,OAAO,IAAI,KAAK,SAAS;EAAI;EAAQ;EAAQ,QAAQ,UAAU,KAAK,OAAO;CAAK;AAC1G;;AAGA,IAAa,cAAc,OAAmB,WAAgC,gBAA+C;CAC3H,MAAM,EAAE,MAAM,QAAQ,QAAQ,WAAW;CACzC,IAAI,SAAS,QAAQ,WAAW,QAAS,gBAAgB,YAAY,WAAW,MAC9E,OAAO;CAET,IAAI,SAAS;CACb,IAAI,cAAc,IAAI;EAEpB,IAAI,WAAW,QAAQ,SAAS,GAC9B,OAAO;EAET,SAAU,OAAO,MAAO,WAAW,OAAO,KAAK;CACjD;CACA,OAAO,gBAAgB,WAAW,GAAG,IAAI,MAAM,EAAE,GAAG,IAAI,MAAM,EAAE,GAAG,IAAI,UAAU,CAAC,MAAM,GAAG,IAAI,MAAM,EAAE,GAAG,IAAI,MAAM;AACtH;AAOA,IAAa,WAAW,SAA+C,cAA2C;CAChH,IAAI,YAAY,QACd,OAAO,cAAc,KAAK;EAAE,KAAK;EAAG,KAAK;CAAG,IAAI;EAAE,KAAK;EAAG,KAAK;CAAG;CAEpE,OAAO;EAAE,KAAK;EAAG,KAAK;CAAG;AAC3B;;AAGA,IAAa,aAAa,SAAwB,OAAe,EAAE,KAAK,UAA0B;CAChG,IAAI,YAAY,MACd,OAAO,QAAQ,IAAI,MAAM;CAE3B,MAAM,OAAO,MAAM,MAAM;CACzB,SAAW,UAAU,MAAM,SAAS,OAAQ,QAAQ,OAAQ;AAC9D;;;;;;AAOA,IAAa,aAAa,QAAgB,OAAe,EAAE,KAAK,UAAoE;CAClI,IAAI,OAAO,SAAS;CACpB,IAAI,KAAK,SAAS,KAAK,OAAO,IAAI,IAAI,KACpC,OAAO;CAET,MAAM,QAAQ,OAAO,IAAI;CACzB,MAAM,OAAO,KAAK,WAAW,KAAK,QAAQ,KAAK;CAC/C,OAAO;EAAE,OAAO,QAAQ,QAAQ,MAAM,MAAM;EAAO,QAAQ,OAAO,KAAK;EAAM;CAAK;AACpF;AAEA,IAAM,YAAkE;CAAE,MAAM;CAAG,QAAQ;CAAI,QAAQ;AAAG;AAU1G,IAAM,eAAe,OAAmB,SAA+C,WAAsC;CAC3H,GAAG;EACF,UAAU;AACb;;AAGA,IAAa,eACX,OACA,SACA,KACA,WACA,WACiB;CACjB,IAAI,YAAY,UAAU;EACxB,MAAM,UAAU,MAAM;EACtB,MAAM,OAAO,YAA4C;GAAE,OAAO;IAAE,GAAG;IAAO;GAAO;GAAG,QAAQ;GAAI,SAAS;EAAM;EACnH,QAAQ,KAAR;GACE,KAAK;GACL,KAAK,KACH,OAAO;IAAE,GAAG,IAAI,IAAI;IAAG,SAAS;GAAK;GACvC,KAAK;GACL,KAAK,KACH,OAAO;IAAE,GAAG,IAAI,IAAI;IAAG,SAAS;GAAK;GACvC,KAAK;GACL,KAAK,aACH,OAAO,IAAI,YAAY,OAAQ,QAAQ,YAAY,OAAO,OAAQ,YAAY,OAAO,OAAO,IAAI;GAClG,KAAK,QACH,OAAO,IAAI,IAAI;GACjB,KAAK,OACH,OAAO,IAAI,IAAI;GACjB,KAAK;GACL,KAAK,UACH,OAAO,IAAI,IAAI;GACjB,SACE,OAAO;EACX;CACF;CAEA,MAAM,QAAQ,QAAQ,SAAS,SAAS;CACxC,MAAM,UAAU,MAAM;CACtB,MAAM,OAAO,OAAsB,UAAU,WAAkB;EAAE,OAAO,YAAY,OAAO,SAAS,KAAK;EAAG,QAAQ;EAAI;CAAQ;CAEhI,IAAI,UAAU,KAAK,GAAG,GAAG;EACvB,MAAM,QAAQ,UAAU,QAAQ,KAAK,KAAK;EAC1C,OAAO;GAAE,OAAO,YAAY,OAAO,SAAS,MAAM,KAAK;GAAG,QAAQ,MAAM;GAAQ,SAAS,MAAM;EAAK;CACtG;CACA,QAAQ,KAAR;EACE,KAAK,WACH,OAAO,IAAI,UAAU,SAAS,GAAG,KAAK,CAAC;EACzC,KAAK,aACH,OAAO,IAAI,UAAU,SAAS,IAAI,KAAK,CAAC;EAC1C,KAAK,UACH,OAAO,IAAI,UAAU,SAAS,UAAU,UAAU,KAAK,CAAC;EAC1D,KAAK,YACH,OAAO,IAAI,UAAU,SAAS,CAAC,UAAU,UAAU,KAAK,CAAC;EAC3D,KAAK,QACH,OAAO,IAAI,MAAM,GAAG;EACtB,KAAK,OACH,OAAO,IAAI,MAAM,GAAG;EACtB,KAAK;EACL,KAAK,UACH,OAAO,IAAI,IAAI;EACjB,SACE,OAAO;CACX;AACF;AAwBA,IAAM,gBACJ;;;;;;;;;AAUF,IAAa,aAAa,EACxB,OACA,eAAe,IACf,eACA,cAAc,UACd,WAAW,eACX,KACA,KACA,WAAW,OACX,WAAW,OACX,UAAU,OACV,MACA,QAAQ,gBACR,WACA,GAAG,WACkB;CACrB,MAAM,SAAS,eAAe;EAAE,GAAG;EAA2B,GAAG;CAAe,IAAI,CAAC,cAAc,CAAC;CACpG,MAAM,YAAY,cAAc,iBAAiB,gBAAgB,GAAG,CAAC,aAAa,CAAC;CACnF,MAAM,CAAC,OAAO,YAAY,eAA2B,UAAU,SAAS,cAAc,SAAS,CAAC;CAChG,MAAM,CAAC,MAAM,WAAW,SAAS,KAAK;CACtC,MAAM,cAAc,OAAuC,CAAC,CAAC;CAC7D,MAAM,YAAY,OAAqD;EAAE,SAAS;EAAQ,MAAM;CAAG,CAAC;CACpG,MAAM,YAAY,WAAW,OAAO,WAAW,WAAW;CAE1D,MAAM,cAAc,OAAO,SAAS;CACpC,MAAM,WAAW,OAAO,KAAK;CAC7B,SAAS,UAAU;CAGnB,IAAI,UAAU,MAAM;EAClB,QAAQ,KAAK;EACb,IAAI,UAAU,KAAA,KAAa,UAAU,WAAW;GAC9C,SAAS,UAAU,OAAO,SAAS,CAAC;GACpC,YAAY,UAAU;EACxB;CACF;CAEA,MAAM,WAAgC;EACpC;EACA;EACA,GAAI,gBAAgB,WAAY,CAAC,QAAQ,IAAc,CAAC;EACxD,GAAI,cAAc,KAAM,CAAC,QAAQ,IAAc,CAAC;CAClD;CAEA,MAAM,gBAAgB,UAAwB;EAC5C,MAAM,SAAS,YAAY,QAAQ;EACnC,IAAI,WAAW,QAAQ,WAAW,KAAA,GAAW;GAC3C,OAAO,MAAM;GACb,OAAO,OAAO;EAChB;CACF;CAEA,MAAM,UAAU,SAA2B;EACzC,MAAM,OAAO,WAAW,MAAM,WAAW,WAAW;EACpD,IAAI,SAAS,YAAY,SAAS;GAChC,YAAY,UAAU;GACtB,gBAAgB,IAAI;EACtB;CACF;CAEA,MAAM,QAAQ,SAA4B,QAAyB;EACjE,MAAM,SAAS,UAAU,QAAQ,YAAY,UAAU,UAAU,QAAQ,OAAO;EAChF,MAAM,SAAS,YAAY,SAAS,SAAS,SAAS,KAAK,WAAW,MAAM;EAC5E,IAAI,WAAW,MACb,OAAO;EAET,UAAU,UAAU;GAAE;GAAS,MAAM,OAAO;EAAO;EACnD,SAAS,UAAU,OAAO;EAC1B,SAAS,OAAO,KAAK;EAGrB,IAAI,OAAO,WAAW,IACpB,OAAO,OAAO,KAAK;EAErB,IAAI,OAAO,SACT,aAAa,SAAS,QAAQ,OAAO,IAAI,CAAC;EAE5C,OAAO;CACT;CAEA,MAAM,aAAa,SAA4B,UAAiD;EAC9F,IAAI,MAAM,UAAU,MAAM,WAAW,MAAM,SACzC;EAEF,MAAM,QAAQ,SAAS,QAAQ,OAAO;EACtC,IAAI,MAAM,QAAQ,eAAe,MAAM,QAAQ,cAAc;GAC3D,MAAM,eAAe;GACrB,aAAa,SAAS,MAAM,QAAQ,eAAe,IAAI,GAAG;GAC1D;EACF;EACA,IAAI,YAAY,UACd;EAEF,IAAI,KAAK,SAAS,MAAM,GAAG,GACzB,MAAM,eAAe;CAEzB;CAIA,MAAM,UAAU,YAAqC;EACnD,MAAM,aAAa,UAAU,QAAQ,YAAY,WAAW,UAAU,QAAQ,SAAS;EACvF,UAAU,UAAU;GAAE;GAAS,MAAM;EAAG;EACxC,IAAI,UAAU,SAAS;EACvB,IAAI,YAAY,UAAU,cAAc,MAAM,QAAQ,SAAS,GAAG;GAChE,UAAU;IAAE,GAAG;IAAS,MAAM;GAAK;GACnC,SAAS,UAAU;GACnB,SAAS,OAAO;EAClB;EACA,IAAI,YACF,OAAO,OAAO;CAElB;CAGA,MAAM,YAAY,SAA4B,UAA+C;EAC3F,IAAI,YAAY,UACd;EAEF,MAAM,QAAQ,MAAM,OAAO,MAAM,MAAM,EAAE;EACzC,KAAK,SAAS,KAAK;CACrB;CAEA,MAAM,aACJ,cAAc,OAAQ,QAAQ,KAAA,KAAa,UAAU,MAAM,GAAG,IAAI,MAAM,IAAI,OAAS,QAAQ,KAAA,KAAa,UAAU,MAAM,GAAG,IAAI,MAAM,IAAI;CAC7I,MAAM,UAAU,WAAW;CAE3B,MAAM,WAAW,YAAuC;EACtD,IAAI,YAAY,UACd,OAAO,MAAM,UAAU;EAEzB,MAAM,UAAU,MAAM;EACtB,OAAO,YAAY,OAAO,KAAK,IAAI,OAAO;CAC5C;CAEA,OACE,qBAAC,YAAD;EAAY,MAAK;EAAQ,WAAW,GAAG,SAAS,SAAS;EAAG,GAAI;EAAhE,UAAA;GACE,oBAAC,OAAD;IAAK,WAAU;IACZ,UAAA,SAAS,KAAK,SAAS,UAAU;KAChC,MAAM,UAAU,YAAY;KAC5B,MAAM,QAAQ,UAAU,QAAQ,SAAS,SAAS,IAAI;MAAE,KAAK;MAAG,KAAK;KAAE;KACvE,MAAM,UAAU,UAAU,MAAM,WAAW,MAAM,WAAW,OAAO,OAAO,MAAM,WAAW,OAAO,IAAI;KACtG,OACE,qBAAC,QAAD;MAAoB,WAAU;MAA9B,UAAA,CACG,QAAQ,KAAK,YAAY,YACxB,oBAAC,QAAD;OAAM,eAAY;OAAO,WAAU;OAAwB,UAAA;MAErD,CAAA,GAER,oBAAC,SAAD;OACE,MAAM,SAAS;QACb,YAAY,QAAQ,SAAS;OAC/B;OACA,aAAU;OACV,gBAAc;OACd,MAAK;OACL,WAAW,UAAU,YAAY;OACjC,cAAa;OACb,aAAY;OACZ,YAAY;OACZ,OAAO,QAAQ,OAAO;OACtB,aAAa,oBAAoB;OACvB;OACA;OACV,cAAY,OAAO;OACnB,iBAAe,YAAY,KAAA;OAC3B,gBAAc,WAAW,KAAA;OACzB,iBAAe,MAAM;OACrB,iBAAe,MAAM;OACrB,iBAAe,WAAW,KAAA;OAC1B,kBAAgB,YAAY,OAAO,OAAO,QAAQ,YAAY,WAAY,MAAM,UAAU,OAAO,QAAS,IAAI,OAAO;OACrH,WAAW,GAAG,eAAe,UAAU,IAAI,kBAAkB,KAAK;OAClE,YAAY,UAAU,UAAU,SAAS,KAAK;OAC9C,WAAW,UAAU,SAAS,SAAS,KAAK;OAC5C,UAAU,UAAU,MAAM,cAAc,OAAO;OAC/C,cAAc,OAAO,OAAO;MAC7B,CAAA,CACG;KAlCK,GAAA,OAkCL;IAEV,CAAC;GACE,CAAA;GACL,oBAAC,iBAAD;IAAiB,OAAM;IAAa,WAAU;IAC5C,UAAA,oBAAC,WAAD,EAAW,eAAY,OAAQ,CAAA;GAChB,CAAA;GAChB,SAAS,KAAA,KAAa,oBAAC,SAAD;IAAO,MAAK;IAAe;IAAM,OAAO;IAAqB;GAAW,CAAA;EACrF;;AAEhB"}
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import { Checkbox as CheckboxPrimitive } from '@base-ui/react/checkbox';
|
|
2
|
+
/** A 24px box (WCAG 2.5.8) that takes no more room than the 16px one drawn inside it: the margin gives the 4px back. */
|
|
2
3
|
export declare const Checkbox: ({ className, ...props }: CheckboxPrimitive.Root.Props) => import('react').JSX.Element;
|
|
@@ -3,14 +3,15 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { IconCheck, IconMinus } from "@tabler/icons-react";
|
|
4
4
|
import { Checkbox } from "@base-ui/react/checkbox";
|
|
5
5
|
//#region ../ui/src/components/ui/checkbox/checkbox.tsx
|
|
6
|
+
/** A 24px box (WCAG 2.5.8) that takes no more room than the 16px one drawn inside it: the margin gives the 4px back. */
|
|
6
7
|
var Checkbox$1 = ({ className, ...props }) => {
|
|
7
8
|
return /* @__PURE__ */ jsx(Checkbox.Root, {
|
|
8
9
|
"data-slot": "checkbox",
|
|
9
|
-
className: cn("peer relative flex size-
|
|
10
|
+
className: cn("peer relative -m-1 flex size-6 shrink-0 items-center justify-center rounded-[calc(var(--radius-sm)+4px)] transition-colors outline-none before:absolute before:inset-1 before:rounded-sm before:border before:border-control before:transition-colors after:absolute after:-inset-x-2 after:-inset-y-1 data-disabled:cursor-not-allowed data-disabled:opacity-50 aria-invalid:before:border-destructive aria-invalid:before:ring-3 aria-invalid:before:ring-destructive/20 aria-invalid:aria-checked:before:border-primary dark:before:bg-input/30 dark:aria-invalid:before:border-destructive/50 dark:aria-invalid:before:ring-destructive/40 data-checked:text-primary-foreground data-checked:before:border-primary data-checked:before:bg-primary dark:data-checked:before:bg-primary", className),
|
|
10
11
|
...props,
|
|
11
12
|
children: /* @__PURE__ */ jsxs(Checkbox.Indicator, {
|
|
12
13
|
"data-slot": "checkbox-indicator",
|
|
13
|
-
className: "group/checkbox-indicator grid place-content-center text-current transition-none [&>svg]:size-3.5",
|
|
14
|
+
className: "group/checkbox-indicator relative grid place-content-center text-current transition-none [&>svg]:size-3.5",
|
|
14
15
|
children: [/* @__PURE__ */ jsx(IconCheck, { className: "group-data-indeterminate/checkbox-indicator:hidden" }), /* @__PURE__ */ jsx(IconMinus, { className: "hidden group-data-indeterminate/checkbox-indicator:block" })]
|
|
15
16
|
})
|
|
16
17
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkbox.js","names":[],"sources":["../../../../../../../ui/src/components/ui/checkbox/checkbox.tsx"],"sourcesContent":["import { Checkbox as CheckboxPrimitive } from '@base-ui/react/checkbox';\n\nimport { cn } from '@edyn/ui/lib/utils';\nimport { IconCheck, IconMinus } from '@tabler/icons-react';\n\nexport const Checkbox = ({ className, ...props }: CheckboxPrimitive.Root.Props) => {\n return (\n <CheckboxPrimitive.Root\n data-slot=\"checkbox\"\n className={cn(\n 'peer relative flex size-
|
|
1
|
+
{"version":3,"file":"checkbox.js","names":[],"sources":["../../../../../../../ui/src/components/ui/checkbox/checkbox.tsx"],"sourcesContent":["import { Checkbox as CheckboxPrimitive } from '@base-ui/react/checkbox';\n\nimport { cn } from '@edyn/ui/lib/utils';\nimport { IconCheck, IconMinus } from '@tabler/icons-react';\n\n/** A 24px box (WCAG 2.5.8) that takes no more room than the 16px one drawn inside it: the margin gives the 4px back. */\nexport const Checkbox = ({ className, ...props }: CheckboxPrimitive.Root.Props) => {\n return (\n <CheckboxPrimitive.Root\n data-slot=\"checkbox\"\n className={cn(\n 'peer relative -m-1 flex size-6 shrink-0 items-center justify-center rounded-[calc(var(--radius-sm)+4px)] transition-colors outline-none before:absolute before:inset-1 before:rounded-sm before:border before:border-control before:transition-colors after:absolute after:-inset-x-2 after:-inset-y-1 data-disabled:cursor-not-allowed data-disabled:opacity-50 aria-invalid:before:border-destructive aria-invalid:before:ring-3 aria-invalid:before:ring-destructive/20 aria-invalid:aria-checked:before:border-primary dark:before:bg-input/30 dark:aria-invalid:before:border-destructive/50 dark:aria-invalid:before:ring-destructive/40 data-checked:text-primary-foreground data-checked:before:border-primary data-checked:before:bg-primary dark:data-checked:before:bg-primary',\n className,\n )}\n {...props}\n >\n <CheckboxPrimitive.Indicator\n data-slot=\"checkbox-indicator\"\n className=\"group/checkbox-indicator relative grid place-content-center text-current transition-none [&>svg]:size-3.5\"\n >\n <IconCheck className=\"group-data-indeterminate/checkbox-indicator:hidden\" />\n <IconMinus className=\"hidden group-data-indeterminate/checkbox-indicator:block\" />\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n );\n};\n"],"mappings":";;;;;;AAMA,IAAa,cAAY,EAAE,WAAW,GAAG,YAA0C;CACjF,OACE,oBAAC,SAAkB,MAAnB;EACE,aAAU;EACV,WAAW,GACT,6vBACA,SACF;EACA,GAAI;EAEJ,UAAA,qBAAC,SAAkB,WAAnB;GACE,aAAU;GACV,WAAU;GAFZ,UAAA,CAIE,oBAAC,WAAD,EAAW,WAAU,qDAAsD,CAAA,GAC3E,oBAAC,WAAD,EAAW,WAAU,2DAA4D,CAAA,CACtD;;CACP,CAAA;AAE5B"}
|
|
@@ -10,22 +10,7 @@ export declare const CommandDialog: ({ title, description, children, className,
|
|
|
10
10
|
children: React.ReactNode;
|
|
11
11
|
}) => React.JSX.Element;
|
|
12
12
|
export declare const CommandInput: ({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Input>) => React.JSX.Element;
|
|
13
|
-
/**
|
|
14
|
-
* The palette's results list. The scroll container is the DS ScrollArea rather
|
|
15
|
-
* than the primitive's own `overflow-y-auto`, because a list of results that
|
|
16
|
-
* continues past the fold with no bar and no fade reads as a complete list. The
|
|
17
|
-
* cap stays overridable: `cn` lets a caller's own `max-h-*` replace the default.
|
|
18
|
-
*
|
|
19
|
-
* The ScrollArea wraps the list from OUTSIDE rather than lining it: cmdk's list
|
|
20
|
-
* carries `role="listbox"`, and a listbox may not contain presentation wrappers
|
|
21
|
-
* (axe flags that as a critical aria-required-children violation). The options
|
|
22
|
-
* have to stay direct children of the element that holds the role.
|
|
23
|
-
*
|
|
24
|
-
* cmdk keeps the active item in view with `scrollIntoView({ block: 'nearest' })`
|
|
25
|
-
* on the item, which acts on the nearest scrollable ancestor, so that keeps
|
|
26
|
-
* working now that the viewport is the one that scrolls. `scroll-py-1` goes on
|
|
27
|
-
* the viewport for the same reason: it is what pads that scroll.
|
|
28
|
-
*/
|
|
13
|
+
/** The palette's results list: eight whole rows at most, like every popup list; the cap stays overridable (see docs/architecture.md). */
|
|
29
14
|
export declare const CommandList: ({ className, children, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) => React.JSX.Element;
|
|
30
15
|
export declare const CommandEmpty: ({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) => React.JSX.Element;
|
|
31
16
|
export declare const CommandGroup: ({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Group>) => React.JSX.Element;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../../../lib/utils.js";
|
|
3
3
|
import { popupItemClass } from "../../../lib/popup-list.js";
|
|
4
|
-
import {
|
|
4
|
+
import { PopupListScroll } from "../scroll-area/popup-list-scroll.js";
|
|
5
5
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "../dialog/dialog.js";
|
|
6
6
|
import { InputGroup, InputGroupAddon } from "../input-group/input-group.js";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -42,29 +42,11 @@ var CommandInput = ({ className, ...props }) => {
|
|
|
42
42
|
})
|
|
43
43
|
});
|
|
44
44
|
};
|
|
45
|
-
/**
|
|
46
|
-
* The palette's results list. The scroll container is the DS ScrollArea rather
|
|
47
|
-
* than the primitive's own `overflow-y-auto`, because a list of results that
|
|
48
|
-
* continues past the fold with no bar and no fade reads as a complete list. The
|
|
49
|
-
* cap stays overridable: `cn` lets a caller's own `max-h-*` replace the default.
|
|
50
|
-
*
|
|
51
|
-
* The ScrollArea wraps the list from OUTSIDE rather than lining it: cmdk's list
|
|
52
|
-
* carries `role="listbox"`, and a listbox may not contain presentation wrappers
|
|
53
|
-
* (axe flags that as a critical aria-required-children violation). The options
|
|
54
|
-
* have to stay direct children of the element that holds the role.
|
|
55
|
-
*
|
|
56
|
-
* cmdk keeps the active item in view with `scrollIntoView({ block: 'nearest' })`
|
|
57
|
-
* on the item, which acts on the nearest scrollable ancestor, so that keeps
|
|
58
|
-
* working now that the viewport is the one that scrolls. `scroll-py-1` goes on
|
|
59
|
-
* the viewport for the same reason: it is what pads that scroll.
|
|
60
|
-
*/
|
|
45
|
+
/** The palette's results list: eight whole rows at most, like every popup list; the cap stays overridable (see docs/architecture.md). */
|
|
61
46
|
var CommandList = ({ className, children, ...props }) => {
|
|
62
|
-
return /* @__PURE__ */ jsx(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
fadeFrom: "popover",
|
|
66
|
-
className: cn("max-h-72", className),
|
|
67
|
-
viewPortClassName: "scroll-py-1",
|
|
47
|
+
return /* @__PURE__ */ jsx(PopupListScroll, {
|
|
48
|
+
className: cn("my-0", className),
|
|
49
|
+
viewportClassName: "scroll-py-1",
|
|
68
50
|
children: /* @__PURE__ */ jsx(Command.List, {
|
|
69
51
|
"data-slot": "command-list",
|
|
70
52
|
className: "outline-none",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.js","names":[],"sources":["../../../../../../../ui/src/components/ui/command/command.tsx"],"sourcesContent":["'use client';\n\nimport { popupItemClass } from '@edyn/ui/lib/popup-list';\nimport { cn } from '@edyn/ui/lib/utils';\nimport { IconCheck, IconSearch } from '@tabler/icons-react';\nimport { Command as CommandPrimitive } from 'cmdk';\nimport type * as React from 'react';\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '../dialog';\nimport { InputGroup, InputGroupAddon } from '../input-group';\nimport {
|
|
1
|
+
{"version":3,"file":"command.js","names":[],"sources":["../../../../../../../ui/src/components/ui/command/command.tsx"],"sourcesContent":["'use client';\n\nimport { popupItemClass } from '@edyn/ui/lib/popup-list';\nimport { cn } from '@edyn/ui/lib/utils';\nimport { IconCheck, IconSearch } from '@tabler/icons-react';\nimport { Command as CommandPrimitive } from 'cmdk';\nimport type * as React from 'react';\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '../dialog';\nimport { InputGroup, InputGroupAddon } from '../input-group';\nimport { PopupListScroll } from '../scroll-area';\n\nexport const Command = ({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) => {\n return (\n <CommandPrimitive\n data-slot=\"command\"\n className={cn('flex size-full flex-col overflow-hidden rounded-xl! bg-popover p-1 text-popover-foreground', className)}\n {...props}\n />\n );\n};\n\nexport const CommandDialog = ({\n title = 'Command Palette',\n description = 'Search for a command to run...',\n children,\n className,\n showCloseButton = false,\n ...props\n}: Omit<React.ComponentProps<typeof Dialog>, 'children'> & {\n title?: string;\n description?: string;\n className?: string;\n showCloseButton?: boolean;\n children: React.ReactNode;\n}) => {\n return (\n <Dialog {...props}>\n <DialogHeader className=\"sr-only\">\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription>{description}</DialogDescription>\n </DialogHeader>\n <DialogContent className={cn('top-1/3 translate-y-0 overflow-hidden rounded-xl! p-0', className)} showCloseButton={showCloseButton}>\n {children}\n </DialogContent>\n </Dialog>\n );\n};\n\nexport const CommandInput = ({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Input>) => {\n return (\n // Borderless, with a hairline under it: a grey block around the search field\n // reads as a second surface inside the popup, and the caret already says\n // where typing goes.\n <div data-slot=\"command-input-wrapper\" className=\"border-b border-border p-1\">\n <InputGroup className=\"h-8 border-transparent bg-transparent shadow-none *:data-[slot=input-group-addon]:pl-2 dark:bg-transparent\">\n <CommandPrimitive.Input\n data-slot=\"command-input\"\n className={cn('w-full text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50', className)}\n {...props}\n />\n <InputGroupAddon>\n <IconSearch className=\"size-4 shrink-0 opacity-50\" />\n </InputGroupAddon>\n </InputGroup>\n </div>\n );\n};\n\n/** The palette's results list: eight whole rows at most, like every popup list; the cap stays overridable (see docs/architecture.md). */\nexport const CommandList = ({ className, children, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) => {\n return (\n <PopupListScroll className={cn('my-0', className)} viewportClassName=\"scroll-py-1\">\n <CommandPrimitive.List data-slot=\"command-list\" className=\"outline-none\" {...props}>\n {children}\n </CommandPrimitive.List>\n </PopupListScroll>\n );\n};\n\nexport const CommandEmpty = ({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) => {\n return <CommandPrimitive.Empty data-slot=\"command-empty\" className={cn('py-6 text-center text-sm', className)} {...props} />;\n};\n\nexport const CommandGroup = ({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Group>) => {\n return (\n <CommandPrimitive.Group\n data-slot=\"command-group\"\n className={cn(\n 'overflow-hidden p-1 text-foreground **:[[cmdk-group-heading]]:px-2 **:[[cmdk-group-heading]]:py-1.5 **:[[cmdk-group-heading]]:text-xs **:[[cmdk-group-heading]]:font-medium **:[[cmdk-group-heading]]:text-muted-foreground',\n className,\n )}\n {...props}\n />\n );\n};\n\nexport const CommandSeparator = ({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Separator>) => {\n return <CommandPrimitive.Separator data-slot=\"command-separator\" className={cn('-mx-1 h-px bg-border', className)} {...props} />;\n};\n\nexport const CommandItem = ({ className, children, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) => {\n return (\n <CommandPrimitive.Item data-slot=\"command-item\" className={cn('group/command-item', popupItemClass('cmdk'), className)} {...props}>\n {children}\n {/* The check keeps the end of the item whether it is shown or not, except\n where a shortcut has it, so the list does not shift as the choice moves. */}\n <IconCheck\n data-slot=\"command-item-indicator\"\n className=\"mr-px ml-auto size-3.5 shrink-0 opacity-0 group-has-data-[slot=command-shortcut]/command-item:hidden group-data-[checked=true]/command-item:opacity-100\"\n />\n </CommandPrimitive.Item>\n );\n};\n\nexport const CommandShortcut = ({ className, ...props }: React.ComponentProps<'span'>) => {\n return (\n <span\n data-slot=\"command-shortcut\"\n className={cn('ml-auto text-xs tracking-widest text-muted-foreground group-data-selected/command-item:text-foreground', className)}\n {...props}\n />\n );\n};\n"],"mappings":";;;;;;;;;;AAWA,IAAa,aAAW,EAAE,WAAW,GAAG,YAA2D;CACjG,OACE,oBAAC,SAAD;EACE,aAAU;EACV,WAAW,GAAG,8FAA8F,SAAS;EACrH,GAAI;CACL,CAAA;AAEL;AAEA,IAAa,iBAAiB,EAC5B,QAAQ,mBACR,cAAc,kCACd,UACA,WACA,kBAAkB,OAClB,GAAG,YAOC;CACJ,OACE,qBAAC,QAAD;EAAQ,GAAI;EAAZ,UAAA,CACE,qBAAC,cAAD;GAAc,WAAU;GAAxB,UAAA,CACE,oBAAC,aAAD,EAAA,UAAc,MAAmB,CAAA,GACjC,oBAAC,mBAAD,EAAA,UAAoB,YAA+B,CAAA,CACvC;EACd,CAAA,GAAA,oBAAC,eAAD;GAAe,WAAW,GAAG,yDAAyD,SAAS;GAAoB;GAChH;EACY,CAAA,CACT;;AAEZ;AAEA,IAAa,gBAAgB,EAAE,WAAW,GAAG,YAAiE;CAC5G,OAIE,oBAAC,OAAD;EAAK,aAAU;EAAwB,WAAU;EAC/C,UAAA,qBAAC,YAAD;GAAY,WAAU;GAAtB,UAAA,CACE,oBAAC,QAAiB,OAAlB;IACE,aAAU;IACV,WAAW,GAAG,iFAAiF,SAAS;IACxG,GAAI;GACL,CAAA,GACD,oBAAC,iBAAD,EAAA,UACE,oBAAC,YAAD,EAAY,WAAU,6BAA8B,CAAA,EACrC,CAAA,CACP;;CACT,CAAA;AAET;;AAGA,IAAa,eAAe,EAAE,WAAW,UAAU,GAAG,YAAgE;CACpH,OACE,oBAAC,iBAAD;EAAiB,WAAW,GAAG,QAAQ,SAAS;EAAG,mBAAkB;EACnE,UAAA,oBAAC,QAAiB,MAAlB;GAAuB,aAAU;GAAe,WAAU;GAAe,GAAI;GAC1E;EACoB,CAAA;CACR,CAAA;AAErB;AAEA,IAAa,gBAAgB,EAAE,WAAW,GAAG,YAAiE;CAC5G,OAAO,oBAAC,QAAiB,OAAlB;EAAwB,aAAU;EAAgB,WAAW,GAAG,4BAA4B,SAAS;EAAG,GAAI;CAAQ,CAAA;AAC7H;AAEA,IAAa,gBAAgB,EAAE,WAAW,GAAG,YAAiE;CAC5G,OACE,oBAAC,QAAiB,OAAlB;EACE,aAAU;EACV,WAAW,GACT,+NACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,IAAa,oBAAoB,EAAE,WAAW,GAAG,YAAqE;CACpH,OAAO,oBAAC,QAAiB,WAAlB;EAA4B,aAAU;EAAoB,WAAW,GAAG,wBAAwB,SAAS;EAAG,GAAI;CAAQ,CAAA;AACjI;AAEA,IAAa,eAAe,EAAE,WAAW,UAAU,GAAG,YAAgE;CACpH,OACE,qBAAC,QAAiB,MAAlB;EAAuB,aAAU;EAAe,WAAW,GAAG,sBAAsB,eAAe,MAAM,GAAG,SAAS;EAAG,GAAI;EAA5H,UAAA,CACG,UAGD,oBAAC,WAAD;GACE,aAAU;GACV,WAAU;EACX,CAAA,CACoB;;AAE3B;AAEA,IAAa,mBAAmB,EAAE,WAAW,GAAG,YAA0C;CACxF,OACE,oBAAC,QAAD;EACE,aAAU;EACV,WAAW,GAAG,0GAA0G,SAAS;EACjI,GAAI;CACL,CAAA;AAEL"}
|
|
@@ -3,7 +3,7 @@ import { ComponentProps } from 'react';
|
|
|
3
3
|
export declare const indicatorVariants: (props?: ({
|
|
4
4
|
variant?: "default" | "destructive" | "dot" | null | undefined;
|
|
5
5
|
side?: "top" | "bottom" | null | undefined;
|
|
6
|
-
align?: "
|
|
6
|
+
align?: "start" | "end" | null | undefined;
|
|
7
7
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
8
8
|
export type TIndicatorVariantProps = VariantProps<typeof indicatorVariants>;
|
|
9
9
|
export type TIndicatorProps = ComponentProps<'span'> & TIndicatorVariantProps;
|
|
@@ -6,7 +6,7 @@ export interface IInputGroupProps extends React.ComponentProps<'div'> {
|
|
|
6
6
|
export declare const InputGroup: ({ className, ...props }: IInputGroupProps) => React.JSX.Element;
|
|
7
7
|
export type TInputGroupAddonAlign = 'inline-start' | 'inline-end' | 'block-start' | 'block-end';
|
|
8
8
|
declare const inputGroupAddonVariants: (props?: ({
|
|
9
|
-
align?: "inline-end" | "inline-start" | "block-
|
|
9
|
+
align?: "inline-end" | "inline-start" | "block-start" | "block-end" | null | undefined;
|
|
10
10
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
11
11
|
export interface IInputGroupAddonProps extends React.ComponentProps<'div'>, VariantProps<typeof inputGroupAddonVariants> {
|
|
12
12
|
}
|
|
@@ -4,10 +4,12 @@ export interface IPopupListScrollProps {
|
|
|
4
4
|
className?: string;
|
|
5
5
|
/** See ScrollArea. A menu turns it off: its items are the tab stops. */
|
|
6
6
|
focusable?: boolean;
|
|
7
|
+
/** Classes for the scrolling element itself (cmdk's keep-in-view scroll padding). */
|
|
8
|
+
viewportClassName?: string;
|
|
7
9
|
}
|
|
8
10
|
/**
|
|
9
11
|
* The scrolling part of a popup list: eight rows at most, or the whole rows the room allows. Only rows are counted; a label
|
|
10
12
|
* or separator among them adds its own height. POPUP_LIST_HEIGHT_CLASS holds the CSS cap until this has measured
|
|
11
13
|
* (`--popup-list-cap`). See docs/architecture.md.
|
|
12
14
|
*/
|
|
13
|
-
export declare const PopupListScroll: ({ children, className, focusable }: IPopupListScrollProps) => import('react').JSX.Element;
|
|
15
|
+
export declare const PopupListScroll: ({ children, className, focusable, viewportClassName }: IPopupListScrollProps) => import('react').JSX.Element;
|
|
@@ -13,7 +13,7 @@ var AIR = 8;
|
|
|
13
13
|
* or separator among them adds its own height. POPUP_LIST_HEIGHT_CLASS holds the CSS cap until this has measured
|
|
14
14
|
* (`--popup-list-cap`). See docs/architecture.md.
|
|
15
15
|
*/
|
|
16
|
-
var PopupListScroll = ({ children, className, focusable }) => {
|
|
16
|
+
var PopupListScroll = ({ children, className, focusable, viewportClassName }) => {
|
|
17
17
|
const refRoot = useRef(null);
|
|
18
18
|
const refViewport = useRef(null);
|
|
19
19
|
useLayoutEffect(() => {
|
|
@@ -59,6 +59,7 @@ var PopupListScroll = ({ children, className, focusable }) => {
|
|
|
59
59
|
type: "always",
|
|
60
60
|
fade: true,
|
|
61
61
|
focusable,
|
|
62
|
+
viewportClassName,
|
|
62
63
|
className: cn(POPUP_LIST_HEIGHT_CLASS, className),
|
|
63
64
|
children
|
|
64
65
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"popup-list-scroll.js","names":[],"sources":["../../../../../../../ui/src/components/ui/scroll-area/popup-list-scroll.tsx"],"sourcesContent":["import { POPUP_LIST_HEIGHT_CLASS } from '@edyn/ui/lib/popup-list';\nimport { cn } from '@edyn/ui/lib/utils';\nimport { type ReactNode, useLayoutEffect, useRef } from 'react';\nimport { ScrollArea } from './scroll-area';\n\nconst ROW = '[role=\"option\"], [role^=\"menuitem\"]';\nconst FULL_ROWS = 8;\n/** POPUP_LIST_HEIGHT_CLASS's margin, above and below. */\nconst AIR = 8;\n\nexport interface IPopupListScrollProps {\n children: ReactNode;\n className?: string;\n /** See ScrollArea. A menu turns it off: its items are the tab stops. */\n focusable?: boolean;\n}\n\n/**\n * The scrolling part of a popup list: eight rows at most, or the whole rows the room allows. Only rows are counted; a label\n * or separator among them adds its own height. POPUP_LIST_HEIGHT_CLASS holds the CSS cap until this has measured\n * (`--popup-list-cap`). See docs/architecture.md.\n */\nexport const PopupListScroll = ({ children, className, focusable }: IPopupListScrollProps) => {\n const refRoot = useRef<HTMLDivElement | null>(null);\n const refViewport = useRef<HTMLDivElement | null>(null);\n\n useLayoutEffect(() => {\n const root = refRoot.current;\n const content = refViewport.current?.firstElementChild;\n if (!root || !(content instanceof HTMLElement)) {\n return;\n }\n const measure = () => {\n // In layout units, not the scale of the opening animation.\n const box = content.getBoundingClientRect();\n const scale = content.offsetHeight > 0 ? box.height / content.offsetHeight : 1;\n const bottoms = [...content.querySelectorAll<HTMLElement>(ROW)].map((row) => (row.getBoundingClientRect().bottom - box.top) / scale);\n const available = Number.parseFloat(getComputedStyle(root).getPropertyValue('--available-height'));\n const room = Number.isFinite(available) ? available - AIR : Number.POSITIVE_INFINITY;\n let rows = Math.min(bottoms.length, FULL_ROWS);\n while (rows > 0 && (bottoms[rows - 1] ?? 0) > room + 0.5) {\n rows -= 1;\n }\n const everything = bottoms.length <= FULL_ROWS && content.offsetHeight <= room + 0.5;\n const cap = everything ? content.offsetHeight : rows > 0 ? (bottoms[rows - 1] ?? 0) : undefined;\n if (cap === undefined) {\n root.style.removeProperty('--popup-list-cap');\n } else {\n root.style.setProperty('--popup-list-cap', `${Math.ceil(cap)}px`);\n }\n };\n measure();\n const resized = new ResizeObserver(measure);\n resized.observe(content);\n const changed = new MutationObserver(measure);\n changed.observe(content, { childList: true, subtree: true });\n // The room changes as the popup is placed and as the window moves.\n let positioner: HTMLElement | null = root.parentElement;\n while (positioner && positioner.style.getPropertyValue('--available-height') === '') {\n positioner = positioner.parentElement;\n }\n const moved = new MutationObserver(measure);\n if (positioner) {\n moved.observe(positioner, { attributes: true, attributeFilter: ['style'] });\n }\n return () => {\n resized.disconnect();\n changed.disconnect();\n moved.disconnect();\n };\n }, []);\n\n return (\n <ScrollArea
|
|
1
|
+
{"version":3,"file":"popup-list-scroll.js","names":[],"sources":["../../../../../../../ui/src/components/ui/scroll-area/popup-list-scroll.tsx"],"sourcesContent":["import { POPUP_LIST_HEIGHT_CLASS } from '@edyn/ui/lib/popup-list';\nimport { cn } from '@edyn/ui/lib/utils';\nimport { type ReactNode, useLayoutEffect, useRef } from 'react';\nimport { ScrollArea } from './scroll-area';\n\nconst ROW = '[role=\"option\"], [role^=\"menuitem\"]';\nconst FULL_ROWS = 8;\n/** POPUP_LIST_HEIGHT_CLASS's margin, above and below. */\nconst AIR = 8;\n\nexport interface IPopupListScrollProps {\n children: ReactNode;\n className?: string;\n /** See ScrollArea. A menu turns it off: its items are the tab stops. */\n focusable?: boolean;\n /** Classes for the scrolling element itself (cmdk's keep-in-view scroll padding). */\n viewportClassName?: string;\n}\n\n/**\n * The scrolling part of a popup list: eight rows at most, or the whole rows the room allows. Only rows are counted; a label\n * or separator among them adds its own height. POPUP_LIST_HEIGHT_CLASS holds the CSS cap until this has measured\n * (`--popup-list-cap`). See docs/architecture.md.\n */\nexport const PopupListScroll = ({ children, className, focusable, viewportClassName }: IPopupListScrollProps) => {\n const refRoot = useRef<HTMLDivElement | null>(null);\n const refViewport = useRef<HTMLDivElement | null>(null);\n\n useLayoutEffect(() => {\n const root = refRoot.current;\n const content = refViewport.current?.firstElementChild;\n if (!root || !(content instanceof HTMLElement)) {\n return;\n }\n const measure = () => {\n // In layout units, not the scale of the opening animation.\n const box = content.getBoundingClientRect();\n const scale = content.offsetHeight > 0 ? box.height / content.offsetHeight : 1;\n const bottoms = [...content.querySelectorAll<HTMLElement>(ROW)].map((row) => (row.getBoundingClientRect().bottom - box.top) / scale);\n const available = Number.parseFloat(getComputedStyle(root).getPropertyValue('--available-height'));\n const room = Number.isFinite(available) ? available - AIR : Number.POSITIVE_INFINITY;\n let rows = Math.min(bottoms.length, FULL_ROWS);\n while (rows > 0 && (bottoms[rows - 1] ?? 0) > room + 0.5) {\n rows -= 1;\n }\n const everything = bottoms.length <= FULL_ROWS && content.offsetHeight <= room + 0.5;\n const cap = everything ? content.offsetHeight : rows > 0 ? (bottoms[rows - 1] ?? 0) : undefined;\n if (cap === undefined) {\n root.style.removeProperty('--popup-list-cap');\n } else {\n root.style.setProperty('--popup-list-cap', `${Math.ceil(cap)}px`);\n }\n };\n measure();\n const resized = new ResizeObserver(measure);\n resized.observe(content);\n const changed = new MutationObserver(measure);\n changed.observe(content, { childList: true, subtree: true });\n // The room changes as the popup is placed and as the window moves.\n let positioner: HTMLElement | null = root.parentElement;\n while (positioner && positioner.style.getPropertyValue('--available-height') === '') {\n positioner = positioner.parentElement;\n }\n const moved = new MutationObserver(measure);\n if (positioner) {\n moved.observe(positioner, { attributes: true, attributeFilter: ['style'] });\n }\n return () => {\n resized.disconnect();\n changed.disconnect();\n moved.disconnect();\n };\n }, []);\n\n return (\n <ScrollArea\n ref={refRoot}\n viewportRef={refViewport}\n type=\"always\"\n fade\n focusable={focusable}\n viewportClassName={viewportClassName}\n className={cn(POPUP_LIST_HEIGHT_CLASS, className)}\n >\n {children}\n </ScrollArea>\n );\n};\n"],"mappings":";;;;;;AAKA,IAAM,MAAM;AACZ,IAAM,YAAY;;AAElB,IAAM,MAAM;;;;;;AAgBZ,IAAa,mBAAmB,EAAE,UAAU,WAAW,WAAW,wBAA+C;CAC/G,MAAM,UAAU,OAA8B,IAAI;CAClD,MAAM,cAAc,OAA8B,IAAI;CAEtD,sBAAsB;EACpB,MAAM,OAAO,QAAQ;EACrB,MAAM,UAAU,YAAY,SAAS;EACrC,IAAI,CAAC,QAAQ,EAAE,mBAAmB,cAChC;EAEF,MAAM,gBAAgB;GAEpB,MAAM,MAAM,QAAQ,sBAAsB;GAC1C,MAAM,QAAQ,QAAQ,eAAe,IAAI,IAAI,SAAS,QAAQ,eAAe;GAC7E,MAAM,UAAU,CAAC,GAAG,QAAQ,iBAA8B,GAAG,CAAC,CAAC,CAAC,KAAK,SAAS,IAAI,sBAAsB,CAAC,CAAC,SAAS,IAAI,OAAO,KAAK;GACnI,MAAM,YAAY,OAAO,WAAW,iBAAiB,IAAI,CAAC,CAAC,iBAAiB,oBAAoB,CAAC;GACjG,MAAM,OAAO,OAAO,SAAS,SAAS,IAAI,YAAY,MAAM,OAAO;GACnE,IAAI,OAAO,KAAK,IAAI,QAAQ,QAAQ,SAAS;GAC7C,OAAO,OAAO,MAAM,QAAQ,OAAO,MAAM,KAAK,OAAO,IACnD,QAAQ;GAGV,MAAM,MADa,QAAQ,UAAU,aAAa,QAAQ,gBAAgB,OAAO,KACxD,QAAQ,eAAe,OAAO,IAAK,QAAQ,OAAO,MAAM,IAAK,KAAA;GACtF,IAAI,QAAQ,KAAA,GACV,KAAK,MAAM,eAAe,kBAAkB;QAE5C,KAAK,MAAM,YAAY,oBAAoB,GAAG,KAAK,KAAK,GAAG,EAAE,GAAG;EAEpE;EACA,QAAQ;EACR,MAAM,UAAU,IAAI,eAAe,OAAO;EAC1C,QAAQ,QAAQ,OAAO;EACvB,MAAM,UAAU,IAAI,iBAAiB,OAAO;EAC5C,QAAQ,QAAQ,SAAS;GAAE,WAAW;GAAM,SAAS;EAAK,CAAC;EAE3D,IAAI,aAAiC,KAAK;EAC1C,OAAO,cAAc,WAAW,MAAM,iBAAiB,oBAAoB,MAAM,IAC/E,aAAa,WAAW;EAE1B,MAAM,QAAQ,IAAI,iBAAiB,OAAO;EAC1C,IAAI,YACF,MAAM,QAAQ,YAAY;GAAE,YAAY;GAAM,iBAAiB,CAAC,OAAO;EAAE,CAAC;EAE5E,aAAa;GACX,QAAQ,WAAW;GACnB,QAAQ,WAAW;GACnB,MAAM,WAAW;EACnB;CACF,GAAG,CAAC,CAAC;CAEL,OACE,oBAAC,YAAD;EACE,KAAK;EACL,aAAa;EACb,MAAK;EACL,MAAA;EACW;EACQ;EACnB,WAAW,GAAG,yBAAyB,SAAS;EAE/C;CACS,CAAA;AAEhB"}
|
|
@@ -313,7 +313,7 @@ var SidebarMenuButton = ({ render, isActive = false, variant = "default", size =
|
|
|
313
313
|
var SidebarMenuAction = ({ className, render, showOnHover = false, ...props }) => {
|
|
314
314
|
return useRender({
|
|
315
315
|
defaultTagName: "button",
|
|
316
|
-
props: mergeProps({ className: cn("absolute top-1
|
|
316
|
+
props: mergeProps({ className: cn("absolute top-1 right-0.5 flex aspect-square w-6 items-center justify-center rounded-[calc(var(--radius-md)+2px)] bg-clip-content p-0.5 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform motion-reduce:transition-none group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[size=default]/menu-button:top-1 peer-data-[size=lg]/menu-button:top-2 peer-data-[size=sm]/menu-button:top-0.5 after:absolute after:-inset-1.5 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0", showOnHover && "group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 peer-data-active/menu-button:text-sidebar-accent-foreground aria-expanded:opacity-100 md:opacity-0", className) }, props),
|
|
317
317
|
render,
|
|
318
318
|
state: {
|
|
319
319
|
slot: "sidebar-menu-action",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidebar.js","names":[],"sources":["../../../../../../../ui/src/components/ui/sidebar/sidebar.tsx"],"sourcesContent":["import { mergeProps } from '@base-ui/react/merge-props';\nimport { useRender } from '@base-ui/react/use-render';\nimport { useIsMobile } from '@edyn/ui/hooks/use-mobile';\nimport { cn } from '@edyn/ui/lib/utils';\nimport { IconLayoutSidebar } from '@tabler/icons-react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport * as React from 'react';\nimport { Button } from '../button';\nimport { Input } from '../input';\nimport { Separator } from '../separator';\nimport { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '../sheet';\nimport { Skeleton } from '../skeleton';\nimport { Tooltip, TooltipContent, TooltipTrigger } from '../tooltip';\n\nconst SIDEBAR_COOKIE_NAME = 'sidebar_state';\nconst SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;\nconst SIDEBAR_WIDTH = '16rem';\nconst SIDEBAR_WIDTH_MOBILE = '18rem';\nconst SIDEBAR_WIDTH_ICON = '3rem';\nconst SIDEBAR_KEYBOARD_SHORTCUT = 'b';\n\ntype TSidebarContextProps = {\n state: 'expanded' | 'collapsed';\n open: boolean;\n setOpen: (open: boolean) => void;\n openMobile: boolean;\n setOpenMobile: (open: boolean) => void;\n isMobile: boolean;\n toggleSidebar: () => void;\n};\n\nconst SidebarContext = React.createContext<TSidebarContextProps | null>(null);\n\nexport const useSidebar = () => {\n const context = React.useContext(SidebarContext);\n if (!context) {\n throw new Error('useSidebar must be used within a SidebarProvider.');\n }\n\n return context;\n};\n\nexport const SidebarProvider = ({\n defaultOpen = true,\n open: openProp,\n onOpenChange: setOpenProp,\n className,\n style,\n children,\n ...props\n}: React.ComponentProps<'div'> & {\n defaultOpen?: boolean;\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n}) => {\n const isMobile = useIsMobile();\n const [openMobile, setOpenMobile] = React.useState(false);\n\n // This is the internal state of the sidebar.\n // We use openProp and setOpenProp for control from outside the component.\n const [_open, _setOpen] = React.useState(defaultOpen);\n const open = openProp ?? _open;\n const setOpen = React.useCallback(\n (value: boolean | ((value: boolean) => boolean)) => {\n const openState = typeof value === 'function' ? value(open) : value;\n if (setOpenProp) {\n setOpenProp(openState);\n } else {\n _setOpen(openState);\n }\n\n // This sets the cookie to keep the sidebar state.\n // biome-ignore lint/suspicious/noDocumentCookie: match shadcn's synchronous, broadly supported persistence contract.\n document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;\n },\n [setOpenProp, open],\n );\n\n // Helper to toggle the sidebar.\n const toggleSidebar = React.useCallback(() => {\n return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open);\n }, [isMobile, setOpen]);\n\n // Adds a keyboard shortcut to toggle the sidebar.\n React.useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {\n event.preventDefault();\n toggleSidebar();\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [toggleSidebar]);\n\n // We add a state so that we can do data-state=\"expanded\" or \"collapsed\".\n // This makes it easier to style the sidebar with Tailwind classes.\n const state = open ? 'expanded' : 'collapsed';\n\n const contextValue = React.useMemo<TSidebarContextProps>(\n () => ({\n state,\n open,\n setOpen,\n isMobile,\n openMobile,\n setOpenMobile,\n toggleSidebar,\n }),\n [state, open, setOpen, isMobile, openMobile, toggleSidebar],\n );\n\n return (\n <SidebarContext.Provider value={contextValue}>\n <div\n data-slot=\"sidebar-wrapper\"\n style={\n {\n '--sidebar-width': SIDEBAR_WIDTH,\n '--sidebar-width-icon': SIDEBAR_WIDTH_ICON,\n ...style,\n } as React.CSSProperties\n }\n className={cn('group/sidebar-wrapper flex min-h-svh w-full has-data-[variant=inset]:bg-sidebar', className)}\n {...props}\n >\n {children}\n </div>\n </SidebarContext.Provider>\n );\n};\n\nexport const Sidebar = ({\n side = 'left',\n variant = 'sidebar',\n collapsible = 'offcanvas',\n className,\n children,\n dir,\n ...props\n}: React.ComponentProps<'div'> & {\n side?: 'left' | 'right';\n variant?: 'sidebar' | 'floating' | 'inset';\n collapsible?: 'offcanvas' | 'icon' | 'none';\n}) => {\n const { isMobile, state, openMobile, setOpenMobile } = useSidebar();\n\n if (collapsible === 'none') {\n return (\n <div data-slot=\"sidebar\" className={cn('flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground', className)} {...props}>\n {children}\n </div>\n );\n }\n\n if (isMobile) {\n return (\n <Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>\n <SheetContent\n dir={dir}\n data-sidebar=\"sidebar\"\n data-slot=\"sidebar\"\n data-mobile=\"true\"\n className=\"w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden\"\n style={\n {\n '--sidebar-width': SIDEBAR_WIDTH_MOBILE,\n } as React.CSSProperties\n }\n side={side}\n >\n <SheetHeader className=\"sr-only\">\n <SheetTitle>Sidebar</SheetTitle>\n <SheetDescription>Displays the mobile sidebar.</SheetDescription>\n </SheetHeader>\n <div className=\"flex h-full w-full flex-col\">{children}</div>\n </SheetContent>\n </Sheet>\n );\n }\n\n return (\n <div\n className=\"group peer hidden text-sidebar-foreground md:block\"\n data-state={state}\n data-collapsible={state === 'collapsed' ? collapsible : ''}\n data-variant={variant}\n data-side={side}\n data-slot=\"sidebar\"\n >\n {/* This is what handles the sidebar gap on desktop */}\n <div\n data-slot=\"sidebar-gap\"\n className={cn(\n 'relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 motion-reduce:transition-none ease-linear',\n 'group-data-[collapsible=offcanvas]:w-0',\n 'group-data-[side=right]:rotate-180',\n variant === 'floating' || variant === 'inset'\n ? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]'\n : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon)',\n )}\n />\n <div\n data-slot=\"sidebar-container\"\n data-side={side}\n className={cn(\n 'fixed inset-y-0 top-(--sidebar-top) z-10 hidden h-[calc(100svh-var(--sidebar-top))] w-(--sidebar-width) transition-[left,right,width] duration-200 motion-reduce:transition-none ease-linear data-[side=left]:left-0 data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] data-[side=right]:right-0 data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)] md:flex',\n // Adjust the padding for floating and inset variants.\n variant === 'floating' || variant === 'inset'\n ? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]'\n : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l',\n className,\n )}\n {...props}\n >\n <div\n data-sidebar=\"sidebar\"\n data-slot=\"sidebar-inner\"\n className=\"flex size-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:shadow-sm group-data-[variant=floating]:ring-1 group-data-[variant=floating]:ring-sidebar-border\"\n >\n {children}\n </div>\n </div>\n </div>\n );\n};\n\nexport const SidebarTrigger = ({ className, onClick, ...props }: React.ComponentProps<typeof Button>) => {\n const { toggleSidebar } = useSidebar();\n\n return (\n <Button\n data-sidebar=\"trigger\"\n data-slot=\"sidebar-trigger\"\n variant=\"ghost\"\n size=\"icon-sm\"\n className={cn(className)}\n onClick={(event) => {\n onClick?.(event);\n toggleSidebar();\n }}\n {...props}\n >\n <IconLayoutSidebar />\n <span className=\"sr-only\">Toggle Sidebar</span>\n </Button>\n );\n};\n\nexport const SidebarRail = ({ className, ...props }: React.ComponentProps<'button'>) => {\n const { toggleSidebar } = useSidebar();\n\n return (\n <button\n data-sidebar=\"rail\"\n data-slot=\"sidebar-rail\"\n aria-label=\"Toggle Sidebar\"\n tabIndex={-1}\n onClick={toggleSidebar}\n title=\"Toggle Sidebar\"\n className={cn(\n 'absolute inset-y-0 z-20 hidden w-4 transition-all motion-reduce:transition-none ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:start-1/2 after:w-[2px] hover:after:bg-sidebar-border sm:flex ltr:-translate-x-1/2 rtl:-translate-x-1/2',\n 'in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize',\n '[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize',\n 'group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full hover:group-data-[collapsible=offcanvas]:bg-sidebar',\n '[[data-side=left][data-collapsible=offcanvas]_&]:-right-2',\n '[[data-side=right][data-collapsible=offcanvas]_&]:-left-2',\n className,\n )}\n {...props}\n />\n );\n};\n\nexport const SidebarInset = ({ className, ...props }: React.ComponentProps<'main'>) => {\n return (\n <main\n data-slot=\"sidebar-inset\"\n className={cn(\n 'relative flex w-full flex-1 flex-col bg-background md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2',\n className,\n )}\n {...props}\n />\n );\n};\n\nexport const SidebarInput = ({ className, ...props }: React.ComponentProps<typeof Input>) => {\n return <Input data-slot=\"sidebar-input\" data-sidebar=\"input\" className={cn('h-8 w-full bg-background shadow-none', className)} {...props} />;\n};\n\nexport const SidebarHeader = ({ className, ...props }: React.ComponentProps<'div'>) => {\n return <div data-slot=\"sidebar-header\" data-sidebar=\"header\" className={cn('flex flex-col gap-2 p-2', className)} {...props} />;\n};\n\nexport const SidebarFooter = ({ className, ...props }: React.ComponentProps<'div'>) => {\n return <div data-slot=\"sidebar-footer\" data-sidebar=\"footer\" className={cn('flex flex-col gap-2 p-2', className)} {...props} />;\n};\n\nexport const SidebarSeparator = ({ className, ...props }: React.ComponentProps<typeof Separator>) => {\n return <Separator data-slot=\"sidebar-separator\" data-sidebar=\"separator\" className={cn('mx-2 w-auto bg-sidebar-border', className)} {...props} />;\n};\n\nexport const SidebarContent = ({ className, ...props }: React.ComponentProps<'div'>) => {\n return (\n <div\n data-slot=\"sidebar-content\"\n data-sidebar=\"content\"\n data-focus=\"inset\"\n className={cn('no-scrollbar flex min-h-0 flex-1 flex-col gap-0 overflow-auto group-data-[collapsible=icon]:overflow-hidden', className)}\n {...props}\n />\n );\n};\n\nexport const SidebarGroup = ({ className, ...props }: React.ComponentProps<'div'>) => {\n return <div data-slot=\"sidebar-group\" data-sidebar=\"group\" className={cn('relative flex w-full min-w-0 flex-col p-2', className)} {...props} />;\n};\n\nexport const SidebarGroupLabel = ({ className, render, ...props }: useRender.ComponentProps<'div'> & React.ComponentProps<'div'>) => {\n return useRender({\n defaultTagName: 'div',\n props: mergeProps<'div'>(\n {\n className: cn(\n 'flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 ring-sidebar-ring outline-hidden transition-[margin,opacity] duration-200 motion-reduce:transition-none ease-linear group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0 focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',\n className,\n ),\n },\n props,\n ),\n render,\n state: {\n slot: 'sidebar-group-label',\n sidebar: 'group-label',\n },\n });\n};\n\nexport const SidebarGroupAction = ({ className, render, ...props }: useRender.ComponentProps<'button'> & React.ComponentProps<'button'>) => {\n return useRender({\n defaultTagName: 'button',\n props: mergeProps<'button'>(\n {\n className: cn(\n 'absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform motion-reduce:transition-none group-data-[collapsible=icon]:hidden after:absolute after:-inset-2 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0',\n className,\n ),\n },\n props,\n ),\n render,\n state: {\n slot: 'sidebar-group-action',\n sidebar: 'group-action',\n },\n });\n};\n\nexport const SidebarGroupContent = ({ className, ...props }: React.ComponentProps<'div'>) => {\n return <div data-slot=\"sidebar-group-content\" data-sidebar=\"group-content\" className={cn('w-full text-sm', className)} {...props} />;\n};\n\nexport const SidebarMenu = ({ className, ...props }: React.ComponentProps<'ul'>) => {\n return <ul data-slot=\"sidebar-menu\" data-sidebar=\"menu\" className={cn('flex w-full min-w-0 flex-col gap-0', className)} {...props} />;\n};\n\nexport const SidebarMenuItem = ({ className, ...props }: React.ComponentProps<'li'>) => {\n return <li data-slot=\"sidebar-menu-item\" data-sidebar=\"menu-item\" className={cn('group/menu-item relative', className)} {...props} />;\n};\n\nconst sidebarMenuButtonVariants = cva(\n 'peer/menu-button group/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm ring-sidebar-ring outline-hidden transition-[width,height,padding] motion-reduce:transition-none group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-accent-foreground data-active:bg-sidebar-accent data-active:font-medium data-active:text-sidebar-accent-foreground [&_svg]:size-4 [&_svg]:shrink-0 [&>span:last-child]:truncate',\n {\n variants: {\n variant: {\n default: 'hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',\n outline:\n 'bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_var(--sidebar-accent)]',\n },\n size: {\n default: 'h-8 text-sm',\n sm: 'h-7 text-xs',\n lg: 'h-12 text-sm group-data-[collapsible=icon]:p-0!',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'default',\n },\n },\n);\n\nexport const SidebarMenuButton = ({\n render,\n isActive = false,\n variant = 'default',\n size = 'default',\n tooltip,\n className,\n ...props\n}: useRender.ComponentProps<'button'> &\n React.ComponentProps<'button'> & {\n isActive?: boolean;\n tooltip?: string | React.ComponentProps<typeof TooltipContent>;\n } & VariantProps<typeof sidebarMenuButtonVariants>) => {\n const { isMobile, state } = useSidebar();\n const comp = useRender({\n defaultTagName: 'button',\n props: mergeProps<'button'>(\n {\n className: cn(sidebarMenuButtonVariants({ variant, size }), className),\n },\n props,\n ),\n render: !tooltip ? render : <TooltipTrigger render={render} />,\n state: {\n slot: 'sidebar-menu-button',\n sidebar: 'menu-button',\n size,\n active: isActive,\n },\n });\n\n if (!tooltip) {\n return comp;\n }\n\n if (typeof tooltip === 'string') {\n tooltip = {\n children: tooltip,\n };\n }\n\n return (\n <Tooltip>\n {comp}\n <TooltipContent side=\"right\" align=\"center\" hidden={state !== 'collapsed' || isMobile} {...tooltip} />\n </Tooltip>\n );\n};\n\nexport const SidebarMenuAction = ({\n className,\n render,\n showOnHover = false,\n ...props\n}: useRender.ComponentProps<'button'> &\n React.ComponentProps<'button'> & {\n showOnHover?: boolean;\n }) => {\n return useRender({\n defaultTagName: 'button',\n props: mergeProps<'button'>(\n {\n className: cn(\n 'absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform motion-reduce:transition-none group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 after:absolute after:-inset-2 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0',\n showOnHover &&\n 'group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 peer-data-active/menu-button:text-sidebar-accent-foreground aria-expanded:opacity-100 md:opacity-0',\n className,\n ),\n },\n props,\n ),\n render,\n state: {\n slot: 'sidebar-menu-action',\n sidebar: 'menu-action',\n },\n });\n};\n\nexport const SidebarMenuBadge = ({ className, ...props }: React.ComponentProps<'div'>) => {\n return (\n <div\n data-slot=\"sidebar-menu-badge\"\n data-sidebar=\"menu-badge\"\n className={cn(\n 'pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium text-sidebar-foreground tabular-nums select-none group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 peer-data-active/menu-button:text-sidebar-accent-foreground',\n className,\n )}\n {...props}\n />\n );\n};\n\nexport const SidebarMenuSkeleton = ({\n className,\n showIcon = false,\n ...props\n}: React.ComponentProps<'div'> & {\n showIcon?: boolean;\n}) => {\n // Random width between 50 to 90%.\n const [width] = React.useState(() => {\n return `${Math.floor(Math.random() * 40) + 50}%`;\n });\n\n return (\n <div\n data-slot=\"sidebar-menu-skeleton\"\n data-sidebar=\"menu-skeleton\"\n className={cn('flex h-8 items-center gap-2 rounded-md px-2', className)}\n {...props}\n >\n {showIcon && <Skeleton className=\"size-4 rounded-md\" data-sidebar=\"menu-skeleton-icon\" />}\n <Skeleton\n className=\"h-4 max-w-(--skeleton-width) flex-1\"\n data-sidebar=\"menu-skeleton-text\"\n style={\n {\n '--skeleton-width': width,\n } as React.CSSProperties\n }\n />\n </div>\n );\n};\n\nexport const SidebarMenuSub = ({ className, ...props }: React.ComponentProps<'ul'>) => {\n return (\n <ul\n data-slot=\"sidebar-menu-sub\"\n data-sidebar=\"menu-sub\"\n className={cn(\n 'mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5 group-data-[collapsible=icon]:hidden',\n className,\n )}\n {...props}\n />\n );\n};\n\nexport const SidebarMenuSubItem = ({ className, ...props }: React.ComponentProps<'li'>) => {\n return <li data-slot=\"sidebar-menu-sub-item\" data-sidebar=\"menu-sub-item\" className={cn('group/menu-sub-item relative', className)} {...props} />;\n};\n\nexport const SidebarMenuSubButton = ({\n render,\n size = 'md',\n isActive = false,\n className,\n ...props\n}: useRender.ComponentProps<'a'> &\n React.ComponentProps<'a'> & {\n size?: 'sm' | 'md';\n isActive?: boolean;\n }) => {\n return useRender({\n defaultTagName: 'a',\n props: mergeProps<'a'>(\n {\n className: cn(\n 'flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground ring-sidebar-ring outline-hidden group-data-[collapsible=icon]:hidden hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[size=md]:text-sm data-[size=sm]:text-xs data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground',\n className,\n ),\n },\n props,\n ),\n render,\n state: {\n slot: 'sidebar-menu-sub-button',\n sidebar: 'menu-sub-button',\n size,\n active: isActive,\n },\n });\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sidebar.js","names":[],"sources":["../../../../../../../ui/src/components/ui/sidebar/sidebar.tsx"],"sourcesContent":["import { mergeProps } from '@base-ui/react/merge-props';\nimport { useRender } from '@base-ui/react/use-render';\nimport { useIsMobile } from '@edyn/ui/hooks/use-mobile';\nimport { cn } from '@edyn/ui/lib/utils';\nimport { IconLayoutSidebar } from '@tabler/icons-react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport * as React from 'react';\nimport { Button } from '../button';\nimport { Input } from '../input';\nimport { Separator } from '../separator';\nimport { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '../sheet';\nimport { Skeleton } from '../skeleton';\nimport { Tooltip, TooltipContent, TooltipTrigger } from '../tooltip';\n\nconst SIDEBAR_COOKIE_NAME = 'sidebar_state';\nconst SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;\nconst SIDEBAR_WIDTH = '16rem';\nconst SIDEBAR_WIDTH_MOBILE = '18rem';\nconst SIDEBAR_WIDTH_ICON = '3rem';\nconst SIDEBAR_KEYBOARD_SHORTCUT = 'b';\n\ntype TSidebarContextProps = {\n state: 'expanded' | 'collapsed';\n open: boolean;\n setOpen: (open: boolean) => void;\n openMobile: boolean;\n setOpenMobile: (open: boolean) => void;\n isMobile: boolean;\n toggleSidebar: () => void;\n};\n\nconst SidebarContext = React.createContext<TSidebarContextProps | null>(null);\n\nexport const useSidebar = () => {\n const context = React.useContext(SidebarContext);\n if (!context) {\n throw new Error('useSidebar must be used within a SidebarProvider.');\n }\n\n return context;\n};\n\nexport const SidebarProvider = ({\n defaultOpen = true,\n open: openProp,\n onOpenChange: setOpenProp,\n className,\n style,\n children,\n ...props\n}: React.ComponentProps<'div'> & {\n defaultOpen?: boolean;\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n}) => {\n const isMobile = useIsMobile();\n const [openMobile, setOpenMobile] = React.useState(false);\n\n // This is the internal state of the sidebar.\n // We use openProp and setOpenProp for control from outside the component.\n const [_open, _setOpen] = React.useState(defaultOpen);\n const open = openProp ?? _open;\n const setOpen = React.useCallback(\n (value: boolean | ((value: boolean) => boolean)) => {\n const openState = typeof value === 'function' ? value(open) : value;\n if (setOpenProp) {\n setOpenProp(openState);\n } else {\n _setOpen(openState);\n }\n\n // This sets the cookie to keep the sidebar state.\n // biome-ignore lint/suspicious/noDocumentCookie: match shadcn's synchronous, broadly supported persistence contract.\n document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;\n },\n [setOpenProp, open],\n );\n\n // Helper to toggle the sidebar.\n const toggleSidebar = React.useCallback(() => {\n return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open);\n }, [isMobile, setOpen]);\n\n // Adds a keyboard shortcut to toggle the sidebar.\n React.useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {\n event.preventDefault();\n toggleSidebar();\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [toggleSidebar]);\n\n // We add a state so that we can do data-state=\"expanded\" or \"collapsed\".\n // This makes it easier to style the sidebar with Tailwind classes.\n const state = open ? 'expanded' : 'collapsed';\n\n const contextValue = React.useMemo<TSidebarContextProps>(\n () => ({\n state,\n open,\n setOpen,\n isMobile,\n openMobile,\n setOpenMobile,\n toggleSidebar,\n }),\n [state, open, setOpen, isMobile, openMobile, toggleSidebar],\n );\n\n return (\n <SidebarContext.Provider value={contextValue}>\n <div\n data-slot=\"sidebar-wrapper\"\n style={\n {\n '--sidebar-width': SIDEBAR_WIDTH,\n '--sidebar-width-icon': SIDEBAR_WIDTH_ICON,\n ...style,\n } as React.CSSProperties\n }\n className={cn('group/sidebar-wrapper flex min-h-svh w-full has-data-[variant=inset]:bg-sidebar', className)}\n {...props}\n >\n {children}\n </div>\n </SidebarContext.Provider>\n );\n};\n\nexport const Sidebar = ({\n side = 'left',\n variant = 'sidebar',\n collapsible = 'offcanvas',\n className,\n children,\n dir,\n ...props\n}: React.ComponentProps<'div'> & {\n side?: 'left' | 'right';\n variant?: 'sidebar' | 'floating' | 'inset';\n collapsible?: 'offcanvas' | 'icon' | 'none';\n}) => {\n const { isMobile, state, openMobile, setOpenMobile } = useSidebar();\n\n if (collapsible === 'none') {\n return (\n <div data-slot=\"sidebar\" className={cn('flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground', className)} {...props}>\n {children}\n </div>\n );\n }\n\n if (isMobile) {\n return (\n <Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>\n <SheetContent\n dir={dir}\n data-sidebar=\"sidebar\"\n data-slot=\"sidebar\"\n data-mobile=\"true\"\n className=\"w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden\"\n style={\n {\n '--sidebar-width': SIDEBAR_WIDTH_MOBILE,\n } as React.CSSProperties\n }\n side={side}\n >\n <SheetHeader className=\"sr-only\">\n <SheetTitle>Sidebar</SheetTitle>\n <SheetDescription>Displays the mobile sidebar.</SheetDescription>\n </SheetHeader>\n <div className=\"flex h-full w-full flex-col\">{children}</div>\n </SheetContent>\n </Sheet>\n );\n }\n\n return (\n <div\n className=\"group peer hidden text-sidebar-foreground md:block\"\n data-state={state}\n data-collapsible={state === 'collapsed' ? collapsible : ''}\n data-variant={variant}\n data-side={side}\n data-slot=\"sidebar\"\n >\n {/* This is what handles the sidebar gap on desktop */}\n <div\n data-slot=\"sidebar-gap\"\n className={cn(\n 'relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 motion-reduce:transition-none ease-linear',\n 'group-data-[collapsible=offcanvas]:w-0',\n 'group-data-[side=right]:rotate-180',\n variant === 'floating' || variant === 'inset'\n ? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]'\n : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon)',\n )}\n />\n <div\n data-slot=\"sidebar-container\"\n data-side={side}\n className={cn(\n 'fixed inset-y-0 top-(--sidebar-top) z-10 hidden h-[calc(100svh-var(--sidebar-top))] w-(--sidebar-width) transition-[left,right,width] duration-200 motion-reduce:transition-none ease-linear data-[side=left]:left-0 data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] data-[side=right]:right-0 data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)] md:flex',\n // Adjust the padding for floating and inset variants.\n variant === 'floating' || variant === 'inset'\n ? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]'\n : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l',\n className,\n )}\n {...props}\n >\n <div\n data-sidebar=\"sidebar\"\n data-slot=\"sidebar-inner\"\n className=\"flex size-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:shadow-sm group-data-[variant=floating]:ring-1 group-data-[variant=floating]:ring-sidebar-border\"\n >\n {children}\n </div>\n </div>\n </div>\n );\n};\n\nexport const SidebarTrigger = ({ className, onClick, ...props }: React.ComponentProps<typeof Button>) => {\n const { toggleSidebar } = useSidebar();\n\n return (\n <Button\n data-sidebar=\"trigger\"\n data-slot=\"sidebar-trigger\"\n variant=\"ghost\"\n size=\"icon-sm\"\n className={cn(className)}\n onClick={(event) => {\n onClick?.(event);\n toggleSidebar();\n }}\n {...props}\n >\n <IconLayoutSidebar />\n <span className=\"sr-only\">Toggle Sidebar</span>\n </Button>\n );\n};\n\nexport const SidebarRail = ({ className, ...props }: React.ComponentProps<'button'>) => {\n const { toggleSidebar } = useSidebar();\n\n return (\n <button\n data-sidebar=\"rail\"\n data-slot=\"sidebar-rail\"\n aria-label=\"Toggle Sidebar\"\n tabIndex={-1}\n onClick={toggleSidebar}\n title=\"Toggle Sidebar\"\n className={cn(\n 'absolute inset-y-0 z-20 hidden w-4 transition-all motion-reduce:transition-none ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:start-1/2 after:w-[2px] hover:after:bg-sidebar-border sm:flex ltr:-translate-x-1/2 rtl:-translate-x-1/2',\n 'in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize',\n '[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize',\n 'group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full hover:group-data-[collapsible=offcanvas]:bg-sidebar',\n '[[data-side=left][data-collapsible=offcanvas]_&]:-right-2',\n '[[data-side=right][data-collapsible=offcanvas]_&]:-left-2',\n className,\n )}\n {...props}\n />\n );\n};\n\nexport const SidebarInset = ({ className, ...props }: React.ComponentProps<'main'>) => {\n return (\n <main\n data-slot=\"sidebar-inset\"\n className={cn(\n 'relative flex w-full flex-1 flex-col bg-background md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2',\n className,\n )}\n {...props}\n />\n );\n};\n\nexport const SidebarInput = ({ className, ...props }: React.ComponentProps<typeof Input>) => {\n return <Input data-slot=\"sidebar-input\" data-sidebar=\"input\" className={cn('h-8 w-full bg-background shadow-none', className)} {...props} />;\n};\n\nexport const SidebarHeader = ({ className, ...props }: React.ComponentProps<'div'>) => {\n return <div data-slot=\"sidebar-header\" data-sidebar=\"header\" className={cn('flex flex-col gap-2 p-2', className)} {...props} />;\n};\n\nexport const SidebarFooter = ({ className, ...props }: React.ComponentProps<'div'>) => {\n return <div data-slot=\"sidebar-footer\" data-sidebar=\"footer\" className={cn('flex flex-col gap-2 p-2', className)} {...props} />;\n};\n\nexport const SidebarSeparator = ({ className, ...props }: React.ComponentProps<typeof Separator>) => {\n return <Separator data-slot=\"sidebar-separator\" data-sidebar=\"separator\" className={cn('mx-2 w-auto bg-sidebar-border', className)} {...props} />;\n};\n\nexport const SidebarContent = ({ className, ...props }: React.ComponentProps<'div'>) => {\n return (\n <div\n data-slot=\"sidebar-content\"\n data-sidebar=\"content\"\n data-focus=\"inset\"\n className={cn('no-scrollbar flex min-h-0 flex-1 flex-col gap-0 overflow-auto group-data-[collapsible=icon]:overflow-hidden', className)}\n {...props}\n />\n );\n};\n\nexport const SidebarGroup = ({ className, ...props }: React.ComponentProps<'div'>) => {\n return <div data-slot=\"sidebar-group\" data-sidebar=\"group\" className={cn('relative flex w-full min-w-0 flex-col p-2', className)} {...props} />;\n};\n\nexport const SidebarGroupLabel = ({ className, render, ...props }: useRender.ComponentProps<'div'> & React.ComponentProps<'div'>) => {\n return useRender({\n defaultTagName: 'div',\n props: mergeProps<'div'>(\n {\n className: cn(\n 'flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 ring-sidebar-ring outline-hidden transition-[margin,opacity] duration-200 motion-reduce:transition-none ease-linear group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0 focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',\n className,\n ),\n },\n props,\n ),\n render,\n state: {\n slot: 'sidebar-group-label',\n sidebar: 'group-label',\n },\n });\n};\n\nexport const SidebarGroupAction = ({ className, render, ...props }: useRender.ComponentProps<'button'> & React.ComponentProps<'button'>) => {\n return useRender({\n defaultTagName: 'button',\n props: mergeProps<'button'>(\n {\n className: cn(\n 'absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform motion-reduce:transition-none group-data-[collapsible=icon]:hidden after:absolute after:-inset-2 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0',\n className,\n ),\n },\n props,\n ),\n render,\n state: {\n slot: 'sidebar-group-action',\n sidebar: 'group-action',\n },\n });\n};\n\nexport const SidebarGroupContent = ({ className, ...props }: React.ComponentProps<'div'>) => {\n return <div data-slot=\"sidebar-group-content\" data-sidebar=\"group-content\" className={cn('w-full text-sm', className)} {...props} />;\n};\n\nexport const SidebarMenu = ({ className, ...props }: React.ComponentProps<'ul'>) => {\n return <ul data-slot=\"sidebar-menu\" data-sidebar=\"menu\" className={cn('flex w-full min-w-0 flex-col gap-0', className)} {...props} />;\n};\n\nexport const SidebarMenuItem = ({ className, ...props }: React.ComponentProps<'li'>) => {\n return <li data-slot=\"sidebar-menu-item\" data-sidebar=\"menu-item\" className={cn('group/menu-item relative', className)} {...props} />;\n};\n\nconst sidebarMenuButtonVariants = cva(\n 'peer/menu-button group/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm ring-sidebar-ring outline-hidden transition-[width,height,padding] motion-reduce:transition-none group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-accent-foreground data-active:bg-sidebar-accent data-active:font-medium data-active:text-sidebar-accent-foreground [&_svg]:size-4 [&_svg]:shrink-0 [&>span:last-child]:truncate',\n {\n variants: {\n variant: {\n default: 'hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',\n outline:\n 'bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_var(--sidebar-accent)]',\n },\n size: {\n default: 'h-8 text-sm',\n sm: 'h-7 text-xs',\n lg: 'h-12 text-sm group-data-[collapsible=icon]:p-0!',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'default',\n },\n },\n);\n\nexport const SidebarMenuButton = ({\n render,\n isActive = false,\n variant = 'default',\n size = 'default',\n tooltip,\n className,\n ...props\n}: useRender.ComponentProps<'button'> &\n React.ComponentProps<'button'> & {\n isActive?: boolean;\n tooltip?: string | React.ComponentProps<typeof TooltipContent>;\n } & VariantProps<typeof sidebarMenuButtonVariants>) => {\n const { isMobile, state } = useSidebar();\n const comp = useRender({\n defaultTagName: 'button',\n props: mergeProps<'button'>(\n {\n className: cn(sidebarMenuButtonVariants({ variant, size }), className),\n },\n props,\n ),\n render: !tooltip ? render : <TooltipTrigger render={render} />,\n state: {\n slot: 'sidebar-menu-button',\n sidebar: 'menu-button',\n size,\n active: isActive,\n },\n });\n\n if (!tooltip) {\n return comp;\n }\n\n if (typeof tooltip === 'string') {\n tooltip = {\n children: tooltip,\n };\n }\n\n return (\n <Tooltip>\n {comp}\n <TooltipContent side=\"right\" align=\"center\" hidden={state !== 'collapsed' || isMobile} {...tooltip} />\n </Tooltip>\n );\n};\n\nexport const SidebarMenuAction = ({\n className,\n render,\n showOnHover = false,\n ...props\n}: useRender.ComponentProps<'button'> &\n React.ComponentProps<'button'> & {\n showOnHover?: boolean;\n }) => {\n return useRender({\n defaultTagName: 'button',\n props: mergeProps<'button'>(\n {\n className: cn(\n 'absolute top-1 right-0.5 flex aspect-square w-6 items-center justify-center rounded-[calc(var(--radius-md)+2px)] bg-clip-content p-0.5 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform motion-reduce:transition-none group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[size=default]/menu-button:top-1 peer-data-[size=lg]/menu-button:top-2 peer-data-[size=sm]/menu-button:top-0.5 after:absolute after:-inset-1.5 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0',\n showOnHover &&\n 'group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 peer-data-active/menu-button:text-sidebar-accent-foreground aria-expanded:opacity-100 md:opacity-0',\n className,\n ),\n },\n props,\n ),\n render,\n state: {\n slot: 'sidebar-menu-action',\n sidebar: 'menu-action',\n },\n });\n};\n\nexport const SidebarMenuBadge = ({ className, ...props }: React.ComponentProps<'div'>) => {\n return (\n <div\n data-slot=\"sidebar-menu-badge\"\n data-sidebar=\"menu-badge\"\n className={cn(\n 'pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium text-sidebar-foreground tabular-nums select-none group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 peer-data-active/menu-button:text-sidebar-accent-foreground',\n className,\n )}\n {...props}\n />\n );\n};\n\nexport const SidebarMenuSkeleton = ({\n className,\n showIcon = false,\n ...props\n}: React.ComponentProps<'div'> & {\n showIcon?: boolean;\n}) => {\n // Random width between 50 to 90%.\n const [width] = React.useState(() => {\n return `${Math.floor(Math.random() * 40) + 50}%`;\n });\n\n return (\n <div\n data-slot=\"sidebar-menu-skeleton\"\n data-sidebar=\"menu-skeleton\"\n className={cn('flex h-8 items-center gap-2 rounded-md px-2', className)}\n {...props}\n >\n {showIcon && <Skeleton className=\"size-4 rounded-md\" data-sidebar=\"menu-skeleton-icon\" />}\n <Skeleton\n className=\"h-4 max-w-(--skeleton-width) flex-1\"\n data-sidebar=\"menu-skeleton-text\"\n style={\n {\n '--skeleton-width': width,\n } as React.CSSProperties\n }\n />\n </div>\n );\n};\n\nexport const SidebarMenuSub = ({ className, ...props }: React.ComponentProps<'ul'>) => {\n return (\n <ul\n data-slot=\"sidebar-menu-sub\"\n data-sidebar=\"menu-sub\"\n className={cn(\n 'mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5 group-data-[collapsible=icon]:hidden',\n className,\n )}\n {...props}\n />\n );\n};\n\nexport const SidebarMenuSubItem = ({ className, ...props }: React.ComponentProps<'li'>) => {\n return <li data-slot=\"sidebar-menu-sub-item\" data-sidebar=\"menu-sub-item\" className={cn('group/menu-sub-item relative', className)} {...props} />;\n};\n\nexport const SidebarMenuSubButton = ({\n render,\n size = 'md',\n isActive = false,\n className,\n ...props\n}: useRender.ComponentProps<'a'> &\n React.ComponentProps<'a'> & {\n size?: 'sm' | 'md';\n isActive?: boolean;\n }) => {\n return useRender({\n defaultTagName: 'a',\n props: mergeProps<'a'>(\n {\n className: cn(\n 'flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground ring-sidebar-ring outline-hidden group-data-[collapsible=icon]:hidden hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[size=md]:text-sm data-[size=sm]:text-xs data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground',\n className,\n ),\n },\n props,\n ),\n render,\n state: {\n slot: 'sidebar-menu-sub-button',\n sidebar: 'menu-sub-button',\n size,\n active: isActive,\n },\n });\n};\n"],"mappings":"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"}
|
|
@@ -56,7 +56,21 @@ var TabsList = ({ className, variant = "default", children, ref, ...props }) =>
|
|
|
56
56
|
if (!horizontal || !viewport || !list) return;
|
|
57
57
|
const revealSelected = () => reveal(viewport, list.querySelector("[aria-selected=\"true\"]"));
|
|
58
58
|
const revealFocused = (event) => reveal(viewport, event.target.closest("[role=\"tab\"]"));
|
|
59
|
+
let reader = false;
|
|
60
|
+
const take = () => {
|
|
61
|
+
reader = true;
|
|
62
|
+
};
|
|
63
|
+
const sizes = new ResizeObserver(() => reader || revealSelected());
|
|
64
|
+
const events = [
|
|
65
|
+
"wheel",
|
|
66
|
+
"pointerdown",
|
|
67
|
+
"keydown",
|
|
68
|
+
"touchstart"
|
|
69
|
+
];
|
|
59
70
|
revealSelected();
|
|
71
|
+
sizes.observe(list);
|
|
72
|
+
sizes.observe(viewport);
|
|
73
|
+
for (const name of events) viewport.addEventListener(name, take, { passive: true });
|
|
60
74
|
const observer = new MutationObserver(revealSelected);
|
|
61
75
|
observer.observe(list, {
|
|
62
76
|
subtree: true,
|
|
@@ -66,6 +80,8 @@ var TabsList = ({ className, variant = "default", children, ref, ...props }) =>
|
|
|
66
80
|
list.addEventListener("focusin", revealFocused);
|
|
67
81
|
return () => {
|
|
68
82
|
observer.disconnect();
|
|
83
|
+
sizes.disconnect();
|
|
84
|
+
for (const name of events) viewport.removeEventListener(name, take);
|
|
69
85
|
list.removeEventListener("focusin", revealFocused);
|
|
70
86
|
};
|
|
71
87
|
}, [horizontal]);
|