@ansiversa/components 0.0.38 → 0.0.40
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 +129 -0
- package/src/styles/global.css +180 -5
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,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
import AvButton from "./AvButton.astro";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
id: string;
|
|
6
|
+
title?: 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
|
+
title = "Confirm",
|
|
18
|
+
description,
|
|
19
|
+
confirmLabel = "Confirm",
|
|
20
|
+
cancelLabel = "Cancel",
|
|
21
|
+
variant = "default",
|
|
22
|
+
busy = false,
|
|
23
|
+
className = "",
|
|
24
|
+
} = Astro.props as Props;
|
|
25
|
+
|
|
26
|
+
const labelId = `${id}-title`;
|
|
27
|
+
const descId = description ? `${id}-desc` : undefined;
|
|
28
|
+
const dialogClass = `av-dialog av-dialog--${variant} ${className}`.trim();
|
|
29
|
+
const dialogIdLiteral = JSON.stringify(id);
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
<dialog
|
|
33
|
+
id={id}
|
|
34
|
+
class={dialogClass}
|
|
35
|
+
aria-modal="true"
|
|
36
|
+
aria-labelledby={labelId}
|
|
37
|
+
aria-describedby={description ? descId : undefined}
|
|
38
|
+
>
|
|
39
|
+
<div class="av-dialog__panel" role="document">
|
|
40
|
+
<div class="av-dialog__header">
|
|
41
|
+
<h2 class="av-dialog__title" id={labelId}>{title}</h2>
|
|
42
|
+
{description ? <p class="av-dialog__desc" id={descId}>{description}</p> : null}
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div class="av-dialog__footer">
|
|
46
|
+
<AvButton type="button" variant="ghost" size="md" data-av-cancel disabled={busy}>
|
|
47
|
+
{cancelLabel}
|
|
48
|
+
</AvButton>
|
|
49
|
+
|
|
50
|
+
<AvButton
|
|
51
|
+
type="button"
|
|
52
|
+
variant="primary"
|
|
53
|
+
size="md"
|
|
54
|
+
data-av-confirm
|
|
55
|
+
className={variant === "danger" ? "av-dialog__confirm--danger" : ""}
|
|
56
|
+
disabled={busy}
|
|
57
|
+
>
|
|
58
|
+
{busy ? "Working…" : confirmLabel}
|
|
59
|
+
</AvButton>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</dialog>
|
|
63
|
+
|
|
64
|
+
<script is:inline>
|
|
65
|
+
(() => {
|
|
66
|
+
const dialog = document.getElementById(${dialogIdLiteral});
|
|
67
|
+
if (!dialog || !(dialog instanceof HTMLDialogElement)) return;
|
|
68
|
+
|
|
69
|
+
const confirmBtn = dialog.querySelector("[data-av-confirm]");
|
|
70
|
+
const cancelBtn = dialog.querySelector("[data-av-cancel]");
|
|
71
|
+
|
|
72
|
+
function openDialogById(targetId) {
|
|
73
|
+
const target = document.getElementById(targetId);
|
|
74
|
+
if (!target || !(target instanceof HTMLDialogElement)) return;
|
|
75
|
+
|
|
76
|
+
if (!target.open && typeof target.showModal === "function") {
|
|
77
|
+
target.showModal();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function closeDialogById(targetId) {
|
|
82
|
+
const target = document.getElementById(targetId);
|
|
83
|
+
if (!target || !(target instanceof HTMLDialogElement)) return;
|
|
84
|
+
|
|
85
|
+
if (target.open) {
|
|
86
|
+
target.close();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function dispatch(name) {
|
|
91
|
+
dialog.dispatchEvent(
|
|
92
|
+
new CustomEvent(name, {
|
|
93
|
+
bubbles: true,
|
|
94
|
+
detail: { id: dialog.id },
|
|
95
|
+
})
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const open = () => openDialogById(dialog.id);
|
|
100
|
+
const close = () => closeDialogById(dialog.id);
|
|
101
|
+
|
|
102
|
+
window.AvDialog = window.AvDialog || {};
|
|
103
|
+
window.AvDialog.open = (targetId) => openDialogById(targetId);
|
|
104
|
+
window.AvDialog.close = (targetId) => closeDialogById(targetId);
|
|
105
|
+
|
|
106
|
+
confirmBtn?.addEventListener("click", () => {
|
|
107
|
+
dispatch("av-confirm");
|
|
108
|
+
close();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
cancelBtn?.addEventListener("click", () => {
|
|
112
|
+
dispatch("av-cancel");
|
|
113
|
+
close();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
dialog.addEventListener("cancel", (event) => {
|
|
117
|
+
event.preventDefault();
|
|
118
|
+
dispatch("av-cancel");
|
|
119
|
+
close();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
dialog.addEventListener("click", (event) => {
|
|
123
|
+
if (event.target === dialog) {
|
|
124
|
+
dispatch("av-cancel");
|
|
125
|
+
close();
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
})();
|
|
129
|
+
</script>
|
package/src/styles/global.css
CHANGED
|
@@ -203,7 +203,6 @@
|
|
|
203
203
|
gap: var(--av-space-sm);
|
|
204
204
|
cursor: pointer;
|
|
205
205
|
list-style: none;
|
|
206
|
-
flex-wrap: wrap;
|
|
207
206
|
}
|
|
208
207
|
|
|
209
208
|
/* Remove default arrow */
|
|
@@ -226,18 +225,29 @@
|
|
|
226
225
|
}
|
|
227
226
|
|
|
228
227
|
.av-faq-question {
|
|
229
|
-
flex: 1 1
|
|
228
|
+
flex: 1 1 auto;
|
|
230
229
|
font-weight: 700;
|
|
231
230
|
font-size: 1.05rem;
|
|
232
231
|
line-height: 1.4;
|
|
233
232
|
}
|
|
234
233
|
|
|
235
234
|
.av-faq-actions {
|
|
236
|
-
flex: 1 1 100%;
|
|
237
235
|
display: flex;
|
|
238
236
|
gap: 0.5rem;
|
|
239
|
-
|
|
240
|
-
|
|
237
|
+
align-items: center;
|
|
238
|
+
flex: 0 0 auto;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
@media (max-width: 640px) {
|
|
242
|
+
.av-faq-summary {
|
|
243
|
+
flex-wrap: wrap;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.av-faq-actions {
|
|
247
|
+
flex: 1 1 100%;
|
|
248
|
+
justify-content: flex-end;
|
|
249
|
+
margin-top: 0.25rem;
|
|
250
|
+
}
|
|
241
251
|
}
|
|
242
252
|
|
|
243
253
|
.av-page {
|
|
@@ -619,6 +629,56 @@
|
|
|
619
629
|
box-shadow: none;
|
|
620
630
|
}
|
|
621
631
|
|
|
632
|
+
/* Confirm Dialog --------------------------------------- */
|
|
633
|
+
|
|
634
|
+
.av-dialog {
|
|
635
|
+
@apply fixed m-0 w-[min(640px,92vw)] border-0 bg-transparent p-0 text-slate-100 inset-0;
|
|
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-full rounded-2xl border border-slate-800/70 bg-slate-950/90 p-6 shadow-2xl flex flex-col gap-5;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
.av-dialog__header {
|
|
651
|
+
@apply space-y-2;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
.av-dialog__title {
|
|
655
|
+
@apply text-lg font-semibold text-slate-100;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
.av-dialog__desc {
|
|
659
|
+
@apply text-sm leading-relaxed text-slate-400;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
.av-dialog__footer {
|
|
663
|
+
@apply mt-1 flex items-center justify-end gap-3;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
.av-dialog--danger .av-dialog__panel {
|
|
667
|
+
@apply border-rose-500/50 shadow-[0_20px_40px_rgba(244,63,94,0.25)];
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
.av-dialog--danger .av-dialog__title {
|
|
671
|
+
@apply text-rose-100;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
.av-dialog__confirm--danger {
|
|
675
|
+
@apply border-rose-400/80 bg-rose-500/70 text-rose-50 shadow-[0_0_30px_rgba(244,63,94,0.35)];
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
.av-dialog__confirm--danger:hover {
|
|
679
|
+
@apply bg-rose-500/80 border-rose-200;
|
|
680
|
+
}
|
|
681
|
+
|
|
622
682
|
/* Forms ------------------------------------------------- */
|
|
623
683
|
|
|
624
684
|
.av-text-error {
|
|
@@ -1265,7 +1325,122 @@
|
|
|
1265
1325
|
background-size: 200% 100%;
|
|
1266
1326
|
}
|
|
1267
1327
|
|
|
1328
|
+
/* Accordion */
|
|
1329
|
+
.av-accordion {
|
|
1330
|
+
border: 1px solid var(--av-border-subtle);
|
|
1331
|
+
border-radius: var(--av-radius-sm);
|
|
1332
|
+
background: var(--av-surface);
|
|
1333
|
+
box-shadow: var(--av-shadow-soft);
|
|
1334
|
+
overflow: hidden;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
.av-accordion--soft {
|
|
1338
|
+
background: var(--av-surface-soft);
|
|
1339
|
+
border-color: rgba(148, 163, 184, 0.2);
|
|
1340
|
+
box-shadow: none;
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
.av-accordion--auth {
|
|
1344
|
+
background: rgba(255, 255, 255, 0.02);
|
|
1345
|
+
border-color: var(--av-border-strong);
|
|
1346
|
+
box-shadow: var(--av-shadow-soft);
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
.av-accordion__item {
|
|
1350
|
+
border-bottom: 1px solid var(--av-border-subtle);
|
|
1351
|
+
transition: background var(--av-transition-fast), border-color var(--av-transition-fast);
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
.av-accordion__item:last-of-type {
|
|
1355
|
+
border-bottom: none;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
.av-accordion__summary {
|
|
1359
|
+
display: flex;
|
|
1360
|
+
align-items: center;
|
|
1361
|
+
gap: 0.75rem;
|
|
1362
|
+
padding: 0.95rem 1.1rem;
|
|
1363
|
+
list-style: none;
|
|
1364
|
+
cursor: pointer;
|
|
1365
|
+
color: var(--av-text);
|
|
1366
|
+
font-weight: 600;
|
|
1367
|
+
letter-spacing: -0.01em;
|
|
1368
|
+
transition: color var(--av-transition-fast), background var(--av-transition-fast);
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
.av-accordion__summary::-webkit-details-marker {
|
|
1372
|
+
display: none;
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
.av-accordion__summary:hover {
|
|
1376
|
+
background: rgba(255, 255, 255, 0.03);
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
.av-accordion--soft .av-accordion__summary:hover {
|
|
1380
|
+
background: rgba(255, 255, 255, 0.02);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
.av-accordion__icon {
|
|
1384
|
+
width: 1.4rem;
|
|
1385
|
+
height: 1.4rem;
|
|
1386
|
+
border-radius: var(--av-radius-xs);
|
|
1387
|
+
border: 1px solid var(--av-border-subtle);
|
|
1388
|
+
display: inline-flex;
|
|
1389
|
+
align-items: center;
|
|
1390
|
+
justify-content: center;
|
|
1391
|
+
color: var(--av-text-muted);
|
|
1392
|
+
background: rgba(255, 255, 255, 0.02);
|
|
1393
|
+
transition: transform var(--av-transition-fast), color var(--av-transition-fast),
|
|
1394
|
+
background var(--av-transition-fast), border-color var(--av-transition-fast);
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
.av-accordion__icon::before {
|
|
1398
|
+
content: "›";
|
|
1399
|
+
transform: translateY(-1px);
|
|
1400
|
+
display: block;
|
|
1401
|
+
font-weight: 700;
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
details[open] > .av-accordion__summary .av-accordion__icon {
|
|
1405
|
+
transform: rotate(90deg);
|
|
1406
|
+
color: var(--av-text-strong);
|
|
1407
|
+
border-color: var(--av-border-strong);
|
|
1408
|
+
background: var(--av-primary-soft);
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
details[open] > .av-accordion__summary {
|
|
1412
|
+
color: var(--av-text-strong);
|
|
1413
|
+
background: rgba(255, 255, 255, 0.02);
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
.av-accordion__title {
|
|
1417
|
+
flex: 1;
|
|
1418
|
+
font-size: 1rem;
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
.av-accordion__content {
|
|
1422
|
+
padding: 0 1.1rem 1.1rem 1.1rem;
|
|
1423
|
+
color: var(--av-text-muted);
|
|
1424
|
+
line-height: 1.7;
|
|
1425
|
+
background: rgba(255, 255, 255, 0.01);
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
.av-accordion__item.is-disabled {
|
|
1429
|
+
opacity: 0.6;
|
|
1430
|
+
background: rgba(255, 255, 255, 0.02);
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
.av-accordion__item.is-disabled .av-accordion__summary {
|
|
1434
|
+
cursor: not-allowed;
|
|
1435
|
+
pointer-events: none;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
.av-accordion__item.is-disabled .av-accordion__icon {
|
|
1439
|
+
background: rgba(255, 255, 255, 0.04);
|
|
1440
|
+
border-color: rgba(148, 163, 184, 0.2);
|
|
1441
|
+
}
|
|
1268
1442
|
|
|
1443
|
+
|
|
1269
1444
|
@keyframes av-shimmer {
|
|
1270
1445
|
0% { background-position: 0 0; }
|
|
1271
1446
|
100% { background-position: 200% 0; }
|