UNPKG

@spark-ui/components

Version:

Spark (Leboncoin design system) components.

773 lines (742 loc) 23.7 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/textarea/index.ts var textarea_exports = {}; __export(textarea_exports, { Textarea: () => Textarea, TextareaGroup: () => TextareaGroup2 }); module.exports = __toCommonJS(textarea_exports); // src/input/InputClearButton.tsx var import_DeleteOutline = require("@spark-ui/icons/DeleteOutline"); var import_class_variance_authority2 = require("class-variance-authority"); // src/icon/Icon.tsx var import_react2 = require("react"); // src/slot/Slot.tsx var import_radix_ui = require("radix-ui"); var import_react = require("react"); var import_jsx_runtime = require("react/jsx-runtime"); var Slottable = import_radix_ui.Slot.Slottable; var Slot = ({ ref, ...props }) => { return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_radix_ui.Slot.Root, { ref, ...props }); }; // src/visually-hidden/VisuallyHidden.tsx var import_jsx_runtime2 = require("react/jsx-runtime"); var VisuallyHidden = ({ asChild = false, ref, ...props }) => { const Component = asChild ? Slot : "span"; return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( Component, { ...props, ref, style: { // See: https://github.com/twbs/bootstrap/blob/main/scss/mixins/_visually-hidden.scss position: "absolute", border: 0, width: 1, height: 1, padding: 0, margin: -1, overflow: "hidden", clip: "rect(0, 0, 0, 0)", whiteSpace: "nowrap", wordWrap: "normal", ...props.style } } ); }; VisuallyHidden.displayName = "VisuallyHidden"; // src/icon/Icon.styles.tsx var import_internal_utils = require("@spark-ui/internal-utils"); var import_class_variance_authority = require("class-variance-authority"); var iconStyles = (0, import_class_variance_authority.cva)(["fill-current shrink-0"], { variants: { /** * Color scheme of the icon. */ intent: (0, import_internal_utils.makeVariants)({ current: ["text-current"], main: ["text-main"], support: ["text-support"], accent: ["text-accent"], basic: ["text-basic"], success: ["text-success"], alert: ["text-alert"], error: ["text-error"], info: ["text-info"], neutral: ["text-neutral"] }), /** * Sets the size of the icon. */ size: (0, import_internal_utils.makeVariants)({ current: ["u-current-font-size"], sm: ["w-sz-16", "h-sz-16"], md: ["w-sz-24", "h-sz-24"], lg: ["w-sz-32", "h-sz-32"], xl: ["w-sz-40", "h-sz-40"] }) } }); // src/icon/Icon.tsx var import_jsx_runtime3 = require("react/jsx-runtime"); var Icon = ({ label, className, size = "current", intent = "current", children, ...others }) => { const child = import_react2.Children.only(children); return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [ (0, import_react2.cloneElement)(child, { className: iconStyles({ className, size, intent }), "data-spark-component": "icon", "aria-hidden": "true", focusable: "false", ...others }), label && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(VisuallyHidden, { children: label }) ] }); }; Icon.displayName = "Icon"; // src/input/InputGroupContext.ts var import_react3 = require("react"); var InputGroupContext = (0, import_react3.createContext)(null); var useInputGroup = () => { const context = (0, import_react3.useContext)(InputGroupContext); return context || { isStandalone: true }; }; // src/input/InputClearButton.tsx var import_jsx_runtime4 = require("react/jsx-runtime"); var Root = ({ className, tabIndex = -1, onClick, ref, ...others }) => { const { onClear, hasTrailingIcon } = useInputGroup(); const handleClick = (event) => { if (onClick) { onClick(event); } if (onClear) { onClear(); } }; return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)( "button", { ref, className: (0, import_class_variance_authority2.cx)( className, "pointer-events-auto absolute top-1/2 -translate-y-1/2", "inline-flex h-full items-center justify-center outline-hidden", "text-neutral hover:text-neutral-hovered", hasTrailingIcon ? "right-3xl px-sz-12" : "pl-md pr-lg right-0" ), tabIndex, onClick: handleClick, type: "button", ...others, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { size: "sm", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_DeleteOutline.DeleteOutline, {}) }) } ); }; var InputClearButton = Object.assign(Root, { id: "ClearButton" }); Root.displayName = "InputGroup.ClearButton"; // src/input/InputGroup.tsx var import_form_field = require("@spark-ui/components/form-field"); var import_use_combined_state = require("@spark-ui/hooks/use-combined-state"); var import_use_merge_refs = require("@spark-ui/hooks/use-merge-refs"); var import_react4 = require("react"); // src/input/InputGroup.styles.ts var import_class_variance_authority3 = require("class-variance-authority"); var inputGroupStyles = (0, import_class_variance_authority3.cva)(["relative inline-flex w-full"], { variants: { /** * When `true`, prevents the user from interacting. */ disabled: { true: [ "cursor-not-allowed", "relative", "after:absolute", "after:top-0", "after:h-full", "after:w-full", "after:border-sm after:border-outline", "after:rounded-lg" ], false: "after:hidden" }, /** * Sets the component as interactive or not. */ readOnly: { true: [ "relative", "after:absolute", "after:top-0", "after:h-full", "after:w-full", "after:border-sm after:border-outline", "after:rounded-lg" ], false: "after:hidden" } } }); // src/input/InputGroup.tsx var import_jsx_runtime5 = require("react/jsx-runtime"); var InputGroup = ({ className, children: childrenProp, state: stateProp, disabled: disabledProp, readOnly: readOnlyProp, onClear, ref: forwardedRef, ...others }) => { const getElementId = (element) => { return element ? element.type.id : ""; }; const findElement = (...values) => { return children.find((child) => values.includes(getElementId(child) || "")); }; const children = import_react4.Children.toArray(childrenProp).filter(import_react4.isValidElement); const input = findElement("Input"); const props = input?.props || {}; const inputRef = (0, import_react4.useRef)(null); const onClearRef = (0, import_react4.useRef)(onClear); const ref = (0, import_use_merge_refs.useMergeRefs)(input?.ref, inputRef); const [value, onChange] = (0, import_use_combined_state.useCombinedState)( props.value, props.defaultValue, props.onValueChange ); const field = (0, import_form_field.useFormFieldControl)(); const state = field.state ?? stateProp; const disabled = field.disabled || !!disabledProp; const readOnly = field.readOnly || !!readOnlyProp; const leadingAddon = findElement("LeadingAddon"); const leadingIcon = findElement("LeadingIcon"); const clearButton = findElement("ClearButton"); const trailingIcon = findElement("TrailingIcon"); const trailingAddon = findElement("TrailingAddon"); const hasLeadingAddon = !!leadingAddon; const hasTrailingAddon = !!trailingAddon; const hasLeadingIcon = !!leadingIcon; const hasTrailingIcon = !!trailingIcon; const hasClearButton = !!value && !!clearButton && !disabled && !readOnly; const handleChange = (event) => { if (props.onChange) { props.onChange(event); } onChange(event.target.value); }; const handleClear = (0, import_react4.useCallback)(() => { if (onClearRef.current) { onClearRef.current(); } onChange(""); inputRef.current.focus(); }, [onChange]); const current = (0, import_react4.useMemo)(() => { return { state, disabled, readOnly, hasLeadingIcon, hasTrailingIcon, hasLeadingAddon, hasTrailingAddon, hasClearButton, onClear: handleClear }; }, [ state, disabled, readOnly, hasLeadingIcon, hasTrailingIcon, hasLeadingAddon, hasTrailingAddon, hasClearButton, handleClear ]); (0, import_react4.useEffect)(() => { onClearRef.current = onClear; }, [onClear]); return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(InputGroupContext.Provider, { value: current, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)( "div", { ref: forwardedRef, className: inputGroupStyles({ disabled, readOnly, className }), ...others, children: [ hasLeadingAddon && leadingAddon, /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "relative inline-flex w-full", children: [ input && (0, import_react4.cloneElement)(input, { ref, defaultValue: void 0, value: value ?? "", onChange: handleChange }), leadingIcon, hasClearButton && clearButton, trailingIcon ] }), hasTrailingAddon && trailingAddon ] } ) }); }; InputGroup.displayName = "InputGroup"; // src/input/InputLeadingAddon.tsx var import_class_variance_authority5 = require("class-variance-authority"); // src/input/InputAddon.tsx var import_react5 = require("react"); // src/input/InputAddon.styles.ts var import_class_variance_authority4 = require("class-variance-authority"); var inputAddonStyles = (0, import_class_variance_authority4.cva)( [ "overflow-hidden", "border-sm", "shrink-0", "h-full", "focus-visible:relative focus-visible:z-raised" ], { variants: { /** * Change the component to the HTML tag or custom component of the only child. */ asChild: { false: ["flex", "items-center", "px-lg"] }, intent: { neutral: "border-outline", error: "border-error", alert: "border-alert", success: "border-success" }, /** * Disable the input addon, preventing user interaction and adding opacity. */ disabled: { true: ["pointer-events-none border-outline!"] }, /** * Changes input addon styles based on the read only status from the input. */ readOnly: { true: ["pointer-events-none"] }, /** * Main style of the input addon. */ design: { text: "", solid: "", inline: "" } }, compoundVariants: [ { disabled: false, readOnly: false, design: "text", class: ["bg-surface", "text-on-surface"] }, { disabled: true, design: "text", class: ["text-on-surface/dim-3"] }, { disabled: true, design: ["solid", "inline"], class: ["opacity-dim-3"] } ], defaultVariants: { intent: "neutral" } } ); // src/input/InputAddon.tsx var import_jsx_runtime6 = require("react/jsx-runtime"); var InputAddon = ({ asChild: asChildProp, className, children, ref, ...others }) => { const { state, disabled, readOnly } = useInputGroup(); const isRawText = typeof children === "string"; const asChild = !!(isRawText ? false : asChildProp); const child = isRawText ? children : import_react5.Children.only(children); const Component = asChild && !isRawText ? Slot : "div"; const getDesign = () => { if (isRawText) return "text"; return asChild ? "solid" : "inline"; }; const design = getDesign(); return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)( Component, { ref, className: inputAddonStyles({ className, intent: state, disabled, readOnly, asChild, design }), ...disabled && { tabIndex: -1 }, ...others, children: child } ); }; InputAddon.displayName = "InputGroup.Addon"; // src/input/InputLeadingAddon.tsx var import_jsx_runtime7 = require("react/jsx-runtime"); var Root2 = ({ className, ref, ...others }) => { const { disabled, readOnly } = useInputGroup(); const isInactive = disabled || readOnly; return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: (0, import_class_variance_authority5.cx)("rounded-l-lg", isInactive ? "bg-on-surface/dim-5" : null), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)( InputAddon, { ref, className: (0, import_class_variance_authority5.cx)(className, "rounded-r-0! mr-[-1px] rounded-l-lg"), ...others } ) }); }; var InputLeadingAddon = Object.assign(Root2, { id: "LeadingAddon" }); Root2.displayName = "InputGroup.LeadingAddon"; // src/input/InputLeadingIcon.tsx var import_class_variance_authority7 = require("class-variance-authority"); // src/input/InputIcon.tsx var import_class_variance_authority6 = require("class-variance-authority"); var import_jsx_runtime8 = require("react/jsx-runtime"); var InputIcon = ({ className, intent, children, ...others }) => { const { disabled, readOnly } = useInputGroup(); const isInactive = disabled || readOnly; return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)( Icon, { intent, className: (0, import_class_variance_authority6.cx)( className, "pointer-events-none absolute top-[calc(var(--spacing-sz-44)/2)] -translate-y-1/2", intent ? void 0 : "text-neutral peer-focus:text-outline-high", isInactive ? "opacity-dim-3" : void 0 ), ...others, children } ); }; InputIcon.displayName = "InputGroup.Icon"; // src/input/InputLeadingIcon.tsx var import_jsx_runtime9 = require("react/jsx-runtime"); var InputLeadingIcon = ({ className, ...others }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(InputIcon, { className: (0, import_class_variance_authority7.cx)(className, "left-lg text-body-1"), ...others }); InputLeadingIcon.id = "LeadingIcon"; InputLeadingIcon.displayName = "InputGroup.LeadingIcon"; // src/input/InputTrailingAddon.tsx var import_class_variance_authority8 = require("class-variance-authority"); var import_jsx_runtime10 = require("react/jsx-runtime"); var Root3 = ({ className, ref, ...others }) => { const { disabled, readOnly } = useInputGroup(); const isInactive = disabled || readOnly; return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: (0, import_class_variance_authority8.cx)("rounded-r-lg", isInactive ? "bg-on-surface/dim-5" : null), children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)( InputAddon, { ref, className: (0, import_class_variance_authority8.cx)(className, "rounded-l-0! ml-[-1px] rounded-r-lg"), ...others } ) }); }; var InputTrailingAddon = Object.assign(Root3, { id: "TrailingAddon" }); Root3.displayName = "InputGroup.TrailingAddon"; // src/input/InputTrailingIcon.tsx var import_class_variance_authority9 = require("class-variance-authority"); var import_jsx_runtime11 = require("react/jsx-runtime"); var InputTrailingIcon = ({ className, ...others }) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(InputIcon, { className: (0, import_class_variance_authority9.cx)(className, "right-lg text-body-1"), ...others }); InputTrailingIcon.id = "TrailingIcon"; InputTrailingIcon.displayName = "InputGroup.TrailingIcon"; // src/input/Input.tsx var import_form_field2 = require("@spark-ui/components/form-field"); // src/input/Input.styles.ts var import_class_variance_authority10 = require("class-variance-authority"); var inputStyles = (0, import_class_variance_authority10.cva)( [ "relative", "border-sm", "peer", "w-full", "appearance-none outline-hidden", "bg-surface", "text-ellipsis text-body-1 text-on-surface", "caret-neutral", "autofill:shadow-surface autofill:shadow-[inset_0_0_0px_1000px]", "disabled:cursor-not-allowed disabled:border-outline disabled:bg-on-surface/dim-5 disabled:text-on-surface/dim-3", "read-only:cursor-default read-only:pointer-events-none read-only:bg-on-surface/dim-5", "focus:ring-1 focus:ring-inset" ], { variants: { /** * Change the component to the HTML tag or custom component of the only child. */ asChild: { true: ["min-h-sz-44"], false: ["h-sz-44"] }, /** * Color scheme of the button. */ intent: { neutral: [ "border-outline", "hover:border-outline-high", "focus:ring-outline-high focus:border-outline-high" ], success: ["border-success", "focus:ring-success"], alert: ["border-alert", "focus:ring-alert"], error: ["border-error", "focus:ring-error"] }, /** * Sets if there is an addon before the input text. */ hasLeadingAddon: { true: ["rounded-l-0"], false: ["rounded-l-lg"] }, /** * Sets if there is an addon after the input text. */ hasTrailingAddon: { true: ["rounded-r-0"], false: ["rounded-r-lg"] }, /** * Sets if there is an icon before the input text. */ hasLeadingIcon: { true: ["pl-3xl"], false: ["pl-lg"] }, /** * Sets if there is an icon after the input text. */ hasTrailingIcon: { true: "" }, /** * Sets if there is a button to clear the input text. */ hasClearButton: { true: "" } }, compoundVariants: [ { hasTrailingIcon: false, hasClearButton: false, class: "pr-lg" }, { hasTrailingIcon: true, hasClearButton: false, class: "pr-3xl" }, { hasTrailingIcon: false, hasClearButton: true, class: "pr-3xl" }, { hasTrailingIcon: true, hasClearButton: true, class: "pr-[calc(var(--spacing-3xl)*2)]" } ], defaultVariants: { intent: "neutral" } } ); // src/input/Input.tsx var import_jsx_runtime12 = require("react/jsx-runtime"); var Root4 = ({ className, asChild = false, onValueChange, onChange, onKeyDown, disabled: disabledProp, readOnly: readOnlyProp, ref, ...others }) => { const field = (0, import_form_field2.useFormFieldControl)(); const group = useInputGroup(); const { id, name, isInvalid, isRequired, description } = field; const { hasLeadingAddon, hasTrailingAddon, hasLeadingIcon, hasTrailingIcon, hasClearButton, onClear } = group; const Component = asChild ? Slot : "input"; const state = field.state || group.state; const disabled = field.disabled || group.disabled || disabledProp; const readOnly = field.readOnly || group.readOnly || readOnlyProp; const handleChange = (event) => { if (onChange) { onChange(event); } if (onValueChange) { onValueChange(event.target.value); } }; const handleKeyDown = (event) => { if (onKeyDown) { onKeyDown(event); } if (hasClearButton && onClear && event.key === "Escape") { onClear(); } }; return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)( Component, { ref, id, name, className: inputStyles({ asChild, className, intent: state, hasLeadingAddon: !!hasLeadingAddon, hasTrailingAddon: !!hasTrailingAddon, hasLeadingIcon: !!hasLeadingIcon, hasTrailingIcon: !!hasTrailingIcon, hasClearButton: !!hasClearButton }), disabled, readOnly, required: isRequired, "aria-describedby": description, "aria-invalid": isInvalid, onChange: handleChange, onKeyDown: handleKeyDown, ...others } ); }; var Input = Object.assign(Root4, { id: "Input" }); Root4.displayName = "Input"; // src/input/index.ts var InputGroup2 = Object.assign(InputGroup, { LeadingAddon: InputLeadingAddon, TrailingAddon: InputTrailingAddon, LeadingIcon: InputLeadingIcon, TrailingIcon: InputTrailingIcon, ClearButton: InputClearButton }); InputGroup2.displayName = "InputGroup"; InputLeadingAddon.displayName = "InputGroup.LeadingAddon"; InputTrailingAddon.displayName = "InputGroup.TrailingAddon"; InputLeadingIcon.displayName = "InputGroup.LeadingIcon"; InputTrailingIcon.displayName = "InputGroup.TrailingIcon"; InputClearButton.displayName = "InputGroup.ClearButton"; // src/textarea/TextareaGroup.tsx var import_jsx_runtime13 = require("react/jsx-runtime"); var TextareaGroup = (props) => { return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(InputGroup2, { ...props }); }; TextareaGroup.displayName = "TextareaGroup"; // src/textarea/TextareaLeadingIcon.tsx var import_jsx_runtime14 = require("react/jsx-runtime"); var TextareaLeadingIcon = (props) => { return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(InputGroup2.LeadingIcon, { "data-spark-component": "textarea-group-leading-icon", ...props }); }; TextareaLeadingIcon.id = InputGroup2.LeadingIcon.id; TextareaLeadingIcon.displayName = "TextareaGroup.LeadingIcon"; // src/textarea/TextareaTrailingIcon.tsx var import_jsx_runtime15 = require("react/jsx-runtime"); var TextareaTrailingIcon = (props) => { return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(InputGroup2.TrailingIcon, { "data-spark-component": "textarea-group-trailing-icon", ...props }); }; TextareaTrailingIcon.id = InputGroup2.TrailingIcon.id; TextareaTrailingIcon.displayName = "TextareaGroup.TrailingIcon"; // src/textarea/Textarea.tsx var import_class_variance_authority11 = require("class-variance-authority"); var import_jsx_runtime16 = require("react/jsx-runtime"); var Root5 = ({ className, disabled, rows = 1, isResizable = true, ref, ...others }) => { return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)( Input, { className: (0, import_class_variance_authority11.cx)( className, "py-[var(--spacing-sz-10)]", isResizable ? "resize-y" : "resize-none" ), "data-spark-component": "textarea", disabled, asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("textarea", { ref, rows, ...others }) } ); }; var Textarea = Object.assign(Root5, { id: Input.id }); Root5.displayName = "Textarea"; // src/textarea/index.ts var TextareaGroup2 = Object.assign(TextareaGroup, { LeadingIcon: TextareaLeadingIcon, TrailingIcon: TextareaTrailingIcon }); TextareaGroup2.displayName = "TextareaGroup"; TextareaLeadingIcon.displayName = "TextareaGroup.LeadingIcon"; TextareaTrailingIcon.displayName = "TextareaGroup.TrailingIcon"; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Textarea, TextareaGroup }); //# sourceMappingURL=index.js.map