@spark-ui/components
Version:
Spark (Leboncoin design system) components.
467 lines (450 loc) • 12.8 kB
JavaScript
import {
Icon
} from "../chunk-UMUMFMFB.mjs";
import "../chunk-KEGAAGJW.mjs";
import "../chunk-6QCEPQ3U.mjs";
// src/select/SelectContext.tsx
import { useFormFieldControl } from "@spark-ui/components/form-field";
import { useCombinedState } from "@spark-ui/hooks/use-combined-state";
import {
createContext,
useContext,
useEffect,
useId,
useState
} from "react";
// src/select/utils.ts
import { Children, isValidElement } from "react";
var findElement = (children) => (name) => {
const validChildren = Children.toArray(children).filter(isValidElement);
return validChildren.find((child) => {
return getElementDisplayName(child)?.includes(name);
});
};
var getElementDisplayName = (element) => {
return element ? element.type.displayName : "";
};
var getOrderedItems = (children, result = []) => {
Children.forEach(children, (child) => {
if (!isValidElement(child)) return;
if (getElementDisplayName(child) === "Select.Item" || getElementDisplayName(child) === "Select.Placeholder") {
const childProps = child.props;
result.push({
value: childProps.value,
disabled: !!childProps.disabled,
text: childProps.children
});
}
if (child.props.children) {
getOrderedItems(child.props.children, result);
}
});
return result;
};
var getItemsFromChildren = (children) => {
const newMap = /* @__PURE__ */ new Map();
getOrderedItems(children).forEach((itemData) => {
newMap.set(itemData.value, itemData);
});
return newMap;
};
// src/select/SelectContext.tsx
import { jsx } from "react/jsx-runtime";
var SelectContext = createContext(null);
var ID_PREFIX = ":select";
var SelectProvider = ({
children,
defaultValue,
value: valueProp,
onValueChange,
disabled: disabledProp = false,
readOnly: readOnlyProp = false,
state: stateProp,
itemsComponent,
name: nameProp,
required: requiredProp
}) => {
const [value, setValue] = useCombinedState(valueProp, defaultValue, onValueChange);
const [placeholder, setPlaceholder] = useState(void 0);
const [itemsMap, setItemsMap] = useState(getItemsFromChildren(itemsComponent));
const [ariaLabel, setAriaLabel] = useState();
const firstItem = itemsMap.entries().next()?.value?.[1];
const selectedItem = typeof value === "string" ? itemsMap.get(value) : firstItem;
const isControlled = valueProp != null;
const field = useFormFieldControl();
const state = field.state || stateProp;
const internalFieldID = `${ID_PREFIX}-field-${useId()}`;
const fieldId = field.id || internalFieldID;
const fieldLabelId = field.labelId;
const disabled = field.disabled ?? disabledProp;
const readOnly = field.readOnly ?? readOnlyProp;
const name = field.name ?? nameProp;
const required = !!(field.isRequired ?? requiredProp);
useEffect(() => {
const newMap = getItemsFromChildren(itemsComponent);
const previousItems = [...itemsMap.values()];
const newItems = [...newMap.values()];
const hasItemsChanges = previousItems.length !== newItems.length || previousItems.some((item, index) => {
const hasUpdatedValue = item.value !== newItems[index]?.value;
const hasUpdatedText = item.text !== newItems[index]?.text;
return hasUpdatedValue || hasUpdatedText;
});
if (hasItemsChanges) {
setItemsMap(newMap);
}
}, [children]);
return /* @__PURE__ */ jsx(
SelectContext.Provider,
{
value: {
disabled,
readOnly,
itemsMap,
state,
itemsComponent,
selectedItem,
setValue,
isControlled,
onValueChange,
ariaLabel,
setAriaLabel,
fieldId,
fieldLabelId,
name,
required,
placeholder,
setPlaceholder
},
children
}
);
};
var useSelectContext = () => {
const context = useContext(SelectContext);
if (!context) {
throw Error("useSelectContext must be used within a Select provider");
}
return context;
};
// src/select/Select.tsx
import { jsx as jsx2 } from "react/jsx-runtime";
var Select = ({ children, ...props }) => {
const finder = findElement(children);
const trigger = finder("Trigger");
const items = finder("Items");
return /* @__PURE__ */ jsx2(SelectProvider, { ...props, itemsComponent: items, children: trigger });
};
Select.displayName = "Select";
// src/select/SelectGroup.tsx
import { cx } from "class-variance-authority";
// src/select/SelectItemsGroupContext.tsx
import { createContext as createContext2, useContext as useContext2, useState as useState2 } from "react";
import { jsx as jsx3 } from "react/jsx-runtime";
var SelectGroupContext = createContext2(null);
var SelectGroupProvider = ({ children }) => {
const [groupLabel, setGroupLabel] = useState2("");
return /* @__PURE__ */ jsx3(SelectGroupContext.Provider, { value: { groupLabel, setGroupLabel }, children });
};
var useSelectGroupContext = () => {
const context = useContext2(SelectGroupContext);
if (!context) {
throw Error("useSelectGroupContext must be used within a SelectGroup provider");
}
return context;
};
// src/select/SelectGroup.tsx
import { jsx as jsx4 } from "react/jsx-runtime";
var Group = ({ children, ref: forwardedRef, ...props }) => {
return /* @__PURE__ */ jsx4(SelectGroupProvider, { children: /* @__PURE__ */ jsx4(GroupContent, { ref: forwardedRef, ...props, children }) });
};
var GroupContent = ({ children, className, ref: forwardedRef }) => {
const { groupLabel } = useSelectGroupContext();
return /* @__PURE__ */ jsx4(
"optgroup",
{
"data-spark-component": "select-group",
ref: forwardedRef,
className: cx(className),
label: groupLabel,
children
}
);
};
Group.displayName = "Select.Group";
// src/select/SelectItem.tsx
import { jsx as jsx5 } from "react/jsx-runtime";
var Item = ({ disabled = false, value, children, ref: forwardedRef }) => {
return /* @__PURE__ */ jsx5(
"option",
{
"data-spark-component": "select-item",
ref: forwardedRef,
value,
disabled,
children
},
value
);
};
Item.displayName = "Select.Item";
// src/select/SelectItems.tsx
import { cva } from "class-variance-authority";
import { jsx as jsx6 } from "react/jsx-runtime";
var styles = cva(
[
"absolute left-0 top-0 size-full rounded-lg opacity-0",
"min-h-sz-44",
// outline styles
"ring-1 outline-hidden ring-inset focus:ring-2"
],
{
variants: {
state: {
undefined: "ring-outline focus:ring-outline-high",
error: "ring-error",
alert: "ring-alert",
success: "ring-success"
},
disabled: {
true: "cursor-not-allowed"
},
readOnly: {
true: "cursor-default"
}
},
compoundVariants: [
{
disabled: false,
state: void 0,
class: "hover:ring-outline-high"
}
]
}
);
var Items = ({
children,
className,
ref,
...rest
}) => {
const {
state,
disabled,
readOnly,
ariaLabel,
fieldLabelId,
isControlled,
onValueChange,
selectedItem,
setValue,
name,
required,
fieldId
} = useSelectContext();
const handleChange = (event) => {
if (isControlled) {
event.preventDefault();
onValueChange?.(event.target.value);
} else {
setValue(event.target.value);
}
};
return /* @__PURE__ */ jsx6(
"select",
{
"data-spark-component": "select-items",
ref,
disabled: disabled || readOnly,
name,
required,
"aria-labelledby": fieldLabelId,
...ariaLabel && { "aria-label": ariaLabel },
className: styles({ className, state, disabled, readOnly }),
value: selectedItem?.value,
onChange: handleChange,
id: fieldId,
...rest,
children
}
);
};
Items.displayName = "Select.Items";
// src/select/SelectLabel.tsx
import { useEffect as useEffect2 } from "react";
var Label = ({ children }) => {
const { setGroupLabel } = useSelectGroupContext();
useEffect2(() => {
setGroupLabel(children);
}, [children]);
return null;
};
Label.displayName = "Select.Label";
// src/select/SelectLeadingIcon.tsx
import { jsx as jsx7 } from "react/jsx-runtime";
var LeadingIcon = ({ children }) => {
return /* @__PURE__ */ jsx7(Icon, { size: "sm", className: "shrink-0", children });
};
LeadingIcon.displayName = "Select.LeadingIcon";
// src/select/SelectPlaceholder.tsx
import { useEffect as useEffect3 } from "react";
import { jsx as jsx8 } from "react/jsx-runtime";
var Placeholder = ({
disabled = false,
children,
ref: forwardedRef
}) => {
const { setPlaceholder } = useSelectContext();
useEffect3(() => {
setPlaceholder(children);
}, [children]);
return /* @__PURE__ */ jsx8(
"option",
{
"data-spark-component": "select-placeholder",
ref: forwardedRef,
value: "",
disabled,
children
},
"placeholder"
);
};
Placeholder.displayName = "Select.Placeholder";
// src/select/SelectTrigger.tsx
import { ArrowHorizontalDown } from "@spark-ui/icons/ArrowHorizontalDown";
import { useEffect as useEffect4 } from "react";
// src/select/SelectTrigger.styles.tsx
import { cva as cva2 } from "class-variance-authority";
var styles2 = cva2(
[
"relative flex w-full items-center justify-between",
"min-h-sz-44 rounded-lg px-lg",
"text-body-1",
// outline styles
"ring-1 outline-hidden ring-inset"
],
{
variants: {
state: {
undefined: "ring-outline",
error: "ring-error",
alert: "ring-alert",
success: "ring-success"
},
disabled: {
false: "focus-within:ring-2"
},
readOnly: {
true: ""
}
},
compoundVariants: [
{
readOnly: false,
disabled: false,
class: "bg-surface text-on-surface"
},
{
readOnly: true,
class: "bg-on-surface/dim-5 text-on-surface cursor-default"
},
{
disabled: true,
class: ["bg-on-surface/dim-5 text-on-surface/dim-3", "cursor-not-allowed"]
},
{
disabled: false,
state: void 0,
class: "hover:ring-outline-high focus-within:ring-outline-high"
}
]
}
);
// src/select/SelectTrigger.tsx
import { jsx as jsx9, jsxs } from "react/jsx-runtime";
var Trigger = ({
"aria-label": ariaLabel,
children,
className,
ref: forwardedRef
}) => {
const { disabled, readOnly, state, setAriaLabel, itemsComponent } = useSelectContext();
useEffect4(() => {
if (ariaLabel) {
setAriaLabel(ariaLabel);
}
}, [ariaLabel]);
return /* @__PURE__ */ jsxs(
"div",
{
"data-spark-component": "select-trigger",
ref: forwardedRef,
className: styles2({ className, state, disabled, readOnly }),
children: [
/* @__PURE__ */ jsx9("span", { className: "gap-md flex items-center justify-start", children }),
/* @__PURE__ */ jsx9(Icon, { className: "ml-md shrink-0", size: "sm", children: /* @__PURE__ */ jsx9(ArrowHorizontalDown, {}) }),
itemsComponent
]
}
);
};
Trigger.displayName = "Select.Trigger";
// src/select/SelectValue.tsx
import { cx as cx2 } from "class-variance-authority";
import { jsx as jsx10 } from "react/jsx-runtime";
var Value = ({
children,
className,
placeholder: customPlaceholder,
ref: forwardedRef
}) => {
const { selectedItem, placeholder, disabled } = useSelectContext();
const isPlaceholderSelected = selectedItem?.value == null;
const valuePlaceholder = customPlaceholder || placeholder;
return /* @__PURE__ */ jsx10(
"span",
{
role: "presentation",
"data-spark-component": "select-value",
ref: forwardedRef,
className: cx2("flex shrink items-center text-left", className),
children: /* @__PURE__ */ jsx10(
"span",
{
className: cx2(
"line-clamp-1 flex-1 overflow-hidden break-all text-ellipsis",
isPlaceholderSelected && !disabled && "text-on-surface/dim-1"
),
children: isPlaceholderSelected ? valuePlaceholder : children || selectedItem?.text
}
)
}
);
};
Value.displayName = "Select.Value";
// src/select/index.ts
var Select2 = Object.assign(Select, {
Group,
Item,
Items,
Placeholder,
Label,
Trigger,
Value,
LeadingIcon
});
Select2.displayName = "Select";
Group.displayName = "Select.Group";
Items.displayName = "Select.Items";
Item.displayName = "Select.Item";
Placeholder.displayName = "Select.Placeholder";
Label.displayName = "Select.Label";
Trigger.displayName = "Select.Trigger";
Value.displayName = "Select.Value";
LeadingIcon.displayName = "Select.LeadingIcon";
export {
Select2 as Select,
SelectProvider,
useSelectContext
};
//# sourceMappingURL=index.mjs.map