@rebasepro/ui 0.25.0 → 0.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -241,9 +241,9 @@ var Select = forwardRef(({ inputRef, open, name, fullWidth = false, id, onOpenCh
241
241
  asChild: true,
242
242
  children: /* @__PURE__ */ jsx(ChevronDownIcon, {
243
243
  size: size === "large" ? iconSize.medium : iconSize.small,
244
- className: cls("transition", open ? "rotate-180" : "", {
245
- "px-2": size === "large",
246
- "px-1": size === "medium" || size === "small"
244
+ className: cls("shrink-0 transition", open ? "rotate-180" : "", {
245
+ "ml-2": size === "large",
246
+ "ml-1": size === "medium" || size === "small"
247
247
  })
248
248
  })
249
249
  })]
@@ -1 +1 @@
1
- {"version":3,"file":"Select.es.js","names":[],"sources":["../../src/components/Select.tsx"],"sourcesContent":["\n\"use client\";\nimport { CheckIcon, ChevronDownIcon } from \"lucide-react\";\nimport { iconSize } from \"../icons/Icon\";\nimport React, { ChangeEvent, Children, forwardRef, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport * as SelectPrimitive from \"@radix-ui/react-select\";\nimport {\n defaultBorderMixin,\n fieldBackgroundDisabledMixin,\n fieldBackgroundHoverMixin,\n fieldBackgroundInvisibleMixin,\n fieldBackgroundMixin,\n focusedDisabled\n} from \"../styles\";\n\nimport { cls } from \"../util\";\nimport { SelectInputLabel } from \"./common/SelectInputLabel\";\nimport { usePortalContainer } from \"../hooks/PortalContainerContext\";\n\nexport type SelectValue = string | number | boolean;\n\n/**\n * Above this many options a closed select renders only the one it shows.\n *\n * Radix renders a closed select's items anyway — into a detached fragment, so\n * that a letter typed on the focused trigger can pick one, and inside a form\n * into the hidden native `<select>` as well. An enum field paid for its whole\n * list on every render of the form around it: 500 options were 100 ms to mount\n * and 17 ms per re-render in jsdom, linear in the count. Small lists keep\n * Radix's own behaviour untouched; long ones answer typed letters here\n * ({@link findNextOption}) and render their items when opened.\n */\nconst LAZY_OPTIONS_THRESHOLD = 50;\n\n/** How long typed letters accumulate into one search — Radix's own window. */\nconst TYPEAHEAD_RESET_MS = 1000;\n\ntype SelectOption = {\n element: React.ReactElement<SelectItemProps<SelectValue>>,\n value: string,\n disabled: boolean\n};\n\n/**\n * The `SelectItem`s among `children`, in document order: through fragments,\n * `SelectGroup`s and any other element wrapping them. An item rendered by a\n * component of the caller's own is not seen, so such a list never counts as\n * long and keeps the default behaviour.\n */\nfunction collectOptions(children: React.ReactNode, into: SelectOption[] = []): SelectOption[] {\n Children.forEach(children, (child) => {\n if (!React.isValidElement(child)) return;\n if (child.type === SelectItem) {\n const props = child.props as SelectItemProps<SelectValue>;\n into.push({\n element: child as React.ReactElement<SelectItemProps<SelectValue>>,\n value: String(props.value),\n disabled: Boolean(props.disabled)\n });\n return;\n }\n const nested = (child.props as { children?: React.ReactNode }).children;\n if (nested !== undefined) collectOptions(nested, into);\n });\n return into;\n}\n\n/** The text a node would render, as far as its elements' own children say. */\nfunction textOf(node: React.ReactNode): string {\n if (node === null || node === undefined || typeof node === \"boolean\") return \"\";\n if (typeof node === \"string\" || typeof node === \"number\") return String(node);\n if (Array.isArray(node)) return node.map(textOf).join(\"\");\n if (React.isValidElement(node)) return textOf((node.props as { children?: React.ReactNode }).children);\n return \"\";\n}\n\n/**\n * What a typed letter matches an option against: its `textValue`, else the\n * text of its children, else its value. Radix reads the rendered text, which\n * an unrendered item does not have — an item whose label is drawn by a\n * component (a chip) passes `textValue`.\n */\nfunction optionLabel(option: SelectOption): string {\n const { textValue, children } = option.element.props;\n if (textValue !== undefined) return textValue;\n return textOf(children).trim() || option.value;\n}\n\n/**\n * Radix's `findNextItem`, over options that are not rendered: the next option\n * after the current one whose label starts with the search; the same letter\n * typed again cycles through the options it starts.\n */\nfunction findNextOption(options: SelectOption[], search: string, current: SelectOption | undefined): SelectOption | undefined {\n if (options.length === 0) return undefined;\n const isRepeated = search.length > 1 && Array.from(search).every((char) => char === search[0]);\n const normalizedSearch = (isRepeated ? search[0] : search).toLowerCase();\n const start = Math.max(current ? options.indexOf(current) : -1, 0);\n let wrapped = options.map((_, index) => options[(start + index) % options.length]);\n if (normalizedSearch.length === 1) wrapped = wrapped.filter((option) => option !== current);\n const next = wrapped.find((option) => optionLabel(option).toLowerCase().startsWith(normalizedSearch));\n return next !== current ? next : undefined;\n}\n\nexport type SelectProps<T extends SelectValue = string> = {\n open?: boolean,\n name?: string,\n fullWidth?: boolean,\n id?: string,\n onOpenChange?: (open: boolean) => void,\n value?: T,\n className?: string,\n inputClassName?: string,\n viewportClassName?: string,\n onChange?: React.EventHandler<ChangeEvent<HTMLSelectElement>>,\n onValueChange?: (updatedValue: T) => void,\n placeholder?: React.ReactNode,\n renderValue?: (value: T) => React.ReactNode,\n size?: \"smallest\" | \"small\" | \"medium\" | \"large\",\n label?: React.ReactNode | string,\n disabled?: boolean,\n error?: boolean,\n position?: \"item-aligned\" | \"popper\",\n endAdornment?: React.ReactNode,\n inputRef?: React.Ref<HTMLButtonElement>,\n padding?: boolean,\n invisible?: boolean,\n /**\n * Whether the trigger draws its own chevron. A table cell turns it off:\n * the cell draws one opener for every kind of editor, in the same place.\n */\n chevron?: boolean,\n /**\n * Replaces everything the trigger draws — the value, the box, its padding,\n * its height and the chevron — with this node. For a caller that frames\n * the select itself: a table cell passes the preview it shows at rest, so\n * selecting the cell moves nothing.\n */\n triggerContent?: React.ReactNode,\n children?: React.ReactNode;\n dataType?: \"string\" | \"number\" | \"boolean\";\n portalContainer?: HTMLElement | null; // Explicitly added to props type if missing\n /**\n * Accessible name for the trigger, for a select whose `label` is a node\n * rather than a string — or which shows no label at all.\n *\n * Without it such a select announces the literal fallback \"Select an\n * option\", which is indistinguishable from every other unlabelled select\n * on the screen.\n */\n \"aria-label\"?: string;\n};\n\nexport const Select = forwardRef<HTMLDivElement, SelectProps>(({\n inputRef,\n open,\n name,\n fullWidth = false,\n id,\n onOpenChange,\n value,\n onChange,\n onValueChange,\n className,\n inputClassName,\n viewportClassName,\n placeholder,\n renderValue,\n label,\n size = \"large\",\n error,\n disabled,\n padding = true,\n position = \"item-aligned\",\n endAdornment,\n invisible,\n chevron = true,\n triggerContent,\n children,\n dataType = \"string\",\n portalContainer: manualContainer, // Rename to avoid confusion\n \"aria-label\": ariaLabel,\n ...props\n}, ref) => {\n\n const [openInternal, setOpenInternal] = useState(open ?? false);\n\n useEffect(() => {\n setOpenInternal(open ?? false);\n }, [open]);\n\n // Get the portal container from context\n const contextContainer = usePortalContainer();\n\n // Resolve final container (Manual Prop > Context Container > Undefined/Body)\n const finalContainer = (manualContainer ?? contextContainer ?? undefined) as HTMLElement | undefined;\n\n const onValueChangeInternal = useCallback((newValue: string) => {\n let typedValue: SelectValue = newValue;\n if (dataType === \"boolean\") {\n if (newValue === \"true\") typedValue = true;\n else if (newValue === \"false\") typedValue = false;\n } else if (dataType === \"number\") {\n if (!isNaN(Number(newValue)) && newValue.trim() !== \"\") typedValue = Number(newValue);\n }\n\n onValueChange?.(typedValue as SelectValue & typeof value);\n if (onChange) {\n const event = {\n target: {\n name,\n value: typedValue\n }\n } as ChangeEvent<HTMLSelectElement>;\n onChange(event);\n }\n }, [onChange, onValueChange, name, dataType]);\n\n const hasValue = Array.isArray(value) ? value.length > 0 : (value != null && value !== \"\" && value !== undefined);\n const stringValue = value !== undefined ? String(value) : undefined;\n\n // A long list renders its items only while open; closed, it renders the\n // one it shows, which keeps that option in the native `<select>` a form\n // submits. See LAZY_OPTIONS_THRESHOLD.\n const options = useMemo(() => collectOptions(children), [children]);\n const lazyOptions = options.length > LAZY_OPTIONS_THRESHOLD;\n const renderedOptions = !lazyOptions || openInternal\n ? children\n : (options.find((option) => option.value === stringValue)?.element ?? null);\n\n // Type-ahead on the closed trigger of a long list, where Radix has no\n // rendered items to search. Radix's own handler still runs after this one\n // and finds nothing to change; it keeps its own buffer in step, so a space\n // typed mid-search does not open the list, as before.\n const typeahead = useRef<{ search: string, timer?: ReturnType<typeof setTimeout> }>({ search: \"\" });\n useEffect(() => () => clearTimeout(typeahead.current.timer), []);\n const resetTypeahead = useCallback(() => {\n clearTimeout(typeahead.current.timer);\n typeahead.current.search = \"\";\n }, []);\n const onTriggerKeyDown = useCallback((event: React.KeyboardEvent<HTMLButtonElement>) => {\n if (!lazyOptions || disabled) return;\n if (event.ctrlKey || event.altKey || event.metaKey || event.key.length !== 1) return;\n const state = typeahead.current;\n // A space starts nothing: it opens the list.\n if (event.key === \" \" && state.search === \"\") return;\n const search = state.search + event.key;\n state.search = search;\n clearTimeout(state.timer);\n state.timer = setTimeout(() => {\n state.search = \"\";\n }, TYPEAHEAD_RESET_MS);\n const enabled = options.filter((option) => !option.disabled);\n const current = enabled.find((option) => option.value === stringValue);\n const next = findNextOption(enabled, search, current);\n if (next) onValueChangeInternal(next.value);\n }, [lazyOptions, disabled, options, stringValue, onValueChangeInternal]);\n\n // Inside a form Radix mirrors the value into a hidden native `<select>` and\n // reports whatever that select then holds. A closed long list has only its\n // shown option in there, so when the value moves to another option — typed,\n // or set by the form — the select has no match for one commit and reports\n // \"\", a change nobody made, which would clear the field. A real \"\" can only\n // come from an option whose value is \"\".\n const hasEmptyOption = useMemo(() => options.some((option) => option.value === \"\"), [options]);\n const onRootValueChange = useCallback((newValue: string) => {\n if (lazyOptions && newValue === \"\" && !hasEmptyOption) return;\n onValueChangeInternal(newValue);\n }, [lazyOptions, hasEmptyOption, onValueChangeInternal]);\n\n const displayChildren = useMemo(() => {\n if (!hasValue || renderValue) return null;\n\n // Find the child that matches the current value to display its content\n let found: React.ReactNode = null;\n Children.forEach(children, (child) => {\n if (React.isValidElement<SelectItemProps>(child) && String(child.props.value) === String(value)) {\n found = child.props.children;\n }\n });\n return found;\n }, [children, hasValue, renderValue, value]);\n\n // Radix reads a value of \"\" as nothing selected and shows the placeholder\n // slot, whatever the Value's children say. So an item whose value really is\n // \"\" (an \"inherit the default\" choice) is shown through that slot, or the\n // trigger stays blank while it is the selected one.\n const emptyItemDisplay = useMemo(() => {\n if (stringValue !== \"\") return null;\n const emptyItem = Children.toArray(children).find((child): child is React.ReactElement<SelectItemProps> =>\n React.isValidElement<SelectItemProps>(child) && String(child.props.value) === \"\");\n if (!emptyItem) return null;\n return value !== undefined && renderValue ? renderValue(value) : emptyItem.props.children;\n }, [children, renderValue, stringValue, value]);\n\n const bare = triggerContent !== undefined;\n // Radix's value slot is an inline `span` that drops the class it is\n // given, so a chip inside it sits on a text baseline and the slot stands\n // a pixel taller than the chip. A block of its own takes it off the line.\n const bareContent = bare ? <div className={\"flex w-full\"}>{triggerContent}</div> : undefined;\n\n const controlHeight = {\n \"min-h-[28px]\": size === \"smallest\",\n \"min-h-[32px]\": size === \"small\",\n \"min-h-[40px]\": size === \"medium\",\n \"min-h-[48px]\": size === \"large\"\n };\n\n return (\n <SelectPrimitive.Root\n name={name}\n value={stringValue}\n open={openInternal}\n disabled={disabled}\n onValueChange={onRootValueChange}\n onOpenChange={(open) => {\n if (open) resetTypeahead();\n onOpenChange?.(open);\n setOpenInternal(open);\n }}\n {...props}>\n {typeof label === \"string\" ? <SelectInputLabel error={error}>{label}</SelectInputLabel> : label}\n <div className={cls(\n \"select-none\",\n !bare && \"rounded-lg text-sm\",\n !bare && (invisible ? fieldBackgroundInvisibleMixin : fieldBackgroundMixin),\n // An invisible field has no hover fill of its own: whatever frames it (a table cell) carries the hover.\n !bare && (disabled ? fieldBackgroundDisabledMixin : (invisible ? \"\" : fieldBackgroundHoverMixin)),\n \"relative flex items-center\",\n className,\n !bare && controlHeight,\n {\n \"w-fit\": !fullWidth,\n \"w-full\": fullWidth\n }\n )}>\n <SelectPrimitive.Trigger\n ref={inputRef}\n id={id}\n asChild={false}\n type=\"button\"\n onKeyDown={onTriggerKeyDown}\n // A `label` that is an element — which every entity form\n // passes, because the label carries the property's type\n // icon — fell through to a hard-coded English literal, so\n // every enum field in every form announced itself as\n // \"Select an option\". Callers pass `aria-label` now; the\n // literal stays as the last resort, and is unreachable for\n // anything that names itself.\n aria-label={ariaLabel ?? (typeof label === \"string\" ? label : (typeof renderValue === \"string\" ? renderValue : \"Select an option\"))}\n aria-invalid={error || undefined}\n className={cls(\n \"h-full\",\n padding && !bare ? {\n \"px-4\": size === \"large\",\n \"px-3\": size === \"medium\",\n \"px-2\": size === \"small\" || size === \"smallest\"\n } : \"\",\n \"outline-hidden focus:outline-hidden\",\n \"outline-none focus:outline-none\",\n \"select-none\",\n !bare && [\n \"rounded-lg text-sm\",\n error ? \"text-red-500 dark:text-red-600\" : \"focus:text-text-primary dark:focus:text-text-primary-dark\",\n error ? \"border border-red-500 dark:border-red-600\" : \"\",\n disabled ? \"text-surface-accent-600 dark:text-surface-accent-400\" : \"text-surface-accent-800 dark:text-white\",\n controlHeight\n ],\n \"relative flex flex-row items-center\",\n {\n \"w-full\": fullWidth,\n \"w-fit\": !fullWidth\n },\n inputClassName\n )}\n >\n <div\n ref={ref}\n className={cls(\n \"flex-grow max-w-full flex flex-row gap-2 items-center\",\n \"overflow-visible\",\n !bare && controlHeight\n )}>\n <SelectPrimitive.Value\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n }}\n placeholder={bareContent ?? emptyItemDisplay ?? placeholder}\n className={\"w-full\"}>\n\n {bareContent ?? (hasValue && value !== undefined && renderValue\n ? renderValue(value)\n : (displayChildren || placeholder)\n )}\n\n </SelectPrimitive.Value>\n </div>\n\n {chevron && !bare && <SelectPrimitive.Icon asChild>\n <ChevronDownIcon size={size === \"large\" ? iconSize.medium : iconSize.small}\n className={cls(\"transition\", open ? \"rotate-180\" : \"\", {\n \"px-2\": size === \"large\",\n \"px-1\": size === \"medium\" || size === \"small\"\n })}/>\n </SelectPrimitive.Icon>}\n </SelectPrimitive.Trigger>\n\n {endAdornment && (\n <div\n className={cls(\"h-full flex items-center absolute right-0 pr-12\")}\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n }}>\n {endAdornment}\n </div>\n )}\n </div>\n\n {/* Pass the calculated finalContainer */}\n <SelectPrimitive.Portal container={finalContainer}>\n <SelectPrimitive.Content position={position}\n className={cls(focusedDisabled, \"z-50 relative overflow-hidden border bg-surface-card p-2 rounded-lg shadow-lg\", defaultBorderMixin)}>\n <SelectPrimitive.Viewport className={cls(\"p-1\", viewportClassName)}\n style={{ maxHeight: \"var(--radix-select-content-available-height)\" }}>\n {renderedOptions}\n </SelectPrimitive.Viewport>\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n </SelectPrimitive.Root>\n );\n});\n\nSelect.displayName = \"Select\";\n\nexport type SelectItemProps<T extends SelectValue = string> = {\n value: T,\n children?: React.ReactNode,\n disabled?: boolean,\n className?: string,\n /**\n * The text a letter typed on the focused select matches this option by.\n * Defaults to the option's text. Set it when the option is drawn by a\n * component (a chip, an icon with a name inside it): a closed select with\n * more than 50 options does not render them, so their text cannot be read.\n */\n textValue?: string,\n};\n\nexport const SelectItem = React.memo(function SelectItem<T extends SelectValue = string>({\n value,\n children,\n disabled,\n className,\n textValue\n}: SelectItemProps<T>) {\n // Convert value to string for Radix UI\n const stringValue = String(value);\n\n return <SelectPrimitive.Item\n key={stringValue}\n value={stringValue}\n disabled={disabled}\n textValue={textValue}\n className={cls(\n \"w-full\",\n \"relative flex items-center p-2 rounded-md text-sm text-surface-accent-700 dark:text-surface-accent-300\",\n \"focus:z-10\",\n \"data-[state=checked]:bg-surface-active focus:bg-surface-hover\",\n \"data-[state=checked]:focus:bg-surface-active\",\n disabled ? \"opacity-50 cursor-not-allowed\" : \"cursor-pointer\",\n \"[&>*]:w-full\",\n \"overflow-visible\",\n className\n )}>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n <div\n className=\"absolute left-1 data-[state=checked]:block hidden\">\n <CheckIcon size={iconSize.smallest}/>\n </div>\n </SelectPrimitive.Item>;\n});\n\nexport type SelectGroupProps = {\n label: React.ReactNode,\n children: React.ReactNode,\n className?: string\n};\n\nexport const SelectGroup = React.memo(function SelectGroup({\n label,\n children,\n className\n}: SelectGroupProps) {\n // The items go inside the group and the label is Radix's own, so the\n // `role=\"group\"` holds its options and is named by the label.\n return <SelectPrimitive.Group className=\"mt-6 first:mt-2\">\n <SelectPrimitive.Label\n className={cls(\n \"text-xs text-surface-accent-900 dark:text-white uppercase tracking-wider font-semibold\",\n \"px-2 py-2\",\n className\n )}>\n {label}\n </SelectPrimitive.Label>\n {children}\n </SelectPrimitive.Group>;\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"Select.es.js","names":[],"sources":["../../src/components/Select.tsx"],"sourcesContent":["\n\"use client\";\nimport { CheckIcon, ChevronDownIcon } from \"lucide-react\";\nimport { iconSize } from \"../icons/Icon\";\nimport React, { ChangeEvent, Children, forwardRef, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport * as SelectPrimitive from \"@radix-ui/react-select\";\nimport {\n defaultBorderMixin,\n fieldBackgroundDisabledMixin,\n fieldBackgroundHoverMixin,\n fieldBackgroundInvisibleMixin,\n fieldBackgroundMixin,\n focusedDisabled\n} from \"../styles\";\n\nimport { cls } from \"../util\";\nimport { SelectInputLabel } from \"./common/SelectInputLabel\";\nimport { usePortalContainer } from \"../hooks/PortalContainerContext\";\n\nexport type SelectValue = string | number | boolean;\n\n/**\n * Above this many options a closed select renders only the one it shows.\n *\n * Radix renders a closed select's items anyway — into a detached fragment, so\n * that a letter typed on the focused trigger can pick one, and inside a form\n * into the hidden native `<select>` as well. An enum field paid for its whole\n * list on every render of the form around it: 500 options were 100 ms to mount\n * and 17 ms per re-render in jsdom, linear in the count. Small lists keep\n * Radix's own behaviour untouched; long ones answer typed letters here\n * ({@link findNextOption}) and render their items when opened.\n */\nconst LAZY_OPTIONS_THRESHOLD = 50;\n\n/** How long typed letters accumulate into one search — Radix's own window. */\nconst TYPEAHEAD_RESET_MS = 1000;\n\ntype SelectOption = {\n element: React.ReactElement<SelectItemProps<SelectValue>>,\n value: string,\n disabled: boolean\n};\n\n/**\n * The `SelectItem`s among `children`, in document order: through fragments,\n * `SelectGroup`s and any other element wrapping them. An item rendered by a\n * component of the caller's own is not seen, so such a list never counts as\n * long and keeps the default behaviour.\n */\nfunction collectOptions(children: React.ReactNode, into: SelectOption[] = []): SelectOption[] {\n Children.forEach(children, (child) => {\n if (!React.isValidElement(child)) return;\n if (child.type === SelectItem) {\n const props = child.props as SelectItemProps<SelectValue>;\n into.push({\n element: child as React.ReactElement<SelectItemProps<SelectValue>>,\n value: String(props.value),\n disabled: Boolean(props.disabled)\n });\n return;\n }\n const nested = (child.props as { children?: React.ReactNode }).children;\n if (nested !== undefined) collectOptions(nested, into);\n });\n return into;\n}\n\n/** The text a node would render, as far as its elements' own children say. */\nfunction textOf(node: React.ReactNode): string {\n if (node === null || node === undefined || typeof node === \"boolean\") return \"\";\n if (typeof node === \"string\" || typeof node === \"number\") return String(node);\n if (Array.isArray(node)) return node.map(textOf).join(\"\");\n if (React.isValidElement(node)) return textOf((node.props as { children?: React.ReactNode }).children);\n return \"\";\n}\n\n/**\n * What a typed letter matches an option against: its `textValue`, else the\n * text of its children, else its value. Radix reads the rendered text, which\n * an unrendered item does not have — an item whose label is drawn by a\n * component (a chip) passes `textValue`.\n */\nfunction optionLabel(option: SelectOption): string {\n const { textValue, children } = option.element.props;\n if (textValue !== undefined) return textValue;\n return textOf(children).trim() || option.value;\n}\n\n/**\n * Radix's `findNextItem`, over options that are not rendered: the next option\n * after the current one whose label starts with the search; the same letter\n * typed again cycles through the options it starts.\n */\nfunction findNextOption(options: SelectOption[], search: string, current: SelectOption | undefined): SelectOption | undefined {\n if (options.length === 0) return undefined;\n const isRepeated = search.length > 1 && Array.from(search).every((char) => char === search[0]);\n const normalizedSearch = (isRepeated ? search[0] : search).toLowerCase();\n const start = Math.max(current ? options.indexOf(current) : -1, 0);\n let wrapped = options.map((_, index) => options[(start + index) % options.length]);\n if (normalizedSearch.length === 1) wrapped = wrapped.filter((option) => option !== current);\n const next = wrapped.find((option) => optionLabel(option).toLowerCase().startsWith(normalizedSearch));\n return next !== current ? next : undefined;\n}\n\nexport type SelectProps<T extends SelectValue = string> = {\n open?: boolean,\n name?: string,\n fullWidth?: boolean,\n id?: string,\n onOpenChange?: (open: boolean) => void,\n value?: T,\n className?: string,\n inputClassName?: string,\n viewportClassName?: string,\n onChange?: React.EventHandler<ChangeEvent<HTMLSelectElement>>,\n onValueChange?: (updatedValue: T) => void,\n placeholder?: React.ReactNode,\n renderValue?: (value: T) => React.ReactNode,\n size?: \"smallest\" | \"small\" | \"medium\" | \"large\",\n label?: React.ReactNode | string,\n disabled?: boolean,\n error?: boolean,\n position?: \"item-aligned\" | \"popper\",\n endAdornment?: React.ReactNode,\n inputRef?: React.Ref<HTMLButtonElement>,\n padding?: boolean,\n invisible?: boolean,\n /**\n * Whether the trigger draws its own chevron. A table cell turns it off:\n * the cell draws one opener for every kind of editor, in the same place.\n */\n chevron?: boolean,\n /**\n * Replaces everything the trigger draws — the value, the box, its padding,\n * its height and the chevron — with this node. For a caller that frames\n * the select itself: a table cell passes the preview it shows at rest, so\n * selecting the cell moves nothing.\n */\n triggerContent?: React.ReactNode,\n children?: React.ReactNode;\n dataType?: \"string\" | \"number\" | \"boolean\";\n portalContainer?: HTMLElement | null; // Explicitly added to props type if missing\n /**\n * Accessible name for the trigger, for a select whose `label` is a node\n * rather than a string — or which shows no label at all.\n *\n * Without it such a select announces the literal fallback \"Select an\n * option\", which is indistinguishable from every other unlabelled select\n * on the screen.\n */\n \"aria-label\"?: string;\n};\n\nexport const Select = forwardRef<HTMLDivElement, SelectProps>(({\n inputRef,\n open,\n name,\n fullWidth = false,\n id,\n onOpenChange,\n value,\n onChange,\n onValueChange,\n className,\n inputClassName,\n viewportClassName,\n placeholder,\n renderValue,\n label,\n size = \"large\",\n error,\n disabled,\n padding = true,\n position = \"item-aligned\",\n endAdornment,\n invisible,\n chevron = true,\n triggerContent,\n children,\n dataType = \"string\",\n portalContainer: manualContainer, // Rename to avoid confusion\n \"aria-label\": ariaLabel,\n ...props\n}, ref) => {\n\n const [openInternal, setOpenInternal] = useState(open ?? false);\n\n useEffect(() => {\n setOpenInternal(open ?? false);\n }, [open]);\n\n // Get the portal container from context\n const contextContainer = usePortalContainer();\n\n // Resolve final container (Manual Prop > Context Container > Undefined/Body)\n const finalContainer = (manualContainer ?? contextContainer ?? undefined) as HTMLElement | undefined;\n\n const onValueChangeInternal = useCallback((newValue: string) => {\n let typedValue: SelectValue = newValue;\n if (dataType === \"boolean\") {\n if (newValue === \"true\") typedValue = true;\n else if (newValue === \"false\") typedValue = false;\n } else if (dataType === \"number\") {\n if (!isNaN(Number(newValue)) && newValue.trim() !== \"\") typedValue = Number(newValue);\n }\n\n onValueChange?.(typedValue as SelectValue & typeof value);\n if (onChange) {\n const event = {\n target: {\n name,\n value: typedValue\n }\n } as ChangeEvent<HTMLSelectElement>;\n onChange(event);\n }\n }, [onChange, onValueChange, name, dataType]);\n\n const hasValue = Array.isArray(value) ? value.length > 0 : (value != null && value !== \"\" && value !== undefined);\n const stringValue = value !== undefined ? String(value) : undefined;\n\n // A long list renders its items only while open; closed, it renders the\n // one it shows, which keeps that option in the native `<select>` a form\n // submits. See LAZY_OPTIONS_THRESHOLD.\n const options = useMemo(() => collectOptions(children), [children]);\n const lazyOptions = options.length > LAZY_OPTIONS_THRESHOLD;\n const renderedOptions = !lazyOptions || openInternal\n ? children\n : (options.find((option) => option.value === stringValue)?.element ?? null);\n\n // Type-ahead on the closed trigger of a long list, where Radix has no\n // rendered items to search. Radix's own handler still runs after this one\n // and finds nothing to change; it keeps its own buffer in step, so a space\n // typed mid-search does not open the list, as before.\n const typeahead = useRef<{ search: string, timer?: ReturnType<typeof setTimeout> }>({ search: \"\" });\n useEffect(() => () => clearTimeout(typeahead.current.timer), []);\n const resetTypeahead = useCallback(() => {\n clearTimeout(typeahead.current.timer);\n typeahead.current.search = \"\";\n }, []);\n const onTriggerKeyDown = useCallback((event: React.KeyboardEvent<HTMLButtonElement>) => {\n if (!lazyOptions || disabled) return;\n if (event.ctrlKey || event.altKey || event.metaKey || event.key.length !== 1) return;\n const state = typeahead.current;\n // A space starts nothing: it opens the list.\n if (event.key === \" \" && state.search === \"\") return;\n const search = state.search + event.key;\n state.search = search;\n clearTimeout(state.timer);\n state.timer = setTimeout(() => {\n state.search = \"\";\n }, TYPEAHEAD_RESET_MS);\n const enabled = options.filter((option) => !option.disabled);\n const current = enabled.find((option) => option.value === stringValue);\n const next = findNextOption(enabled, search, current);\n if (next) onValueChangeInternal(next.value);\n }, [lazyOptions, disabled, options, stringValue, onValueChangeInternal]);\n\n // Inside a form Radix mirrors the value into a hidden native `<select>` and\n // reports whatever that select then holds. A closed long list has only its\n // shown option in there, so when the value moves to another option — typed,\n // or set by the form — the select has no match for one commit and reports\n // \"\", a change nobody made, which would clear the field. A real \"\" can only\n // come from an option whose value is \"\".\n const hasEmptyOption = useMemo(() => options.some((option) => option.value === \"\"), [options]);\n const onRootValueChange = useCallback((newValue: string) => {\n if (lazyOptions && newValue === \"\" && !hasEmptyOption) return;\n onValueChangeInternal(newValue);\n }, [lazyOptions, hasEmptyOption, onValueChangeInternal]);\n\n const displayChildren = useMemo(() => {\n if (!hasValue || renderValue) return null;\n\n // Find the child that matches the current value to display its content\n let found: React.ReactNode = null;\n Children.forEach(children, (child) => {\n if (React.isValidElement<SelectItemProps>(child) && String(child.props.value) === String(value)) {\n found = child.props.children;\n }\n });\n return found;\n }, [children, hasValue, renderValue, value]);\n\n // Radix reads a value of \"\" as nothing selected and shows the placeholder\n // slot, whatever the Value's children say. So an item whose value really is\n // \"\" (an \"inherit the default\" choice) is shown through that slot, or the\n // trigger stays blank while it is the selected one.\n const emptyItemDisplay = useMemo(() => {\n if (stringValue !== \"\") return null;\n const emptyItem = Children.toArray(children).find((child): child is React.ReactElement<SelectItemProps> =>\n React.isValidElement<SelectItemProps>(child) && String(child.props.value) === \"\");\n if (!emptyItem) return null;\n return value !== undefined && renderValue ? renderValue(value) : emptyItem.props.children;\n }, [children, renderValue, stringValue, value]);\n\n const bare = triggerContent !== undefined;\n // Radix's value slot is an inline `span` that drops the class it is\n // given, so a chip inside it sits on a text baseline and the slot stands\n // a pixel taller than the chip. A block of its own takes it off the line.\n const bareContent = bare ? <div className={\"flex w-full\"}>{triggerContent}</div> : undefined;\n\n const controlHeight = {\n \"min-h-[28px]\": size === \"smallest\",\n \"min-h-[32px]\": size === \"small\",\n \"min-h-[40px]\": size === \"medium\",\n \"min-h-[48px]\": size === \"large\"\n };\n\n return (\n <SelectPrimitive.Root\n name={name}\n value={stringValue}\n open={openInternal}\n disabled={disabled}\n onValueChange={onRootValueChange}\n onOpenChange={(open) => {\n if (open) resetTypeahead();\n onOpenChange?.(open);\n setOpenInternal(open);\n }}\n {...props}>\n {typeof label === \"string\" ? <SelectInputLabel error={error}>{label}</SelectInputLabel> : label}\n <div className={cls(\n \"select-none\",\n !bare && \"rounded-lg text-sm\",\n !bare && (invisible ? fieldBackgroundInvisibleMixin : fieldBackgroundMixin),\n // An invisible field has no hover fill of its own: whatever frames it (a table cell) carries the hover.\n !bare && (disabled ? fieldBackgroundDisabledMixin : (invisible ? \"\" : fieldBackgroundHoverMixin)),\n \"relative flex items-center\",\n className,\n !bare && controlHeight,\n {\n \"w-fit\": !fullWidth,\n \"w-full\": fullWidth\n }\n )}>\n <SelectPrimitive.Trigger\n ref={inputRef}\n id={id}\n asChild={false}\n type=\"button\"\n onKeyDown={onTriggerKeyDown}\n // A `label` that is an element — which every entity form\n // passes, because the label carries the property's type\n // icon — fell through to a hard-coded English literal, so\n // every enum field in every form announced itself as\n // \"Select an option\". Callers pass `aria-label` now; the\n // literal stays as the last resort, and is unreachable for\n // anything that names itself.\n aria-label={ariaLabel ?? (typeof label === \"string\" ? label : (typeof renderValue === \"string\" ? renderValue : \"Select an option\"))}\n aria-invalid={error || undefined}\n className={cls(\n \"h-full\",\n padding && !bare ? {\n \"px-4\": size === \"large\",\n \"px-3\": size === \"medium\",\n \"px-2\": size === \"small\" || size === \"smallest\"\n } : \"\",\n \"outline-hidden focus:outline-hidden\",\n \"outline-none focus:outline-none\",\n \"select-none\",\n !bare && [\n \"rounded-lg text-sm\",\n error ? \"text-red-500 dark:text-red-600\" : \"focus:text-text-primary dark:focus:text-text-primary-dark\",\n error ? \"border border-red-500 dark:border-red-600\" : \"\",\n disabled ? \"text-surface-accent-600 dark:text-surface-accent-400\" : \"text-surface-accent-800 dark:text-white\",\n controlHeight\n ],\n \"relative flex flex-row items-center\",\n {\n \"w-full\": fullWidth,\n \"w-fit\": !fullWidth\n },\n inputClassName\n )}\n >\n <div\n ref={ref}\n className={cls(\n \"flex-grow max-w-full flex flex-row gap-2 items-center\",\n \"overflow-visible\",\n !bare && controlHeight\n )}>\n <SelectPrimitive.Value\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n }}\n placeholder={bareContent ?? emptyItemDisplay ?? placeholder}\n className={\"w-full\"}>\n\n {bareContent ?? (hasValue && value !== undefined && renderValue\n ? renderValue(value)\n : (displayChildren || placeholder)\n )}\n\n </SelectPrimitive.Value>\n </div>\n\n {/* Spaced with a margin, never padding: under border-box,\n padding on the svg comes out of its width and draws the\n chevron smaller and fainter than the relation field's. */}\n {chevron && !bare && <SelectPrimitive.Icon asChild>\n <ChevronDownIcon size={size === \"large\" ? iconSize.medium : iconSize.small}\n className={cls(\"shrink-0 transition\", open ? \"rotate-180\" : \"\", {\n \"ml-2\": size === \"large\",\n \"ml-1\": size === \"medium\" || size === \"small\"\n })}/>\n </SelectPrimitive.Icon>}\n </SelectPrimitive.Trigger>\n\n {endAdornment && (\n <div\n className={cls(\"h-full flex items-center absolute right-0 pr-12\")}\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n }}>\n {endAdornment}\n </div>\n )}\n </div>\n\n {/* Pass the calculated finalContainer */}\n <SelectPrimitive.Portal container={finalContainer}>\n <SelectPrimitive.Content position={position}\n className={cls(focusedDisabled, \"z-50 relative overflow-hidden border bg-surface-card p-2 rounded-lg shadow-lg\", defaultBorderMixin)}>\n <SelectPrimitive.Viewport className={cls(\"p-1\", viewportClassName)}\n style={{ maxHeight: \"var(--radix-select-content-available-height)\" }}>\n {renderedOptions}\n </SelectPrimitive.Viewport>\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n </SelectPrimitive.Root>\n );\n});\n\nSelect.displayName = \"Select\";\n\nexport type SelectItemProps<T extends SelectValue = string> = {\n value: T,\n children?: React.ReactNode,\n disabled?: boolean,\n className?: string,\n /**\n * The text a letter typed on the focused select matches this option by.\n * Defaults to the option's text. Set it when the option is drawn by a\n * component (a chip, an icon with a name inside it): a closed select with\n * more than 50 options does not render them, so their text cannot be read.\n */\n textValue?: string,\n};\n\nexport const SelectItem = React.memo(function SelectItem<T extends SelectValue = string>({\n value,\n children,\n disabled,\n className,\n textValue\n}: SelectItemProps<T>) {\n // Convert value to string for Radix UI\n const stringValue = String(value);\n\n return <SelectPrimitive.Item\n key={stringValue}\n value={stringValue}\n disabled={disabled}\n textValue={textValue}\n className={cls(\n \"w-full\",\n \"relative flex items-center p-2 rounded-md text-sm text-surface-accent-700 dark:text-surface-accent-300\",\n \"focus:z-10\",\n \"data-[state=checked]:bg-surface-active focus:bg-surface-hover\",\n \"data-[state=checked]:focus:bg-surface-active\",\n disabled ? \"opacity-50 cursor-not-allowed\" : \"cursor-pointer\",\n \"[&>*]:w-full\",\n \"overflow-visible\",\n className\n )}>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n <div\n className=\"absolute left-1 data-[state=checked]:block hidden\">\n <CheckIcon size={iconSize.smallest}/>\n </div>\n </SelectPrimitive.Item>;\n});\n\nexport type SelectGroupProps = {\n label: React.ReactNode,\n children: React.ReactNode,\n className?: string\n};\n\nexport const SelectGroup = React.memo(function SelectGroup({\n label,\n children,\n className\n}: SelectGroupProps) {\n // The items go inside the group and the label is Radix's own, so the\n // `role=\"group\"` holds its options and is named by the label.\n return <SelectPrimitive.Group className=\"mt-6 first:mt-2\">\n <SelectPrimitive.Label\n className={cls(\n \"text-xs text-surface-accent-900 dark:text-white uppercase tracking-wider font-semibold\",\n \"px-2 py-2\",\n className\n )}>\n {label}\n </SelectPrimitive.Label>\n {children}\n </SelectPrimitive.Group>;\n});\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebasepro/ui",
3
- "version": "0.25.0",
3
+ "version": "0.25.1",
4
4
  "description": "Rebase design system — React components and design tokens built on Radix UI, Tailwind CSS and lucide-react",
5
5
  "keywords": [
6
6
  "rebase",