@ansiversa/components 0.0.39 → 0.0.41
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/index.ts +3 -1
- package/package.json +1 -1
- package/src/AvAccordion.astro +123 -0
- package/src/AvConfirmDialog.astro +124 -0
- package/src/styles/global.css +166 -0
package/index.ts
CHANGED
|
@@ -25,4 +25,6 @@ export { default as AvChipList } from './src/AvChipList.astro';
|
|
|
25
25
|
export { default as AvTemplateCard } from './src/AvTemplateCard.astro';
|
|
26
26
|
export { default as AvTemplateGrid } from './src/AvTemplateGrid.astro';
|
|
27
27
|
export { default as AvDrawer } from './src/AvDrawer.astro';
|
|
28
|
-
export
|
|
28
|
+
export { default as AvAccordion } from './src/AvAccordion.astro';
|
|
29
|
+
export { default as AvConfirmDialog } from './src/AvConfirmDialog.astro';
|
|
30
|
+
export * from "./src/alpine";
|
package/package.json
CHANGED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
---
|
|
2
|
+
export interface AvAccordionItem {
|
|
3
|
+
id?: string;
|
|
4
|
+
title: string;
|
|
5
|
+
content: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
items: AvAccordionItem[];
|
|
11
|
+
mode?: "single" | "multiple";
|
|
12
|
+
defaultOpenIds?: string[];
|
|
13
|
+
variant?: "default" | "soft" | "auth";
|
|
14
|
+
className?: string;
|
|
15
|
+
[attrs: string]: any;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
items = [],
|
|
20
|
+
mode = "multiple",
|
|
21
|
+
defaultOpenIds = [],
|
|
22
|
+
variant = "default",
|
|
23
|
+
className = "",
|
|
24
|
+
...attrs
|
|
25
|
+
} = Astro.props as Props;
|
|
26
|
+
|
|
27
|
+
const accordionId = `av-accordion-${Math.random().toString(36).slice(2)}`;
|
|
28
|
+
|
|
29
|
+
const preparedItems = items.map((item, index) => {
|
|
30
|
+
const id = item.id ?? `item-${index}`;
|
|
31
|
+
const isDefaultOpen = !item.disabled && defaultOpenIds?.includes(id);
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
...item,
|
|
35
|
+
id,
|
|
36
|
+
isDefaultOpen,
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const wrapperClass = [
|
|
41
|
+
"av-accordion",
|
|
42
|
+
`av-accordion--${variant}`,
|
|
43
|
+
className,
|
|
44
|
+
]
|
|
45
|
+
.filter(Boolean)
|
|
46
|
+
.join(" ");
|
|
47
|
+
|
|
48
|
+
const isSingleMode = mode === "single";
|
|
49
|
+
const needsScript = isSingleMode || preparedItems.some((item) => item.disabled);
|
|
50
|
+
|
|
51
|
+
const scriptContent = `
|
|
52
|
+
const root = document.getElementById("${accordionId}");
|
|
53
|
+
const isSingleMode = ${JSON.stringify(isSingleMode)};
|
|
54
|
+
|
|
55
|
+
if (root) {
|
|
56
|
+
root.addEventListener("click", (event) => {
|
|
57
|
+
const target = event.target;
|
|
58
|
+
const summary = target instanceof HTMLElement ? target.closest("summary") : null;
|
|
59
|
+
if (!summary) return;
|
|
60
|
+
|
|
61
|
+
const details = summary.parentElement;
|
|
62
|
+
if (details?.dataset.disabled === "true") {
|
|
63
|
+
event.preventDefault();
|
|
64
|
+
event.stopPropagation();
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
root.addEventListener("toggle", (event) => {
|
|
69
|
+
const details = event.target;
|
|
70
|
+
if (!(details instanceof HTMLDetailsElement)) return;
|
|
71
|
+
|
|
72
|
+
const isDisabled = details.dataset.disabled === "true";
|
|
73
|
+
if (isDisabled && details.open) {
|
|
74
|
+
details.open = false;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (isSingleMode && details.open) {
|
|
79
|
+
root
|
|
80
|
+
.querySelectorAll("details[data-av-accordion-item]")
|
|
81
|
+
.forEach((item) => {
|
|
82
|
+
if (item !== details && item instanceof HTMLDetailsElement) {
|
|
83
|
+
item.open = false;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
`;
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
<div
|
|
93
|
+
id={accordionId}
|
|
94
|
+
class={wrapperClass}
|
|
95
|
+
data-av-accordion-root
|
|
96
|
+
data-mode={mode}
|
|
97
|
+
{...attrs}
|
|
98
|
+
>
|
|
99
|
+
{preparedItems.map((item) => (
|
|
100
|
+
<details
|
|
101
|
+
class={`av-accordion__item ${item.disabled ? "is-disabled" : ""}`}
|
|
102
|
+
open={item.isDefaultOpen}
|
|
103
|
+
data-av-accordion-item
|
|
104
|
+
data-id={item.id}
|
|
105
|
+
data-disabled={item.disabled ? "true" : undefined}
|
|
106
|
+
aria-disabled={item.disabled ? "true" : undefined}
|
|
107
|
+
disabled={item.disabled ? true : undefined}
|
|
108
|
+
>
|
|
109
|
+
<summary
|
|
110
|
+
class="av-accordion__summary"
|
|
111
|
+
aria-disabled={item.disabled ? "true" : undefined}
|
|
112
|
+
tabindex={item.disabled ? -1 : undefined}
|
|
113
|
+
>
|
|
114
|
+
<span class="av-accordion__icon" aria-hidden="true"></span>
|
|
115
|
+
<span class="av-accordion__title">{item.title}</span>
|
|
116
|
+
</summary>
|
|
117
|
+
|
|
118
|
+
<div class="av-accordion__content" set:html={item.content}></div>
|
|
119
|
+
</details>
|
|
120
|
+
))}
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
{needsScript ? <script is:inline set:html={scriptContent}></script> : null}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { AvButton } from "@ansiversa/components";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
id: string;
|
|
6
|
+
headline?: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
confirmLabel?: string;
|
|
9
|
+
cancelLabel?: string;
|
|
10
|
+
variant?: "default" | "danger";
|
|
11
|
+
busy?: boolean;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const {
|
|
16
|
+
id,
|
|
17
|
+
headline = "Confirm",
|
|
18
|
+
description,
|
|
19
|
+
confirmLabel = "Confirm",
|
|
20
|
+
cancelLabel = "Cancel",
|
|
21
|
+
variant = "default",
|
|
22
|
+
busy = false,
|
|
23
|
+
className = "",
|
|
24
|
+
...attrs
|
|
25
|
+
} = Astro.props as Props & Record<string, unknown>;
|
|
26
|
+
|
|
27
|
+
if (!id) {
|
|
28
|
+
throw new Error("AvConfirmDialog requires an `id` prop.");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const labelId = `${id}-title`;
|
|
32
|
+
const descId = description ? `${id}-desc` : undefined;
|
|
33
|
+
const dialogClass = `av-dialog av-dialog--${variant} ${className}`.trim();
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
<dialog
|
|
37
|
+
id={id}
|
|
38
|
+
class={dialogClass}
|
|
39
|
+
aria-modal="true"
|
|
40
|
+
aria-labelledby={labelId}
|
|
41
|
+
aria-describedby={description ? descId : undefined}
|
|
42
|
+
{...attrs}
|
|
43
|
+
>
|
|
44
|
+
<div class="av-dialog__panel" role="document">
|
|
45
|
+
<div class="av-dialog__header">
|
|
46
|
+
<h2 class="av-dialog__title" id={labelId}>{headline}</h2>
|
|
47
|
+
{description ? <p class="av-dialog__desc" id={descId}>{description}</p> : null}
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div class="av-dialog__footer">
|
|
51
|
+
<AvButton type="button" variant="ghost" size="md" data-av-cancel disabled={busy}>
|
|
52
|
+
{cancelLabel}
|
|
53
|
+
</AvButton>
|
|
54
|
+
|
|
55
|
+
<AvButton
|
|
56
|
+
type="button"
|
|
57
|
+
variant="primary"
|
|
58
|
+
size="md"
|
|
59
|
+
data-av-confirm
|
|
60
|
+
className={variant === "danger" ? "av-dialog__confirm--danger" : ""}
|
|
61
|
+
disabled={busy}
|
|
62
|
+
>
|
|
63
|
+
{busy ? "Working..." : confirmLabel}
|
|
64
|
+
</AvButton>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
</dialog>
|
|
68
|
+
|
|
69
|
+
<script define:vars={{ dialogId: id }}>
|
|
70
|
+
(() => {
|
|
71
|
+
const dialog = document.getElementById(dialogId);
|
|
72
|
+
if (!dialog) return;
|
|
73
|
+
|
|
74
|
+
const confirmBtn = dialog.querySelector("[data-av-confirm]");
|
|
75
|
+
const cancelBtn = dialog.querySelector("[data-av-cancel]");
|
|
76
|
+
|
|
77
|
+
function dispatch(name) {
|
|
78
|
+
dialog.dispatchEvent(
|
|
79
|
+
new CustomEvent(name, { bubbles: true, detail: { id: dialogId } })
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function open() {
|
|
84
|
+
if (!dialog.open && typeof dialog.showModal === "function") {
|
|
85
|
+
dialog.showModal();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function close() {
|
|
90
|
+
if (dialog.open) dialog.close();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
window.AvDialog = window.AvDialog || {};
|
|
94
|
+
window.AvDialog.open = (targetId) => {
|
|
95
|
+
if (targetId === dialogId) open();
|
|
96
|
+
};
|
|
97
|
+
window.AvDialog.close = (targetId) => {
|
|
98
|
+
if (targetId === dialogId) close();
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
confirmBtn?.addEventListener("click", () => {
|
|
102
|
+
dispatch("av-confirm");
|
|
103
|
+
close();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
cancelBtn?.addEventListener("click", () => {
|
|
107
|
+
dispatch("av-cancel");
|
|
108
|
+
close();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
dialog.addEventListener("cancel", (e) => {
|
|
112
|
+
e.preventDefault(); // Escape
|
|
113
|
+
dispatch("av-cancel");
|
|
114
|
+
close();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
dialog.addEventListener("click", (e) => {
|
|
118
|
+
if (e.target === dialog) {
|
|
119
|
+
dispatch("av-cancel");
|
|
120
|
+
close();
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
})();
|
|
124
|
+
</script>
|
package/src/styles/global.css
CHANGED
|
@@ -629,6 +629,57 @@
|
|
|
629
629
|
box-shadow: none;
|
|
630
630
|
}
|
|
631
631
|
|
|
632
|
+
/* Confirm Dialog --------------------------------------- */
|
|
633
|
+
|
|
634
|
+
.av-dialog {
|
|
635
|
+
@apply fixed inset-0 m-0 w-full max-w-none border-0 bg-transparent p-0 text-slate-100;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
.av-dialog[open] {
|
|
639
|
+
@apply grid place-items-center;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
.av-dialog::backdrop {
|
|
643
|
+
@apply bg-slate-950/80 backdrop-blur-sm;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
.av-dialog__panel {
|
|
647
|
+
@apply w-[min(640px,92vw)] rounded-2xl border border-slate-800/70 bg-slate-950/90 p-6 shadow-2xl flex flex-col gap-5;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
.av-dialog__header {
|
|
652
|
+
@apply space-y-2;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
.av-dialog__title {
|
|
656
|
+
@apply text-lg font-semibold text-slate-100;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
.av-dialog__desc {
|
|
660
|
+
@apply text-sm leading-relaxed text-slate-400;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
.av-dialog__footer {
|
|
664
|
+
@apply mt-1 flex items-center justify-end gap-3;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
.av-dialog--danger .av-dialog__panel {
|
|
668
|
+
@apply border-rose-500/50 shadow-[0_20px_40px_rgba(244,63,94,0.25)];
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
.av-dialog--danger .av-dialog__title {
|
|
672
|
+
@apply text-rose-100;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
.av-dialog__confirm--danger {
|
|
676
|
+
@apply border-rose-400/80 bg-rose-500/70 text-rose-50 shadow-[0_0_30px_rgba(244,63,94,0.35)];
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
.av-dialog__confirm--danger:hover {
|
|
680
|
+
@apply bg-rose-500/80 border-rose-200;
|
|
681
|
+
}
|
|
682
|
+
|
|
632
683
|
/* Forms ------------------------------------------------- */
|
|
633
684
|
|
|
634
685
|
.av-text-error {
|
|
@@ -1275,7 +1326,122 @@
|
|
|
1275
1326
|
background-size: 200% 100%;
|
|
1276
1327
|
}
|
|
1277
1328
|
|
|
1329
|
+
/* Accordion */
|
|
1330
|
+
.av-accordion {
|
|
1331
|
+
border: 1px solid var(--av-border-subtle);
|
|
1332
|
+
border-radius: var(--av-radius-sm);
|
|
1333
|
+
background: var(--av-surface);
|
|
1334
|
+
box-shadow: var(--av-shadow-soft);
|
|
1335
|
+
overflow: hidden;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
.av-accordion--soft {
|
|
1339
|
+
background: var(--av-surface-soft);
|
|
1340
|
+
border-color: rgba(148, 163, 184, 0.2);
|
|
1341
|
+
box-shadow: none;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
.av-accordion--auth {
|
|
1345
|
+
background: rgba(255, 255, 255, 0.02);
|
|
1346
|
+
border-color: var(--av-border-strong);
|
|
1347
|
+
box-shadow: var(--av-shadow-soft);
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
.av-accordion__item {
|
|
1351
|
+
border-bottom: 1px solid var(--av-border-subtle);
|
|
1352
|
+
transition: background var(--av-transition-fast), border-color var(--av-transition-fast);
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
.av-accordion__item:last-of-type {
|
|
1356
|
+
border-bottom: none;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
.av-accordion__summary {
|
|
1360
|
+
display: flex;
|
|
1361
|
+
align-items: center;
|
|
1362
|
+
gap: 0.75rem;
|
|
1363
|
+
padding: 0.95rem 1.1rem;
|
|
1364
|
+
list-style: none;
|
|
1365
|
+
cursor: pointer;
|
|
1366
|
+
color: var(--av-text);
|
|
1367
|
+
font-weight: 600;
|
|
1368
|
+
letter-spacing: -0.01em;
|
|
1369
|
+
transition: color var(--av-transition-fast), background var(--av-transition-fast);
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
.av-accordion__summary::-webkit-details-marker {
|
|
1373
|
+
display: none;
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
.av-accordion__summary:hover {
|
|
1377
|
+
background: rgba(255, 255, 255, 0.03);
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
.av-accordion--soft .av-accordion__summary:hover {
|
|
1381
|
+
background: rgba(255, 255, 255, 0.02);
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
.av-accordion__icon {
|
|
1385
|
+
width: 1.4rem;
|
|
1386
|
+
height: 1.4rem;
|
|
1387
|
+
border-radius: var(--av-radius-xs);
|
|
1388
|
+
border: 1px solid var(--av-border-subtle);
|
|
1389
|
+
display: inline-flex;
|
|
1390
|
+
align-items: center;
|
|
1391
|
+
justify-content: center;
|
|
1392
|
+
color: var(--av-text-muted);
|
|
1393
|
+
background: rgba(255, 255, 255, 0.02);
|
|
1394
|
+
transition: transform var(--av-transition-fast), color var(--av-transition-fast),
|
|
1395
|
+
background var(--av-transition-fast), border-color var(--av-transition-fast);
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
.av-accordion__icon::before {
|
|
1399
|
+
content: "›";
|
|
1400
|
+
transform: translateY(-1px);
|
|
1401
|
+
display: block;
|
|
1402
|
+
font-weight: 700;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
details[open] > .av-accordion__summary .av-accordion__icon {
|
|
1406
|
+
transform: rotate(90deg);
|
|
1407
|
+
color: var(--av-text-strong);
|
|
1408
|
+
border-color: var(--av-border-strong);
|
|
1409
|
+
background: var(--av-primary-soft);
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
details[open] > .av-accordion__summary {
|
|
1413
|
+
color: var(--av-text-strong);
|
|
1414
|
+
background: rgba(255, 255, 255, 0.02);
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
.av-accordion__title {
|
|
1418
|
+
flex: 1;
|
|
1419
|
+
font-size: 1rem;
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
.av-accordion__content {
|
|
1423
|
+
padding: 0 1.1rem 1.1rem 1.1rem;
|
|
1424
|
+
color: var(--av-text-muted);
|
|
1425
|
+
line-height: 1.7;
|
|
1426
|
+
background: rgba(255, 255, 255, 0.01);
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
.av-accordion__item.is-disabled {
|
|
1430
|
+
opacity: 0.6;
|
|
1431
|
+
background: rgba(255, 255, 255, 0.02);
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
.av-accordion__item.is-disabled .av-accordion__summary {
|
|
1435
|
+
cursor: not-allowed;
|
|
1436
|
+
pointer-events: none;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
.av-accordion__item.is-disabled .av-accordion__icon {
|
|
1440
|
+
background: rgba(255, 255, 255, 0.04);
|
|
1441
|
+
border-color: rgba(148, 163, 184, 0.2);
|
|
1442
|
+
}
|
|
1278
1443
|
|
|
1444
|
+
|
|
1279
1445
|
@keyframes av-shimmer {
|
|
1280
1446
|
0% { background-position: 0 0; }
|
|
1281
1447
|
100% { background-position: 200% 0; }
|