@plexui/vue 0.3.0 → 0.5.0
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.
- package/dist/es/components/Field/Field.js +3 -0
- package/dist/es/components/Field/Field.js.map +1 -1
- package/dist/es/components/Page/Page.js +89 -0
- package/dist/es/components/Page/Page.js.map +1 -0
- package/dist/es/components/Page/index.js +2 -0
- package/dist/es/components/Page/index.js.map +1 -0
- package/dist/es/components/Select/Select.js +20 -12
- package/dist/es/components/Select/Select.js.map +1 -1
- package/dist/es/components/Sidebar/Sidebar.js +17 -3
- package/dist/es/components/Sidebar/Sidebar.js.map +1 -1
- package/dist/es/components/Sidebar/index.js.map +1 -1
- package/dist/es/components/Sidebar/useSidebarContext.js +7 -2
- package/dist/es/components/Sidebar/useSidebarContext.js.map +1 -1
- package/dist/types/components/Field/Field.d.ts +11 -0
- package/dist/types/components/Page/Page.d.ts +117 -0
- package/dist/types/components/Page/index.d.ts +1 -0
- package/dist/types/components/Select/Select.d.ts +8 -15
- package/dist/types/components/Sidebar/Sidebar.d.ts +2 -2
- package/dist/types/components/Sidebar/index.d.ts +1 -1
- package/dist/types/components/Sidebar/useSidebarContext.d.ts +13 -0
- package/package.json +1 -1
|
@@ -116,6 +116,8 @@ const FieldComponent = defineComponent({
|
|
|
116
116
|
orientation: String,
|
|
117
117
|
/** One line of a FieldGroup: label at the start, control at the end. @default false */
|
|
118
118
|
row: { type: Boolean, default: false },
|
|
119
|
+
/** On a `row`, the control takes the rest of the line after the label. @default false */
|
|
120
|
+
fill: { type: Boolean, default: false },
|
|
119
121
|
/** Overrides the generated id set on the control and the label's `for`. A child's own `id` wins over both. */
|
|
120
122
|
id: String,
|
|
121
123
|
/** Optically aligns the control's text with surrounding content; passed to the control. */
|
|
@@ -172,6 +174,7 @@ const FieldComponent = defineComponent({
|
|
|
172
174
|
"data-orientation": orientation,
|
|
173
175
|
"data-optically-align": opticallyAlign,
|
|
174
176
|
"data-row": row ? "" : undefined,
|
|
177
|
+
"data-fill": props.fill ? "" : undefined,
|
|
175
178
|
}, [
|
|
176
179
|
h("div", { class: s.LabelGroup }, [
|
|
177
180
|
hasLabelAction
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Field.js","sourceRoot":"","sources":["../../../../src/components/Field/Field.ts"],"names":[],"mappings":"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","sourcesContent":["import clsx, { type ClassValue } from \"clsx\"\nimport {\n Comment,\n Fragment,\n Static,\n Text,\n camelize,\n cloneVNode,\n defineComponent,\n h,\n isVNode,\n useId,\n type PropType,\n type SlotsType,\n type VNode,\n type VNodeArrayChildren,\n type VNodeChild,\n} from \"vue\"\nimport type { ControlSize } from \"@plexui/styles/types\"\nimport { FieldError } from \"../FieldError/index.js\"\nimport { hasContent } from \"../../lib/slotContent.js\"\nimport s from \"@plexui/styles/components/Field/Field.module.css\"\n\n// Vue port of packages/ui/src/components/Field/Field.tsx.\n//\n// - `label`, `description` and `errorMessage` take a string prop or a slot of\n// the same name (the slot wins), like React's ReactNode props.\n// - Children: React accepts an element, which Field clones with the field\n// props, or a render prop `(fieldProps) => ReactNode`, which it leaves alone.\n// Both are the default slot here, which receives the field props:\n// - a slot that reads them (`v-slot=\"fieldProps\"` + `v-bind=\"fieldProps\"`,\n// `v-slot=\"{ id }\"`, `(fieldProps) => h(...)`) is the render prop: its\n// content renders as it is.\n// - a slot that does not read them and holds exactly one element or\n// component gets it cloned like React's `cloneElement`: `opticallyAlign`\n// replaces the child's own, the child's own `id` stays and the label's\n// `for` follows it, `aria-describedby` is joined after the child's own\n// ids, and an explicit `aria-invalid` on the child wins. Template\n// comments do not count as children (JSX comments); the\n// placeholder of a `v-if` that renders nothing does, like `{false}` in a\n// React children array, so `<A v-if=\"x\" /><Input />` is not cloned.\n// - anything else (text, several roots, a `v-for` list) renders as it is,\n// like a React child that is not a single element.\n// - content a wrapper forwards with `<slot />` counts as its own roots, as\n// React's `children` passed on to Field are the elements themselves.\n// - As in React only `class` reaches the root element; other attributes are\n// dropped. On a `row` they all reach it, as React's rest props do.\n\nexport type FieldChildProps = {\n id: string\n /** The label's id. Bind it to a control that the label's `for` cannot name, such as a Select trigger. */\n \"aria-labelledby\"?: string\n \"aria-describedby\"?: string\n \"aria-invalid\"?: boolean\n opticallyAlign?: \"start\" | \"end\"\n}\n\ntype Props = Record<string, unknown>\n\nconst CONTROL_SIZES: ReadonlySet<unknown> = new Set([\"3xs\", \"2xs\", \"xs\", \"sm\", \"md\", \"lg\", \"xl\", \"2xl\", \"3xl\"])\n\n/** The slot props, recording whether the slot read them (the render-prop form). */\nconst trackUse = (target: FieldChildProps) => {\n const state = { used: false }\n const proxy = new Proxy(target, {\n get(object, key, receiver) {\n state.used = true\n return Reflect.get(object, key, receiver)\n },\n has(object, key) {\n state.used = true\n return Reflect.has(object, key)\n },\n ownKeys(object) {\n state.used = true\n return Reflect.ownKeys(object)\n },\n getOwnPropertyDescriptor(object, key) {\n state.used = true\n return Reflect.getOwnPropertyDescriptor(object, key)\n },\n })\n return { proxy, state }\n}\n\n/** Like React's `isValidElement` for a slot root: an element or a component. */\nconst isElementVNode = (node: VNode) => {\n const { type } = node\n if (type === Fragment || type === Text || type === Comment || type === Static) return false\n if (typeof type === \"object\" && type !== null && (\"__isTeleport\" in type || \"__isSuspense\" in type)) return false\n return true\n}\n\n/**\n * Template comments (`<!-- note -->`) are no children, like JSX comments.\n * The empty placeholder of a `v-if` that renders nothing (or of a `false`\n * child) still counts, like `{false}` in a React children array.\n */\nconst isTemplateComment = (node: VNodeArrayChildren[number]) =>\n isVNode(node) && node.type === Comment && !!node.children && node.children !== \"v-if\"\n\n/**\n * Unwraps content forwarded through a wrapper's `<slot />`: the fragment\n * `renderSlot` creates, keyed `_<slot name>`.\n */\nconst unwrapForwardedSlots = (nodes: VNodeArrayChildren): VNodeArrayChildren =>\n nodes.flatMap((node) =>\n isVNode(node) &&\n node.type === Fragment &&\n typeof node.key === \"string\" &&\n node.key.startsWith(\"_\") &&\n Array.isArray(node.children)\n ? unwrapForwardedSlots(node.children)\n : [node],\n )\n\n/** The one element a non-render-prop slot holds, if it holds exactly one. */\nconst singleElement = (nodes: VNodeArrayChildren): VNode | undefined => {\n const roots = unwrapForwardedSlots(nodes).filter((node) => !isTemplateComment(node))\n if (roots.length !== 1) return undefined\n const [root] = roots\n return isVNode(root) && isElementVNode(root) ? root : undefined\n}\n\n/** A child prop under its attribute name or its camelCase prop name. */\nconst childProp = (props: Props, name: string) => props[name] ?? props[camelize(name)]\n\n/**\n * `cloneElement(child, fieldProps)` with React Field's merging rules. A child\n * without its own name (`aria-label`, `aria-labelledby`, `label`, a `#label`\n * slot) is also labelled by the field's label, which names triggers `for`\n * cannot reach.\n */\nconst cloneWithFieldProps = (child: VNode, fieldChildProps: FieldChildProps, labelId: string): VNode => {\n const own: Props = { ...(child.props ?? {}) }\n const childDescribedBy = childProp(own, \"aria-describedby\") as string | undefined\n const childInvalid = childProp(own, \"aria-invalid\")\n const childSlots = child.children\n const childNamed =\n childProp(own, \"aria-label\") != null ||\n childProp(own, \"aria-labelledby\") != null ||\n own.label != null ||\n (childSlots != null && typeof childSlots === \"object\" && !Array.isArray(childSlots) && \"label\" in childSlots)\n\n const overrides: Props = {\n ...fieldChildProps,\n // Merge with the child's own associations instead of overwriting them\n \"aria-describedby\": [childDescribedBy, fieldChildProps[\"aria-describedby\"]].filter(Boolean).join(\" \") || undefined,\n \"aria-invalid\": childInvalid ?? fieldChildProps[\"aria-invalid\"],\n ...(!childNamed && { \"aria-labelledby\": labelId }),\n }\n\n // One spelling per prop, so the field's value is the one that applies\n for (const key of Object.keys(overrides)) {\n for (const spelling of [key, camelize(key), key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)]) {\n delete own[spelling]\n }\n }\n\n return cloneVNode({ ...child, props: own } as VNode, overrides)\n}\n\nconst FieldComponent = defineComponent({\n name: \"Field\",\n inheritAttrs: false,\n props: {\n /** Label text for the field (or the `label` slot) */\n label: String,\n /** Helper text below the label, linked via `aria-describedby` (or the `description` slot) */\n description: String,\n /**\n * Error message below the control (or the `errorMessage` slot). When set,\n * the control gets `aria-invalid=\"true\"`. Rendered with FieldError.\n */\n errorMessage: String,\n /** Content at the end of the label row, such as a link that acts on the field (or the `labelAction` slot) */\n labelAction: String,\n /** Font size of the label, to match the control's size. @default the child control's `size`, else \"md\" */\n size: String as PropType<ControlSize>,\n /** Shows a required asterisk after the label. Visual only: no `required` attribute. @default false */\n required: { type: Boolean, default: false },\n /** Layout direction of label and control. @default \"horizontal\" on a `row`, else \"vertical\" */\n orientation: String as PropType<\"vertical\" | \"horizontal\">,\n /** One line of a FieldGroup: label at the start, control at the end. @default false */\n row: { type: Boolean, default: false },\n /** Overrides the generated id set on the control and the label's `for`. A child's own `id` wins over both. */\n id: String,\n /** Optically aligns the control's text with surrounding content; passed to the control. */\n opticallyAlign: String as PropType<\"start\" | \"end\">,\n },\n slots: Object as SlotsType<{\n /** The control. Read the props to render them yourself (React's render prop). */\n default?: (fieldProps: FieldChildProps) => VNodeChild\n label?: () => VNodeChild\n labelAction?: () => VNodeChild\n description?: () => VNodeChild\n errorMessage?: () => VNodeChild\n }>,\n setup(props, { attrs, slots }) {\n const generatedId = useId()\n\n return () => {\n const { required, row, opticallyAlign } = props\n const orientation = props.orientation ?? (row ? \"horizontal\" : \"vertical\")\n const fieldId = props.id || `field-${generatedId}`\n const labelId = `${fieldId}-label`\n\n const description = slots.description ? slots.description() : props.description\n const hasDescription = slots.description ? hasContent(description as VNodeArrayChildren) : !!description\n const errorMessage = slots.errorMessage ? slots.errorMessage() : props.errorMessage\n const hasError = slots.errorMessage ? hasContent(errorMessage as VNodeArrayChildren) : !!errorMessage\n\n const descriptionId = hasDescription ? `${fieldId}-description` : undefined\n const errorId = hasError ? `${fieldId}-error` : undefined\n\n // Build aria-describedby from description + error IDs\n const ariaDescribedBy = [descriptionId, errorId].filter(Boolean).join(\" \") || undefined\n const invalid = hasError\n\n // Props to inject into the child control\n const fieldChildProps: FieldChildProps = {\n \"id\": fieldId,\n \"aria-describedby\": ariaDescribedBy,\n \"aria-invalid\": invalid || undefined,\n ...(opticallyAlign && { opticallyAlign }),\n }\n\n // Resolve the children\n let resolvedChildren: VNodeArrayChildren | undefined\n let childSize: unknown\n // The control's id, which the label's `for` has to match\n let controlId = fieldId\n if (slots.default) {\n const { proxy, state } = trackUse({ ...fieldChildProps, \"aria-labelledby\": labelId })\n const nodes = slots.default(proxy) as VNodeArrayChildren\n const child = state.used ? undefined : singleElement(nodes)\n childSize = child?.props?.size\n controlId = (child?.props?.id as string | undefined) || fieldId\n resolvedChildren = child ? [cloneWithFieldProps(child, { ...fieldChildProps, id: controlId }, labelId)] : nodes\n }\n const size = props.size ?? (CONTROL_SIZES.has(childSize) ? (childSize as ControlSize) : \"md\")\n\n const labelAction = slots.labelAction ? slots.labelAction() : props.labelAction\n const hasLabelAction = slots.labelAction ? hasContent(labelAction as VNodeArrayChildren) : !!labelAction\n const labelElement = h(\"label\", { class: s.Label, id: labelId, for: controlId }, [\n slots.label ? slots.label() : props.label,\n required ? h(\"span\", { \"class\": s.RequiredIndicator, \"aria-hidden\": \"true\" }, \"*\") : null,\n ])\n\n return h(\n \"div\",\n {\n ...(row && attrs),\n \"class\": clsx(s.Field, attrs.class as ClassValue),\n \"data-size\": size,\n \"data-orientation\": orientation,\n \"data-optically-align\": opticallyAlign,\n \"data-row\": row ? \"\" : undefined,\n },\n [\n h(\"div\", { class: s.LabelGroup }, [\n hasLabelAction\n ? h(\"div\", { class: s.LabelRow }, [labelElement, h(\"div\", { class: s.LabelAction }, [labelAction as VNodeChild])])\n : labelElement,\n hasDescription ? h(\"div\", { class: s.Description, id: descriptionId }, [description as VNodeChild]) : null,\n ]),\n h(\"div\", { class: s.Content }, [\n h(\"div\", { class: s.Control }, resolvedChildren),\n hasError ? h(FieldError, { id: errorId }, () => [errorMessage as VNodeChild]) : null,\n ]),\n ],\n )\n }\n },\n})\n\ntype FieldOwnProps = InstanceType<typeof FieldComponent>[\"$props\"]\n\n/** Field props; `class` reaches the root element. */\nexport type FieldProps = FieldOwnProps & { class?: ClassValue }\n\n/** Form field wrapper with label, description, error and accessibility linking. */\nexport const Field = FieldComponent as typeof FieldComponent & { new (): { $props: FieldProps } }\n"]}
|
|
1
|
+
{"version":3,"file":"Field.js","sourceRoot":"","sources":["../../../../src/components/Field/Field.ts"],"names":[],"mappings":"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","sourcesContent":["import clsx, { type ClassValue } from \"clsx\"\nimport {\n Comment,\n Fragment,\n Static,\n Text,\n camelize,\n cloneVNode,\n defineComponent,\n h,\n isVNode,\n useId,\n type PropType,\n type SlotsType,\n type VNode,\n type VNodeArrayChildren,\n type VNodeChild,\n} from \"vue\"\nimport type { ControlSize } from \"@plexui/styles/types\"\nimport { FieldError } from \"../FieldError/index.js\"\nimport { hasContent } from \"../../lib/slotContent.js\"\nimport s from \"@plexui/styles/components/Field/Field.module.css\"\n\n// Vue port of packages/ui/src/components/Field/Field.tsx.\n//\n// - `label`, `description` and `errorMessage` take a string prop or a slot of\n// the same name (the slot wins), like React's ReactNode props.\n// - Children: React accepts an element, which Field clones with the field\n// props, or a render prop `(fieldProps) => ReactNode`, which it leaves alone.\n// Both are the default slot here, which receives the field props:\n// - a slot that reads them (`v-slot=\"fieldProps\"` + `v-bind=\"fieldProps\"`,\n// `v-slot=\"{ id }\"`, `(fieldProps) => h(...)`) is the render prop: its\n// content renders as it is.\n// - a slot that does not read them and holds exactly one element or\n// component gets it cloned like React's `cloneElement`: `opticallyAlign`\n// replaces the child's own, the child's own `id` stays and the label's\n// `for` follows it, `aria-describedby` is joined after the child's own\n// ids, and an explicit `aria-invalid` on the child wins. Template\n// comments do not count as children (JSX comments); the\n// placeholder of a `v-if` that renders nothing does, like `{false}` in a\n// React children array, so `<A v-if=\"x\" /><Input />` is not cloned.\n// - anything else (text, several roots, a `v-for` list) renders as it is,\n// like a React child that is not a single element.\n// - content a wrapper forwards with `<slot />` counts as its own roots, as\n// React's `children` passed on to Field are the elements themselves.\n// - As in React only `class` reaches the root element; other attributes are\n// dropped. On a `row` they all reach it, as React's rest props do.\n\nexport type FieldChildProps = {\n id: string\n /** The label's id. Bind it to a control that the label's `for` cannot name, such as a Select trigger. */\n \"aria-labelledby\"?: string\n \"aria-describedby\"?: string\n \"aria-invalid\"?: boolean\n opticallyAlign?: \"start\" | \"end\"\n}\n\ntype Props = Record<string, unknown>\n\nconst CONTROL_SIZES: ReadonlySet<unknown> = new Set([\"3xs\", \"2xs\", \"xs\", \"sm\", \"md\", \"lg\", \"xl\", \"2xl\", \"3xl\"])\n\n/** The slot props, recording whether the slot read them (the render-prop form). */\nconst trackUse = (target: FieldChildProps) => {\n const state = { used: false }\n const proxy = new Proxy(target, {\n get(object, key, receiver) {\n state.used = true\n return Reflect.get(object, key, receiver)\n },\n has(object, key) {\n state.used = true\n return Reflect.has(object, key)\n },\n ownKeys(object) {\n state.used = true\n return Reflect.ownKeys(object)\n },\n getOwnPropertyDescriptor(object, key) {\n state.used = true\n return Reflect.getOwnPropertyDescriptor(object, key)\n },\n })\n return { proxy, state }\n}\n\n/** Like React's `isValidElement` for a slot root: an element or a component. */\nconst isElementVNode = (node: VNode) => {\n const { type } = node\n if (type === Fragment || type === Text || type === Comment || type === Static) return false\n if (typeof type === \"object\" && type !== null && (\"__isTeleport\" in type || \"__isSuspense\" in type)) return false\n return true\n}\n\n/**\n * Template comments (`<!-- note -->`) are no children, like JSX comments.\n * The empty placeholder of a `v-if` that renders nothing (or of a `false`\n * child) still counts, like `{false}` in a React children array.\n */\nconst isTemplateComment = (node: VNodeArrayChildren[number]) =>\n isVNode(node) && node.type === Comment && !!node.children && node.children !== \"v-if\"\n\n/**\n * Unwraps content forwarded through a wrapper's `<slot />`: the fragment\n * `renderSlot` creates, keyed `_<slot name>`.\n */\nconst unwrapForwardedSlots = (nodes: VNodeArrayChildren): VNodeArrayChildren =>\n nodes.flatMap((node) =>\n isVNode(node) &&\n node.type === Fragment &&\n typeof node.key === \"string\" &&\n node.key.startsWith(\"_\") &&\n Array.isArray(node.children)\n ? unwrapForwardedSlots(node.children)\n : [node],\n )\n\n/** The one element a non-render-prop slot holds, if it holds exactly one. */\nconst singleElement = (nodes: VNodeArrayChildren): VNode | undefined => {\n const roots = unwrapForwardedSlots(nodes).filter((node) => !isTemplateComment(node))\n if (roots.length !== 1) return undefined\n const [root] = roots\n return isVNode(root) && isElementVNode(root) ? root : undefined\n}\n\n/** A child prop under its attribute name or its camelCase prop name. */\nconst childProp = (props: Props, name: string) => props[name] ?? props[camelize(name)]\n\n/**\n * `cloneElement(child, fieldProps)` with React Field's merging rules. A child\n * without its own name (`aria-label`, `aria-labelledby`, `label`, a `#label`\n * slot) is also labelled by the field's label, which names triggers `for`\n * cannot reach.\n */\nconst cloneWithFieldProps = (child: VNode, fieldChildProps: FieldChildProps, labelId: string): VNode => {\n const own: Props = { ...(child.props ?? {}) }\n const childDescribedBy = childProp(own, \"aria-describedby\") as string | undefined\n const childInvalid = childProp(own, \"aria-invalid\")\n const childSlots = child.children\n const childNamed =\n childProp(own, \"aria-label\") != null ||\n childProp(own, \"aria-labelledby\") != null ||\n own.label != null ||\n (childSlots != null && typeof childSlots === \"object\" && !Array.isArray(childSlots) && \"label\" in childSlots)\n\n const overrides: Props = {\n ...fieldChildProps,\n // Merge with the child's own associations instead of overwriting them\n \"aria-describedby\": [childDescribedBy, fieldChildProps[\"aria-describedby\"]].filter(Boolean).join(\" \") || undefined,\n \"aria-invalid\": childInvalid ?? fieldChildProps[\"aria-invalid\"],\n ...(!childNamed && { \"aria-labelledby\": labelId }),\n }\n\n // One spelling per prop, so the field's value is the one that applies\n for (const key of Object.keys(overrides)) {\n for (const spelling of [key, camelize(key), key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)]) {\n delete own[spelling]\n }\n }\n\n return cloneVNode({ ...child, props: own } as VNode, overrides)\n}\n\nconst FieldComponent = defineComponent({\n name: \"Field\",\n inheritAttrs: false,\n props: {\n /** Label text for the field (or the `label` slot) */\n label: String,\n /** Helper text below the label, linked via `aria-describedby` (or the `description` slot) */\n description: String,\n /**\n * Error message below the control (or the `errorMessage` slot). When set,\n * the control gets `aria-invalid=\"true\"`. Rendered with FieldError.\n */\n errorMessage: String,\n /** Content at the end of the label row, such as a link that acts on the field (or the `labelAction` slot) */\n labelAction: String,\n /** Font size of the label, to match the control's size. @default the child control's `size`, else \"md\" */\n size: String as PropType<ControlSize>,\n /** Shows a required asterisk after the label. Visual only: no `required` attribute. @default false */\n required: { type: Boolean, default: false },\n /** Layout direction of label and control. @default \"horizontal\" on a `row`, else \"vertical\" */\n orientation: String as PropType<\"vertical\" | \"horizontal\">,\n /** One line of a FieldGroup: label at the start, control at the end. @default false */\n row: { type: Boolean, default: false },\n /** On a `row`, the control takes the rest of the line after the label. @default false */\n fill: { type: Boolean, default: false },\n /** Overrides the generated id set on the control and the label's `for`. A child's own `id` wins over both. */\n id: String,\n /** Optically aligns the control's text with surrounding content; passed to the control. */\n opticallyAlign: String as PropType<\"start\" | \"end\">,\n },\n slots: Object as SlotsType<{\n /** The control. Read the props to render them yourself (React's render prop). */\n default?: (fieldProps: FieldChildProps) => VNodeChild\n label?: () => VNodeChild\n labelAction?: () => VNodeChild\n description?: () => VNodeChild\n errorMessage?: () => VNodeChild\n }>,\n setup(props, { attrs, slots }) {\n const generatedId = useId()\n\n return () => {\n const { required, row, opticallyAlign } = props\n const orientation = props.orientation ?? (row ? \"horizontal\" : \"vertical\")\n const fieldId = props.id || `field-${generatedId}`\n const labelId = `${fieldId}-label`\n\n const description = slots.description ? slots.description() : props.description\n const hasDescription = slots.description ? hasContent(description as VNodeArrayChildren) : !!description\n const errorMessage = slots.errorMessage ? slots.errorMessage() : props.errorMessage\n const hasError = slots.errorMessage ? hasContent(errorMessage as VNodeArrayChildren) : !!errorMessage\n\n const descriptionId = hasDescription ? `${fieldId}-description` : undefined\n const errorId = hasError ? `${fieldId}-error` : undefined\n\n // Build aria-describedby from description + error IDs\n const ariaDescribedBy = [descriptionId, errorId].filter(Boolean).join(\" \") || undefined\n const invalid = hasError\n\n // Props to inject into the child control\n const fieldChildProps: FieldChildProps = {\n \"id\": fieldId,\n \"aria-describedby\": ariaDescribedBy,\n \"aria-invalid\": invalid || undefined,\n ...(opticallyAlign && { opticallyAlign }),\n }\n\n // Resolve the children\n let resolvedChildren: VNodeArrayChildren | undefined\n let childSize: unknown\n // The control's id, which the label's `for` has to match\n let controlId = fieldId\n if (slots.default) {\n const { proxy, state } = trackUse({ ...fieldChildProps, \"aria-labelledby\": labelId })\n const nodes = slots.default(proxy) as VNodeArrayChildren\n const child = state.used ? undefined : singleElement(nodes)\n childSize = child?.props?.size\n controlId = (child?.props?.id as string | undefined) || fieldId\n resolvedChildren = child ? [cloneWithFieldProps(child, { ...fieldChildProps, id: controlId }, labelId)] : nodes\n }\n const size = props.size ?? (CONTROL_SIZES.has(childSize) ? (childSize as ControlSize) : \"md\")\n\n const labelAction = slots.labelAction ? slots.labelAction() : props.labelAction\n const hasLabelAction = slots.labelAction ? hasContent(labelAction as VNodeArrayChildren) : !!labelAction\n const labelElement = h(\"label\", { class: s.Label, id: labelId, for: controlId }, [\n slots.label ? slots.label() : props.label,\n required ? h(\"span\", { \"class\": s.RequiredIndicator, \"aria-hidden\": \"true\" }, \"*\") : null,\n ])\n\n return h(\n \"div\",\n {\n ...(row && attrs),\n \"class\": clsx(s.Field, attrs.class as ClassValue),\n \"data-size\": size,\n \"data-orientation\": orientation,\n \"data-optically-align\": opticallyAlign,\n \"data-row\": row ? \"\" : undefined,\n \"data-fill\": props.fill ? \"\" : undefined,\n },\n [\n h(\"div\", { class: s.LabelGroup }, [\n hasLabelAction\n ? h(\"div\", { class: s.LabelRow }, [labelElement, h(\"div\", { class: s.LabelAction }, [labelAction as VNodeChild])])\n : labelElement,\n hasDescription ? h(\"div\", { class: s.Description, id: descriptionId }, [description as VNodeChild]) : null,\n ]),\n h(\"div\", { class: s.Content }, [\n h(\"div\", { class: s.Control }, resolvedChildren),\n hasError ? h(FieldError, { id: errorId }, () => [errorMessage as VNodeChild]) : null,\n ]),\n ],\n )\n }\n },\n})\n\ntype FieldOwnProps = InstanceType<typeof FieldComponent>[\"$props\"]\n\n/** Field props; `class` reaches the root element. */\nexport type FieldProps = FieldOwnProps & { class?: ClassValue }\n\n/** Form field wrapper with label, description, error and accessibility linking. */\nexport const Field = FieldComponent as typeof FieldComponent & { new (): { $props: FieldProps } }\n"]}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { computed, defineComponent, h, inject, mergeProps, provide, ref, } from "vue";
|
|
2
|
+
import s from "@plexui/styles/components/Page/Page.module.css";
|
|
3
|
+
// A trackpad's small settle at the top must not flash the line.
|
|
4
|
+
const SCROLLED_AFTER = 4;
|
|
5
|
+
const PAGE_CONTEXT = Symbol("Page");
|
|
6
|
+
function usePageContext(part) {
|
|
7
|
+
const scrolled = inject(PAGE_CONTEXT, null);
|
|
8
|
+
if (!scrolled) {
|
|
9
|
+
throw new Error(`${part} must be used within Page`);
|
|
10
|
+
}
|
|
11
|
+
return scrolled;
|
|
12
|
+
}
|
|
13
|
+
const PageComponent = defineComponent({
|
|
14
|
+
name: "Page",
|
|
15
|
+
props: {
|
|
16
|
+
/**
|
|
17
|
+
* Width of the column that header and content share: `"full"` (edge to
|
|
18
|
+
* edge, for tables), `"wide"` (1280px, centred, for overviews) or `"column"`
|
|
19
|
+
* (768px, centred, for settings and forms). Set `--page-width` on the page
|
|
20
|
+
* for any other width.
|
|
21
|
+
* @default "full"
|
|
22
|
+
*/
|
|
23
|
+
width: { type: String, default: "full" },
|
|
24
|
+
},
|
|
25
|
+
setup(props, { slots }) {
|
|
26
|
+
provide(PAGE_CONTEXT, ref(false));
|
|
27
|
+
return () => h("div", { "class": s.Page, "data-width": props.width }, slots.default?.());
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
export const Page = PageComponent;
|
|
31
|
+
/** The band above the content, one column with it (React `Page.Header`). */
|
|
32
|
+
export const PageHeader = defineComponent({
|
|
33
|
+
name: "PageHeader",
|
|
34
|
+
props: {
|
|
35
|
+
/**
|
|
36
|
+
* Shows the line, and the glass on a phone, once the content has scrolled
|
|
37
|
+
* under the header. Turn it off when the content already draws a line of
|
|
38
|
+
* its own at that place.
|
|
39
|
+
* @default true
|
|
40
|
+
*/
|
|
41
|
+
divider: { type: Boolean, default: true },
|
|
42
|
+
},
|
|
43
|
+
setup(props, { slots }) {
|
|
44
|
+
const scrolled = usePageContext("PageHeader");
|
|
45
|
+
const line = computed(() => props.divider && scrolled.value);
|
|
46
|
+
return () => h("div", { "class": s.Header, "data-scrolled": line.value ? "" : undefined }, [
|
|
47
|
+
h("div", { class: s.Head }, slots.default?.()),
|
|
48
|
+
]);
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
function part(name, tag, className) {
|
|
52
|
+
return defineComponent({
|
|
53
|
+
name,
|
|
54
|
+
setup(_, { slots }) {
|
|
55
|
+
return () => h(tag, { class: className }, slots.default?.());
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/** The row of the title and the actions (React `Page.Bar`). */
|
|
60
|
+
export const PageBar = part("PageBar", "div", s.Bar);
|
|
61
|
+
/** Title with what stands beside it, a subtitle under it on a phone (React `Page.Heading`). */
|
|
62
|
+
export const PageHeading = part("PageHeading", "div", s.Heading);
|
|
63
|
+
/** Page heading, an `<h1>` (React `Page.Title`). */
|
|
64
|
+
export const PageTitle = part("PageTitle", "h1", s.Title);
|
|
65
|
+
/** Status of the page, a `<p>` (React `Page.Subtitle`). */
|
|
66
|
+
export const PageSubtitle = part("PageSubtitle", "p", s.Subtitle);
|
|
67
|
+
/** Buttons on the trailing side of the bar (React `Page.Actions`). */
|
|
68
|
+
export const PageActions = part("PageActions", "div", s.Actions);
|
|
69
|
+
/** The part of the page that scrolls (React `Page.Content`). */
|
|
70
|
+
export const PageContent = defineComponent({
|
|
71
|
+
name: "PageContent",
|
|
72
|
+
inheritAttrs: false,
|
|
73
|
+
props: {
|
|
74
|
+
/**
|
|
75
|
+
* The content fills the page and scrolls inside itself, as a table does:
|
|
76
|
+
* the page stops scrolling from 768px up.
|
|
77
|
+
* @default false
|
|
78
|
+
*/
|
|
79
|
+
fill: { type: Boolean, default: false },
|
|
80
|
+
},
|
|
81
|
+
setup(props, { attrs, slots }) {
|
|
82
|
+
const scrolled = usePageContext("PageContent");
|
|
83
|
+
const onScroll = (event) => {
|
|
84
|
+
scrolled.value = event.currentTarget.scrollTop > SCROLLED_AFTER;
|
|
85
|
+
};
|
|
86
|
+
return () => h("div", mergeProps({ "class": s.Content, "data-fill": props.fill ? "" : undefined, onScroll }, attrs), [h("div", { class: s.Column }, slots.default?.())]);
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
//# sourceMappingURL=Page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Page.js","sourceRoot":"","sources":["../../../../src/components/Page/Page.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n computed,\n defineComponent,\n h,\n inject,\n mergeProps,\n provide,\n ref,\n type HTMLAttributes,\n type InjectionKey,\n type PropType,\n type Ref,\n} from \"vue\"\nimport s from \"@plexui/styles/components/Page/Page.module.css\"\n\n// Vue port of packages/ui/src/components/Page/Page.tsx. Exports are flat\n// (`PageHeader` for `Page.Header`, ...). As in React, caller attributes are\n// applied after the page's own (`class` is merged), so they win.\n\nexport type PageWidth = \"full\" | \"wide\" | \"column\"\n\n// A trackpad's small settle at the top must not flash the line.\nconst SCROLLED_AFTER = 4\n\nconst PAGE_CONTEXT: InjectionKey<Ref<boolean>> = Symbol(\"Page\")\n\nfunction usePageContext(part: string): Ref<boolean> {\n const scrolled = inject(PAGE_CONTEXT, null)\n if (!scrolled) {\n throw new Error(`${part} must be used within Page`)\n }\n return scrolled\n}\n\nconst PageComponent = defineComponent({\n name: \"Page\",\n props: {\n /**\n * Width of the column that header and content share: `\"full\"` (edge to\n * edge, for tables), `\"wide\"` (1280px, centred, for overviews) or `\"column\"`\n * (768px, centred, for settings and forms). Set `--page-width` on the page\n * for any other width.\n * @default \"full\"\n */\n width: { type: String as PropType<PageWidth>, default: \"full\" },\n },\n setup(props, { slots }) {\n provide(PAGE_CONTEXT, ref(false))\n\n return () => h(\"div\", { \"class\": s.Page, \"data-width\": props.width }, slots.default?.())\n },\n})\n\n/** Props of `Page`. */\nexport type PageProps = { width?: PageWidth } & HTMLAttributes\n\nexport const Page = PageComponent as typeof PageComponent & { new (): { $props: PageProps } }\n\n/** The band above the content, one column with it (React `Page.Header`). */\nexport const PageHeader = defineComponent({\n name: \"PageHeader\",\n props: {\n /**\n * Shows the line, and the glass on a phone, once the content has scrolled\n * under the header. Turn it off when the content already draws a line of\n * its own at that place.\n * @default true\n */\n divider: { type: Boolean, default: true },\n },\n setup(props, { slots }) {\n const scrolled = usePageContext(\"PageHeader\")\n const line = computed(() => props.divider && scrolled.value)\n\n return () =>\n h(\"div\", { \"class\": s.Header, \"data-scrolled\": line.value ? \"\" : undefined }, [\n h(\"div\", { class: s.Head }, slots.default?.()),\n ])\n },\n})\n\nfunction part(name: string, tag: string, className: string) {\n return defineComponent({\n name,\n setup(_, { slots }) {\n return () => h(tag, { class: className }, slots.default?.())\n },\n })\n}\n\n/** The row of the title and the actions (React `Page.Bar`). */\nexport const PageBar = part(\"PageBar\", \"div\", s.Bar)\n/** Title with what stands beside it, a subtitle under it on a phone (React `Page.Heading`). */\nexport const PageHeading = part(\"PageHeading\", \"div\", s.Heading)\n/** Page heading, an `<h1>` (React `Page.Title`). */\nexport const PageTitle = part(\"PageTitle\", \"h1\", s.Title)\n/** Status of the page, a `<p>` (React `Page.Subtitle`). */\nexport const PageSubtitle = part(\"PageSubtitle\", \"p\", s.Subtitle)\n/** Buttons on the trailing side of the bar (React `Page.Actions`). */\nexport const PageActions = part(\"PageActions\", \"div\", s.Actions)\n\n/** The part of the page that scrolls (React `Page.Content`). */\nexport const PageContent = defineComponent({\n name: \"PageContent\",\n inheritAttrs: false,\n props: {\n /**\n * The content fills the page and scrolls inside itself, as a table does:\n * the page stops scrolling from 768px up.\n * @default false\n */\n fill: { type: Boolean, default: false },\n },\n setup(props, { attrs, slots }) {\n const scrolled = usePageContext(\"PageContent\")\n\n const onScroll = (event: Event) => {\n scrolled.value = (event.currentTarget as HTMLElement).scrollTop > SCROLLED_AFTER\n }\n\n return () =>\n h(\n \"div\",\n mergeProps(\n { \"class\": s.Content, \"data-fill\": props.fill ? \"\" : undefined, onScroll },\n attrs,\n ),\n [h(\"div\", { class: s.Column }, slots.default?.())],\n )\n },\n})\n\n/** Props of the Page parts: native attributes of their element. */\nexport type PageSlotProps = HTMLAttributes\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/components/Page/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,WAAW,EACX,OAAO,EACP,WAAW,EACX,UAAU,EACV,WAAW,EACX,YAAY,EACZ,SAAS,GAIV,MAAM,WAAW,CAAA","sourcesContent":["export {\n Page,\n PageActions,\n PageBar,\n PageContent,\n PageHeader,\n PageHeading,\n PageSubtitle,\n PageTitle,\n type PageProps,\n type PageSlotProps,\n type PageWidth,\n} from \"./Page.js\"\n"]}
|
|
@@ -71,12 +71,12 @@ const selectProps = {
|
|
|
71
71
|
alignOffset: Number,
|
|
72
72
|
/** Collision detection of the menu. @default true */
|
|
73
73
|
avoidCollisions: { type: Boolean, default: true },
|
|
74
|
-
/** Width of the menu. @default
|
|
74
|
+
/** Width of the menu. @default the trigger width (fits the options when not block) */
|
|
75
75
|
listWidth: [Number, String],
|
|
76
|
-
/** `min-width` of the menu, in pixels. @default the trigger width
|
|
76
|
+
/** `min-width` of the menu, in pixels. @default the trigger width */
|
|
77
77
|
listMinWidth: [Number, String],
|
|
78
|
-
/** `max-width` of the menu, in pixels. @default "auto" */
|
|
79
|
-
listMaxWidth:
|
|
78
|
+
/** `max-width` of the menu, in pixels. @default "auto" (300 when not block) */
|
|
79
|
+
listMaxWidth: [Number, String],
|
|
80
80
|
/** Predicate used to filter searches */
|
|
81
81
|
searchPredicate: Function,
|
|
82
82
|
/** Placeholder of the search input */
|
|
@@ -149,8 +149,6 @@ const SelectComponent = defineComponent({
|
|
|
149
149
|
const align = computed(() => props.align ?? (block.value ? "center" : "start"));
|
|
150
150
|
// Flush with the trigger's edge, as the prop documents
|
|
151
151
|
const alignOffset = computed(() => props.alignOffset ?? 0);
|
|
152
|
-
// A menu is never narrower than the field it belongs to. Inline selects keep their 300px floor.
|
|
153
|
-
const listMinWidth = computed(() => props.listMinWidth ?? (block.value ? undefined : 300));
|
|
154
152
|
const change = (selection) => {
|
|
155
153
|
emit("update:value", Array.isArray(selection) ? selection.map((option) => option.value) : selection.value);
|
|
156
154
|
emit("change", selection);
|
|
@@ -196,6 +194,11 @@ const SelectComponent = defineComponent({
|
|
|
196
194
|
});
|
|
197
195
|
// Locking down searchable count to a single value
|
|
198
196
|
const searchable = computed(() => optionsCount.value > 15);
|
|
197
|
+
// A block menu matches its field. An inline one fits its options: no narrower than
|
|
198
|
+
// its trigger (the CSS fallback reads the trigger width), no wider than 300px.
|
|
199
|
+
const fitsOptions = computed(() => !block.value && props.listWidth === undefined);
|
|
200
|
+
const listWidth = computed(() => props.listWidth ?? (fitsOptions.value ? "auto" : undefined));
|
|
201
|
+
const listMaxWidth = computed(() => props.listMaxWidth ?? (fitsOptions.value ? 300 : "auto"));
|
|
199
202
|
const slotsVersion = ref(0);
|
|
200
203
|
onBeforeUpdate(() => {
|
|
201
204
|
slotsVersion.value++;
|
|
@@ -209,7 +212,8 @@ const SelectComponent = defineComponent({
|
|
|
209
212
|
block,
|
|
210
213
|
align,
|
|
211
214
|
alignOffset,
|
|
212
|
-
|
|
215
|
+
listWidth,
|
|
216
|
+
listMaxWidth,
|
|
213
217
|
searchable,
|
|
214
218
|
actions,
|
|
215
219
|
onSelect,
|
|
@@ -518,7 +522,7 @@ const SelectContentElement = markRaw(defineComponent({
|
|
|
518
522
|
const SelectPopoverContent = defineComponent({
|
|
519
523
|
name: "SelectPopoverContent",
|
|
520
524
|
setup() {
|
|
521
|
-
const { props, align, alignOffset,
|
|
525
|
+
const { props, align, alignOffset, listWidth, listMaxWidth } = useSelectContext();
|
|
522
526
|
const { handleRootOpenChange, popoverTriggerEl } = inject(CustomSelectContextKey);
|
|
523
527
|
// Radix (1.8.1) dismisses on the click that follows a primary pointer down outside
|
|
524
528
|
const { onPointerDownOutside, onFocusOutside } = useDeferredPointerDownOutside((originalEvent) => {
|
|
@@ -545,9 +549,9 @@ const SelectPopoverContent = defineComponent({
|
|
|
545
549
|
onPointerDownOutside,
|
|
546
550
|
onFocusOutside,
|
|
547
551
|
style: toCssVariables({
|
|
548
|
-
"select-list-width":
|
|
549
|
-
"select-list-min-width": listMinWidth
|
|
550
|
-
"select-list-max-width":
|
|
552
|
+
"select-list-width": listWidth.value,
|
|
553
|
+
"select-list-min-width": props.listMinWidth,
|
|
554
|
+
"select-list-max-width": listMaxWidth.value,
|
|
551
555
|
}),
|
|
552
556
|
}, { default: () => h(CustomSelectMenu) });
|
|
553
557
|
},
|
|
@@ -590,7 +594,7 @@ const focusMounted = (element) => {
|
|
|
590
594
|
const CustomSelectMenu = defineComponent({
|
|
591
595
|
name: "CustomSelectMenu",
|
|
592
596
|
setup() {
|
|
593
|
-
const { props, searchable, searchPredicate } = useSelectContext();
|
|
597
|
+
const { props, searchable, searchPredicate, listWidth } = useSelectContext();
|
|
594
598
|
const { handleOpenChange } = inject(CustomSelectContextKey);
|
|
595
599
|
const menuRef = shallowRef(null);
|
|
596
600
|
const listRef = shallowRef(null);
|
|
@@ -758,6 +762,10 @@ const CustomSelectMenu = defineComponent({
|
|
|
758
762
|
// On mount behavior
|
|
759
763
|
onMounted(() => {
|
|
760
764
|
syncActiveDescendant();
|
|
765
|
+
// A long list has a search field: the menu keeps the width of all its options as the results narrow.
|
|
766
|
+
if (searchable.value && listWidth.value === "auto" && menuRef.value) {
|
|
767
|
+
menuRef.value.style.minWidth = `${menuRef.value.offsetWidth}px`;
|
|
768
|
+
}
|
|
761
769
|
// Like React's mount effect, the scroll target is the highlight at mount
|
|
762
770
|
const initialHighlightedValue = highlightedValue.value;
|
|
763
771
|
// Ensure initial highlighted option is in view
|