UNPKG

@plone/components

Version:

ReactJS components for Plone

704 lines (693 loc) 18.9 kB
"use client" import { AligncenterIcon, AlignleftIcon, AlignrightIcon, ChevrondownIcon, ChevronupIcon, ImageIcon, ImagefitIcon, ImagefullIcon, ImagenarrowIcon, ImagewideIcon } from "./chunk-V3F5PCGN.js"; // src/components/Field/Field.quanta.tsx import React from "react"; import { Group, FieldError as RACFieldError, Input as RACInput, Label as RACLabel, Text, composeRenderProps as composeRenderProps2, useRenderProps, FieldErrorContext, LabelContext, Provider, GroupContext, TextContext } from "react-aria-components"; import { twMerge as twMerge2 } from "tailwind-merge"; import { tv as tv2 } from "tailwind-variants"; // src/components/utils.ts import { useMemo, useLayoutEffect, useCallback, useRef, useState } from "react"; import { composeRenderProps } from "react-aria-components"; import { twMerge } from "tailwind-merge"; import { tv } from "tailwind-variants"; var focusRing = tv({ base: "outline-quanta-cobalt outline forced-colors:outline-[Highlight]", variants: { isFocusVisible: { false: "outline-0", true: "outline-3" } } }); function composeTailwindRenderProps(className, tw) { return composeRenderProps(className, (className2) => twMerge(tw, className2)); } function useSlot(initialState = true) { const [hasSlot, setHasSlot] = useState(initialState); const hasRun = useRef(false); const ref = useCallback((el) => { hasRun.current = true; setHasSlot(!!el); }, []); useLayoutEffect(() => { if (!hasRun.current) { setHasSlot(false); } }, []); return [ref, hasSlot]; } // src/components/Field/Field.quanta.tsx import { useField } from "react-aria"; import { filterDOMProps } from "@react-aria/utils"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; function Label(props) { return /* @__PURE__ */ jsx( RACLabel, { ...props, className: twMerge2( ` w-fit cursor-default text-xs font-medium text-quanta-pigeon group-data-disabled:text-quanta-silver group-data-invalid:text-quanta-candy after:bg-quanta-candy group-data-required:after:mx-1 group-data-required:after:inline-block group-data-required:after:h-2 group-data-required:after:w-2 group-data-required:after:rounded-4xl not-group-data-invalid:not-group-data-readonly:has-[+input:focus]:text-quanta-sapphire `, props.className ) } ); } function Description(props) { return /* @__PURE__ */ jsx( Text, { ...props, slot: "description", className: twMerge2( ` text-xs font-normal text-quanta-pigeon group-data-disabled:text-quanta-silver `, props.className ) } ); } function FieldError(props) { return /* @__PURE__ */ jsx( RACFieldError, { ...props, className: composeTailwindRenderProps( props.className, ` text-xs font-normal text-quanta-candy forced-colors:text-[Mark] ` ) } ); } var fieldBorderStyles = tv2({ variants: { isFocusWithin: { false: `forced-colors:border-[ButtonBorder]`, true: ` bg-quanta-air inset-ring-quanta-sapphire outline-2 group-data-readonly:inset-ring-0 hover:bg-quanta-air forced-colors:border-[Highlight] ` }, isInvalid: { true: ` bg-quanta-ballet outline-2 hover:bg-quanta-flamingo focus:inset-ring-quanta-candy forced-colors:border-[Mark] ` }, isDisabled: { true: ` bg-quanta-air text-quanta-silver hover:bg-quanta-air forced-colors:border-[GrayText] ` } } }); var fieldGroupStyles = tv2({ extend: focusRing, base: ` group flex h-11 items-center overflow-hidden rounded-lg bg-quanta-snow text-quanta-space hover:bg-quanta-smoke read-only:hover:bg-quanta-air focus:bg-quanta-air active:bg-quanta-air forced-colors:bg-[Field] `, variants: { ...fieldBorderStyles.variants, isFocusWithin: { ...fieldBorderStyles.variants.isFocusWithin, true: twMerge2( fieldBorderStyles.variants.isFocusWithin.true, `inset-ring-2` ) }, isInvalid: { ...fieldBorderStyles.variants.isInvalid, true: twMerge2( fieldBorderStyles.variants.isInvalid.true, `focus:inset-ring-2` ) } } }); function FieldGroup(props) { return /* @__PURE__ */ jsx( Group, { ...props, className: composeRenderProps2( props.className, (className, renderProps) => fieldGroupStyles({ ...renderProps, className }) ) } ); } var inputStyles = tv2({ extend: focusRing, base: "rounded-md p-3", variants: { isFocused: { ...fieldBorderStyles.variants.isFocusWithin, true: twMerge2( fieldBorderStyles.variants.isFocusWithin.true, `inset-ring-2` ) }, isInvalid: { ...fieldBorderStyles.variants.isInvalid, true: twMerge2( fieldBorderStyles.variants.isInvalid.true, `focus:inset-ring-2` ) }, isDisabled: fieldBorderStyles.variants.isDisabled } }); function Input(props) { return /* @__PURE__ */ jsx( RACInput, { ...props, className: composeTailwindRenderProps( props.className, ` min-w-0 flex-1 bg-quanta-snow px-2 py-1.5 text-sm text-quanta-space outline not-group-data-focus-within:outline-0 group-data-focus-within:outline-0 group-data-hovered:bg-quanta-smoke read-only:border-1 read-only:border-dashed read-only:bg-quanta-air hover:bg-quanta-smoke read-only:hover:bg-quanta-air focus:bg-quanta-air active:bg-quanta-air disabled:cursor-not-allowed disabled:text-quanta-silver ` ) } ); } var Field = React.forwardRef( (props, ref) => { const [labelRef] = useSlot( !props["aria-label"] && !props["aria-labelledby"] ); const { labelProps, descriptionProps, errorMessageProps } = useField({ ...props, isInvalid: props.isInvalid || false, errorMessage: props.errorMessage }); const renderProps = useRenderProps({ ...props, values: { isDisabled: props.isDisabled || false, isInvalid: props.isInvalid || false, isReadOnly: props.isReadOnly || false, isRequired: props.isRequired || false }, defaultClassName: "react-aria-Field" }); const DOMProps = filterDOMProps(props); return /* @__PURE__ */ jsx( "div", { ...DOMProps, ...renderProps, ref, slot: props.slot || void 0, "data-disabled": props.isDisabled || void 0, "data-invalid": props.isInvalid || void 0, "data-readonly": props.isReadOnly || void 0, "data-required": props.isRequired || void 0, children: /* @__PURE__ */ jsx( Provider, { values: [ [LabelContext, { ...labelProps, ref: labelRef }], [ GroupContext, { role: "presentation", isInvalid: props.isInvalid || false, isDisabled: props.isDisabled || false } ], [ TextContext, { slots: { description: descriptionProps, errorMessage: errorMessageProps } } ], [ FieldErrorContext, { isInvalid: props.isInvalid || false, validationErrors: props.errorMessage ? [props.errorMessage] : [], // @ts-expect-error We won't use validationDetails in this context validationDetails: null } ] ], children: renderProps.children } ) } ); } ); Field.displayName = "Field"; var FieldWrapper = (props) => { const { wrapped = true, children, label, description, errorMessage } = props; return wrapped ? /* @__PURE__ */ jsxs( Field, { ...props, className: twMerge2(props.className, "group flex flex-col gap-1"), children: [ label && /* @__PURE__ */ jsx(Label, { children: label }), children, description && /* @__PURE__ */ jsx(Description, { children: description }), /* @__PURE__ */ jsx(FieldError, { children: errorMessage }) ] } ) : /* @__PURE__ */ jsx(Fragment, { children }); }; // src/components/RadioGroup/RadioGroup.quanta.tsx import "react"; import { composeRenderProps as composeRenderProps3, Radio as RACRadio, RadioGroup as RACRadioGroup } from "react-aria-components"; import { tv as tv3 } from "tailwind-variants"; import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime"; function RadioGroup(props) { return /* @__PURE__ */ jsxs2( RACRadioGroup, { ...props, className: composeTailwindRenderProps( props.className, "group flex flex-col gap-2" ), children: [ /* @__PURE__ */ jsx2(Label, { children: props.label }), /* @__PURE__ */ jsx2( "div", { className: ` flex gap-2 group-orientation-horizontal:gap-4 group-orientation-vertical:flex-col `, children: props.children } ), props.description && /* @__PURE__ */ jsx2(Description, { children: props.description }), /* @__PURE__ */ jsx2(FieldError, { children: props.errorMessage }) ] } ); } var styles = tv3({ extend: focusRing, base: ` h-5 w-5 rounded-full border-2 bg-white transition-all dark:bg-zinc-900 `, variants: { isSelected: { false: ` border-gray-400 group-pressed:border-gray-500 dark:border-zinc-400 dark:group-pressed:border-zinc-300 `, true: ` border-[7px] border-gray-700 group-pressed:border-gray-800 dark:border-slate-300 dark:group-pressed:border-slate-200 forced-colors:border-[Highlight]! ` }, isInvalid: { true: ` border-red-700 group-pressed:border-red-800 dark:border-red-600 dark:group-pressed:border-red-700 forced-colors:border-[Mark]! ` }, isDisabled: { true: ` border-gray-200 dark:border-zinc-700 forced-colors:border-[GrayText]! ` } } }); function Radio(props) { return /* @__PURE__ */ jsx2( RACRadio, { ...props, className: composeTailwindRenderProps( props.className, ` group flex items-center gap-2 text-sm text-gray-800 transition disabled:text-gray-300 dark:text-zinc-200 dark:disabled:text-zinc-600 forced-colors:disabled:text-[GrayText] ` ), children: (renderProps) => /* @__PURE__ */ jsxs2(Fragment2, { children: [ /* @__PURE__ */ jsx2("div", { className: styles(renderProps) }), props.children ] }) } ); } var customRadioButton = tv3({ extend: focusRing, base: ` flex h-10 w-10 cursor-pointer items-center justify-center rounded-md font-medium text-quanta-iron transition `, variants: { isSelected: { false: ` bg-quanta-air hover:bg-quanta-sky focus:bg-quanta-snow active:bg-quanta-silver `, true: ` bg-quanta-sky hover:bg-quanta-sky focus:bg-quanta-sky active:bg-quanta-sky pressed:bg-quanta-sky ` }, isPressed: { true: "pressed:bg-quanta-cobalt" }, isInvalid: { true: ` border border-red-700 text-red-700 dark:border-red-600 dark:text-red-600 ` }, isDisabled: { true: "text-quanta-smoke" } } }); function CustomRadio(props) { return /* @__PURE__ */ jsx2( RACRadio, { ...props, className: composeRenderProps3( props.className, (className, renderProps) => customRadioButton({ ...renderProps, className }) ), children: props.children } ); } // src/components/AlignWidget/AlignWidget.tsx import "react"; import { jsx as jsx3 } from "react/jsx-runtime"; var defaultActionsInfo = { left: [AlignleftIcon, "Left"], right: [AlignrightIcon, "Right"], center: [AligncenterIcon, "Center"], narrow: [ImageIcon, "Narrow"], wide: [ImageIcon, "Wide"], full: [ImagefullIcon, "Full"] }; function AlignWidget(props) { const { // eslint-disable-next-line @typescript-eslint/no-unused-vars id, onChange, actions = ["left", "right", "center", "full"], actionsInfoMap, ...radioGroupProps } = props; const actionsInfo = { ...defaultActionsInfo, ...actionsInfoMap }; const handleChange = (selectedValue) => { if (onChange) { onChange(selectedValue); } }; return /* @__PURE__ */ jsx3( RadioGroup, { ...radioGroupProps, onChange: handleChange, orientation: "horizontal", children: actions.map((action) => { const [IconComponent, label] = actionsInfo[action]; return /* @__PURE__ */ jsx3(CustomRadio, { value: action, children: /* @__PURE__ */ jsx3(IconComponent, { size: "base", "aria-label": label }) }, action); }) } ); } // src/components/Select/Select.tsx import "react"; import { Button, FieldError as FieldError2, Header, Label as Label2, ListBox, ListBoxItem, ListBoxSection, Popover, PopoverContext, Select as RACSelect, SelectValue, Text as Text2, useContextProps } from "react-aria-components"; import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime"; function Select({ label, description, errorMessage, children, items, ...props }) { const [popoverProps] = useContextProps({}, null, PopoverContext); return /* @__PURE__ */ jsx4(RACSelect, { ...props, children: ({ isOpen }) => /* @__PURE__ */ jsxs3(Fragment3, { children: [ label && /* @__PURE__ */ jsx4(Label2, { children: label }), /* @__PURE__ */ jsxs3(Button, { children: [ /* @__PURE__ */ jsx4(SelectValue, {}), /* @__PURE__ */ jsx4("span", { "aria-hidden": "true", style: { display: "flex" }, children: isOpen ? /* @__PURE__ */ jsx4(ChevronupIcon, {}) : /* @__PURE__ */ jsx4(ChevrondownIcon, {}) }) ] }), description && /* @__PURE__ */ jsx4(Text2, { slot: "description", children: description }), /* @__PURE__ */ jsx4(FieldError2, { children: errorMessage }), /* @__PURE__ */ jsx4(Popover, { offset: 0, ...popoverProps, children: /* @__PURE__ */ jsx4(SelectListBox, { items, children: children ? children : DefaultSelectItem }) }) ] }) }); } function DefaultSelectItem(item) { return /* @__PURE__ */ jsx4(SelectItem, { id: item.label, children: item.value }); } function SelectListBox(props) { return /* @__PURE__ */ jsx4(ListBox, { ...props }); } function SelectItem(props) { return /* @__PURE__ */ jsx4(ListBoxItem, { ...props }); } function SelectSection(props) { return /* @__PURE__ */ jsx4(ListBoxSection, { ...props }); } function SelectSectionHeader(props) { return /* @__PURE__ */ jsx4(Header, { ...props }); } // src/components/SizeWidget/SizeWidget.tsx import "react"; import { jsx as jsx5 } from "react/jsx-runtime"; var defaultSizeActionsInfo = { s: ["S", "Small"], m: ["M", "Medium"], l: ["L", "Large"] }; function SizeWidget(props) { const { // eslint-disable-next-line @typescript-eslint/no-unused-vars id, onChange, actions = ["s", "m", "l"], actionsInfoMap, ...radioGroupProps } = props; const actionsInfo = { ...defaultSizeActionsInfo, ...actionsInfoMap }; const handleChange = (selectedValue) => { if (onChange) { onChange(selectedValue); } }; return /* @__PURE__ */ jsx5( RadioGroup, { ...radioGroupProps, onChange: handleChange, orientation: props.orientation || "horizontal", children: actions.map((action) => /* @__PURE__ */ jsx5(CustomRadio, { value: action, children: /* @__PURE__ */ jsx5("span", { "aria-label": actionsInfo[action][1], children: actionsInfo[action][0] }) }, action)) } ); } // src/components/WidthWidget/WidthWidget.tsx import "react"; import { jsx as jsx6 } from "react/jsx-runtime"; var defaultActionsInfo2 = { narrow: [ImagenarrowIcon, "Narrow"], default: [ImagefitIcon, "Default"], layout: [ImagewideIcon, "Layout"], full: [ImagefullIcon, "Full"] }; function WidthWidget(props) { const { // eslint-disable-next-line @typescript-eslint/no-unused-vars id, onChange, actions = ["narrow", "default", "layout", "full"], actionsInfoMap, ...radioGroupProps } = props; const actionsInfo = { ...defaultActionsInfo2, ...actionsInfoMap }; const handleChange = (selectedValue) => { if (onChange) { onChange(selectedValue); } }; return /* @__PURE__ */ jsx6( RadioGroup, { ...radioGroupProps, onChange: handleChange, orientation: "horizontal", children: actions.map((action) => { const [IconComponent, label] = actionsInfo[action]; return /* @__PURE__ */ jsx6(CustomRadio, { value: action, children: /* @__PURE__ */ jsx6(IconComponent, { size: "base", "aria-label": label }) }, action); }) } ); } // src/components/Menu/menuTriggerChildren.tsx import React7 from "react"; function isFragmentElement(element) { return element.type === React7.Fragment; } function getMenuTriggerChildren(children, componentName) { const nodes = React7.Children.toArray(children); if (nodes.length !== 2) { throw new Error( `${componentName} expects exactly two children: a trigger element and a menu element.` ); } const [trigger, menu] = nodes; if (!React7.isValidElement(trigger) || !React7.isValidElement(menu)) { throw new Error( `${componentName} expects both children to be valid React elements.` ); } if (isFragmentElement(trigger) || isFragmentElement(menu)) { throw new Error( `${componentName} does not accept Fragment children. Pass the trigger element and menu element directly.` ); } return [trigger, menu]; } export { focusRing, composeTailwindRenderProps, Label, Description, FieldError, fieldBorderStyles, fieldGroupStyles, FieldGroup, inputStyles, Input, Field, FieldWrapper, RadioGroup, Radio, CustomRadio, AlignWidget, getMenuTriggerChildren, Select, SelectListBox, SelectItem, SelectSection, SelectSectionHeader, SizeWidget, WidthWidget };