@plone/components
Version:
ReactJS components for Plone
704 lines (693 loc) • 18.9 kB
JavaScript
"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
};