@pienter/ui 0.3.0 → 0.7.1
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/CHANGELOG.md +258 -0
- package/CONVENTIONS.md +342 -385
- package/README.md +33 -18
- package/components/display/record-details/RecordDetails.vue +61 -0
- package/components/display/record-details/record-details.css +37 -0
- package/components/display/record-details/types.ts +8 -0
- package/components/feedback/toast/toast.css +1 -1
- package/components/form/block-editor/BlockEditor.vue +454 -0
- package/components/form/block-editor/block-editor.css +149 -0
- package/components/form/block-editor/types.ts +15 -0
- package/components/form/combobox/Combobox.vue +42 -72
- package/components/form/combobox/combobox.css +1 -1
- package/components/form/form/Form.vue +1 -2
- package/components/form/label/label.css +1 -1
- package/components/form/number-field/NumberField.vue +1 -2
- package/components/form/number-field/number-field.css +1 -1
- package/components/form/radio-group/RadioGroup.vue +2 -5
- package/components/form/record-form/RecordFields.vue +128 -0
- package/components/form/record-form/RecordForm.vue +116 -0
- package/components/form/record-form/fields.ts +20 -0
- package/components/form/record-form/record-form.css +15 -0
- package/components/form/record-form/types.ts +28 -0
- package/components/form/slider/slider.css +2 -3
- package/components/form/tags-input/tags-input.css +1 -2
- package/components/form/text-input/text-input.css +2 -0
- package/components/form/textarea/textarea.css +1 -1
- package/components/layout/accordion/Accordion.vue +6 -27
- package/components/layout/collapsible/Collapsible.vue +9 -19
- package/components/layout/index/Index.vue +353 -0
- package/components/layout/index/index.css +114 -0
- package/components/layout/index/useIndex.ts +390 -0
- package/components/layout/table/table.css +5 -4
- package/components/navigation/breadcrumb/Breadcrumb.vue +24 -5
- package/components/navigation/breadcrumb/breadcrumb.css +15 -0
- package/components/navigation/sidebar/Sidebar.vue +329 -84
- package/components/navigation/sidebar/SidebarMenuItem.vue +199 -0
- package/components/navigation/sidebar/context.ts +21 -0
- package/components/navigation/sidebar/sidebar.css +346 -78
- package/components/navigation/sidebar/types.ts +13 -1
- package/components/navigation/tabs/Tabs.vue +6 -0
- package/components/overlay/alert-dialog/AlertDialog.vue +10 -31
- package/components/overlay/command/Command.vue +10 -38
- package/components/overlay/command/command.css +1 -1
- package/components/overlay/dropdown-menu/DropdownMenu.vue +37 -62
- package/components/overlay/modal/Modal.vue +7 -28
- package/components/overlay/popover/Popover.vue +9 -31
- package/components/overlay/sheet/Sheet.vue +7 -28
- package/components/overlay/tooltip/Tooltip.vue +14 -19
- package/{utils/controllers/dialog.ts → composables/useDialog.ts} +90 -78
- package/composables/useDisclosure.ts +113 -0
- package/composables/useEventListener.ts +16 -0
- package/composables/useMenu.ts +212 -0
- package/{utils/controllers/popover.ts → composables/usePopover.ts} +107 -120
- package/package.json +16 -40
- package/styles/0-settings/colors.css +10 -0
- package/styles/0-settings/layout.css +18 -0
- package/styles/0-settings/motion.css +2 -2
- package/styles/0-settings/spacing.css +3 -1
- package/utils/a11y/focus.ts +9 -3
- package/utils/cms/index.ts +283 -0
- package/utils/cms/schema.json +126 -0
- package/utils/navigation/sidebar.ts +97 -0
- package/utils/validation/form.ts +6 -9
- package/components/action/button/Button.astro +0 -95
- package/components/action/button/IconButton.astro +0 -86
- package/components/display/avatar/Avatar.astro +0 -17
- package/components/display/avatar/AvatarStack.astro +0 -9
- package/components/display/badge/Badge.astro +0 -15
- package/components/display/empty/Empty.astro +0 -9
- package/components/display/icon/Icon.astro +0 -52
- package/components/feedback/alert/Alert.astro +0 -52
- package/components/feedback/progress/Progress.astro +0 -68
- package/components/feedback/skeleton/Skeleton.astro +0 -32
- package/components/feedback/spinner/Spinner.astro +0 -25
- package/components/feedback/toast/Toast.astro +0 -50
- package/components/form/checkbox/Checkbox.astro +0 -79
- package/components/form/date-input/DateInput.astro +0 -105
- package/components/form/form/Form.astro +0 -106
- package/components/form/input-otp/InputOTP.astro +0 -147
- package/components/form/label/Label.astro +0 -13
- package/components/form/number-field/NumberField.astro +0 -142
- package/components/form/radio-group/RadioGroup.astro +0 -105
- package/components/form/select/Select.astro +0 -105
- package/components/form/slider/Slider.astro +0 -205
- package/components/form/switch/Switch.astro +0 -75
- package/components/form/tags-input/TagsInput.astro +0 -153
- package/components/form/text-input/TextInput.astro +0 -84
- package/components/form/textarea/Textarea.astro +0 -86
- package/components/layout/card/Card.astro +0 -13
- package/components/layout/separator/Separator.astro +0 -31
- package/components/layout/table/Table.astro +0 -116
- package/components/navigation/breadcrumb/Breadcrumb.astro +0 -36
- package/components/navigation/navbar/Navbar.astro +0 -62
- package/components/navigation/pagination/PaginationFooter.astro +0 -24
- package/components/navigation/sidebar/Sidebar.astro +0 -132
- package/components/navigation/sidebar/SidebarItemRender.astro +0 -83
- package/components/navigation/sidebar/SidebarItemRender.vue +0 -98
- package/components/overlay/alert-dialog/AlertDialog.astro +0 -112
- package/components/overlay/modal/Modal.astro +0 -66
- package/utils/controllers/disclosure.ts +0 -117
- package/utils/controllers/form.ts +0 -524
- package/utils/controllers/index.ts +0 -39
- package/utils/controllers/menu.ts +0 -255
- package/utils/controllers/number-field.ts +0 -103
- package/utils/controllers/otp.ts +0 -252
- package/utils/controllers/sidebar.ts +0 -610
- package/utils/controllers/slider.ts +0 -336
- package/utils/controllers/tags-input.ts +0 -255
- package/utils/controllers/toast.ts +0 -426
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import './modal.css';
|
|
3
|
-
import { generateId } from '../../../utils/a11y/id.js';
|
|
4
|
-
|
|
5
|
-
interface Props {
|
|
6
|
-
id?: string;
|
|
7
|
-
open?: boolean;
|
|
8
|
-
persistent?: boolean;
|
|
9
|
-
title: string;
|
|
10
|
-
subtitle?: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const {
|
|
14
|
-
id,
|
|
15
|
-
open = false,
|
|
16
|
-
persistent = false,
|
|
17
|
-
title,
|
|
18
|
-
subtitle,
|
|
19
|
-
...rest
|
|
20
|
-
} = Astro.props;
|
|
21
|
-
|
|
22
|
-
const modalId = id ?? generateId('modal');
|
|
23
|
-
const titleId = `${modalId}-title`;
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
<dialog
|
|
27
|
-
id={modalId}
|
|
28
|
-
class="pui-modal"
|
|
29
|
-
data-state="closed"
|
|
30
|
-
data-pui-modal
|
|
31
|
-
data-persistent={persistent ? 'true' : undefined}
|
|
32
|
-
data-open-initially={open ? 'true' : undefined}
|
|
33
|
-
aria-labelledby={titleId}
|
|
34
|
-
{...rest}
|
|
35
|
-
>
|
|
36
|
-
<div class="pui-modal__head">
|
|
37
|
-
<h2 id={titleId} class="pui-modal__title">{title}</h2>
|
|
38
|
-
{subtitle && <p class="pui-modal__sub">{subtitle}</p>}
|
|
39
|
-
</div>
|
|
40
|
-
<div class="pui-modal__body"><slot /></div>
|
|
41
|
-
{
|
|
42
|
-
Astro.slots.has('footer') && (
|
|
43
|
-
<div class="pui-modal__foot">
|
|
44
|
-
<slot name="footer" />
|
|
45
|
-
</div>
|
|
46
|
-
)
|
|
47
|
-
}
|
|
48
|
-
</dialog>
|
|
49
|
-
|
|
50
|
-
<script>
|
|
51
|
-
import { mountDialog } from '@pienter/ui/utils/controllers/dialog.js';
|
|
52
|
-
|
|
53
|
-
document
|
|
54
|
-
.querySelectorAll<HTMLDialogElement>('dialog[data-pui-modal]')
|
|
55
|
-
.forEach((el) => {
|
|
56
|
-
const node = el as HTMLDialogElement & {
|
|
57
|
-
_dialog?: ReturnType<typeof mountDialog>;
|
|
58
|
-
};
|
|
59
|
-
if (node._dialog) return; // idempotent — guard against double-mounts (HMR, multiple <Modal> imports)
|
|
60
|
-
const persistent = el.dataset.persistent === 'true';
|
|
61
|
-
const openInitially = el.dataset.openInitially === 'true';
|
|
62
|
-
const control = mountDialog(el, { persistent });
|
|
63
|
-
node._dialog = control;
|
|
64
|
-
if (openInitially) control.open();
|
|
65
|
-
});
|
|
66
|
-
</script>
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Disclosure controller — shared by Accordion and Collapsible per the
|
|
3
|
-
* locked controller-sharing mapping in `CONVENTIONS.md`.
|
|
4
|
-
*
|
|
5
|
-
* The v1 disclosure family is built on top of the **native HTML
|
|
6
|
-
* `<details>` / `<summary>` element pair** per the locked "Use native
|
|
7
|
-
* APIs where applicable" rule. The browser already handles:
|
|
8
|
-
*
|
|
9
|
-
* - Expand / collapse on summary click
|
|
10
|
-
* - The `[open]` attribute reflecting current state
|
|
11
|
-
* - Enter / Space activation when the summary is focused
|
|
12
|
-
* - Tab focus moves to the summary, not the panel content
|
|
13
|
-
* - Initial closed state when the `open` attribute is absent
|
|
14
|
-
*
|
|
15
|
-
* What the browser does NOT provide and we layer on top:
|
|
16
|
-
*
|
|
17
|
-
* - **Single-open coordination** for Accordion mode — when one
|
|
18
|
-
* `<details>` opens, sibling `<details>` elements within the same
|
|
19
|
-
* mount root close. Native `<details>` has no built-in mutual
|
|
20
|
-
* exclusion (the obsolete `name` attribute exists in some browsers
|
|
21
|
-
* but is not reliable cross-browser in 2026).
|
|
22
|
-
* - A **callback** so framework code can react to toggles without
|
|
23
|
-
* each `<details>` wiring its own `@toggle` listener.
|
|
24
|
-
* - A consistent **teardown** lifecycle that matches the rest of the
|
|
25
|
-
* controller archetype.
|
|
26
|
-
*
|
|
27
|
-
* Mount on:
|
|
28
|
-
* - A single `<details>` element for Collapsible (no `singleOpen`
|
|
29
|
-
* needed; the controller is essentially a callback bridge).
|
|
30
|
-
* - A wrapper `<div>` containing multiple `<details>` for Accordion
|
|
31
|
-
* (`singleOpen: true` makes them mutually exclusive).
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
export interface DisclosureConfig {
|
|
35
|
-
/**
|
|
36
|
-
* When `true`, opening any **top-level** `<details>` within the
|
|
37
|
-
* mount root closes all sibling top-level `<details>` elements
|
|
38
|
-
* (Accordion single-open mode). A "top-level" `<details>` here
|
|
39
|
-
* means one whose nearest ancestor `<details>` lies outside the
|
|
40
|
-
* mount root — i.e., a disclosure that belongs to THIS mount, not
|
|
41
|
-
* a nested disclosure inside an item's panel content.
|
|
42
|
-
*
|
|
43
|
-
* When `false`, multiple disclosures can be open simultaneously
|
|
44
|
-
* (Collapsible / Accordion multi-open mode). Default: `false`.
|
|
45
|
-
*/
|
|
46
|
-
singleOpen?: boolean;
|
|
47
|
-
/**
|
|
48
|
-
* Called whenever a `<details>` within the mount root toggles.
|
|
49
|
-
* Receives the toggled element and its new open state. Frameworks
|
|
50
|
-
* use this to reflect the change back into reactive state (e.g.,
|
|
51
|
-
* Vue `update:open`). The controller does NOT track open state
|
|
52
|
-
* itself — the DOM is the source of truth via `el.open`.
|
|
53
|
-
*/
|
|
54
|
-
onToggle?: (target: HTMLDetailsElement, open: boolean) => void;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Mount disclosure behavior on `el`. The mount root may be either a
|
|
59
|
-
* single `<details>` (Collapsible) or a wrapper containing multiple
|
|
60
|
-
* `<details>` children (Accordion). Returns a teardown function that
|
|
61
|
-
* removes the listener; the controller owns no other resources.
|
|
62
|
-
*
|
|
63
|
-
* The `toggle` event is captured at the mount root because it bubbles
|
|
64
|
-
* from the originating `<details>`. This means a single listener
|
|
65
|
-
* covers any number of disclosures without per-element wiring.
|
|
66
|
-
*/
|
|
67
|
-
export function mountDisclosure(
|
|
68
|
-
el: HTMLElement,
|
|
69
|
-
config: DisclosureConfig = {},
|
|
70
|
-
): () => void {
|
|
71
|
-
const singleOpen = config.singleOpen ?? false;
|
|
72
|
-
const onToggle = config.onToggle;
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* A `<details>` is "top-level" relative to `el` when no other
|
|
76
|
-
* `<details>` lies between it and `el` in the ancestor chain. Only
|
|
77
|
-
* top-level disclosures participate in single-open coordination —
|
|
78
|
-
* nested disclosures are part of the consumer's panel content and
|
|
79
|
-
* must not be closed by the outer mount.
|
|
80
|
-
*/
|
|
81
|
-
function isTopLevel(details: HTMLDetailsElement): boolean {
|
|
82
|
-
const parentDetails = details.parentElement?.closest('details') ?? null;
|
|
83
|
-
if (!parentDetails) return true;
|
|
84
|
-
// If the parent `<details>` is outside the mount root, treat
|
|
85
|
-
// `details` as top-level for THIS mount.
|
|
86
|
-
return !el.contains(parentDetails);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function handleToggle(event: Event): void {
|
|
90
|
-
const target = event.target;
|
|
91
|
-
if (!(target instanceof HTMLDetailsElement)) return;
|
|
92
|
-
if (!el.contains(target)) return;
|
|
93
|
-
|
|
94
|
-
if (singleOpen && target.open && isTopLevel(target)) {
|
|
95
|
-
// Close every other top-level `<details>` rooted in this mount.
|
|
96
|
-
const allDetails =
|
|
97
|
-
el.querySelectorAll<HTMLDetailsElement>('details');
|
|
98
|
-
allDetails.forEach((other) => {
|
|
99
|
-
if (other === target) return;
|
|
100
|
-
if (!isTopLevel(other)) return;
|
|
101
|
-
if (other.open) other.open = false;
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
onToggle?.(target, target.open);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// `toggle` does not bubble in the standards sense — but a capturing
|
|
109
|
-
// listener fires for any descendant `<details>` toggle. Use capture
|
|
110
|
-
// phase so a single mount-root listener covers every `<details>`
|
|
111
|
-
// within the scope.
|
|
112
|
-
el.addEventListener('toggle', handleToggle, true);
|
|
113
|
-
|
|
114
|
-
return function teardown(): void {
|
|
115
|
-
el.removeEventListener('toggle', handleToggle, true);
|
|
116
|
-
};
|
|
117
|
-
}
|