@adia-ai/web-components 0.8.6 → 0.8.8

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.
Files changed (108) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +3 -3
  3. package/dist/host.min.css +1 -1
  4. package/dist/host.sheet.js +1 -1
  5. package/dist/theme-provider.min.js +2 -2
  6. package/dist/web-components.min.css +1 -1
  7. package/dist/web-components.min.js +1 -1
  8. package/dist/web-components.sheet.js +1 -1
  9. package/package.json +2 -1
  10. package/patterns/access-requests/access-requests.examples.html +132 -0
  11. package/patterns/access-requests/access-requests.html +60 -0
  12. package/patterns/admin-keys-and-export/admin-keys-and-export.examples.html +236 -0
  13. package/patterns/admin-keys-and-export/admin-keys-and-export.html +64 -0
  14. package/patterns/admin-shell/admin-shell.examples.html +327 -0
  15. package/patterns/admin-shell/admin-shell.html +44 -0
  16. package/patterns/agent-cost/agent-cost.examples.html +111 -0
  17. package/patterns/agent-cost/agent-cost.html +62 -0
  18. package/patterns/agent-memory/agent-memory.examples.html +155 -0
  19. package/patterns/agent-memory/agent-memory.html +64 -0
  20. package/patterns/agent-prompt-library/agent-prompt-library.examples.html +181 -0
  21. package/patterns/agent-prompt-library/agent-prompt-library.html +64 -0
  22. package/patterns/agent-tool-call/agent-tool-call.examples.html +123 -0
  23. package/patterns/agent-tool-call/agent-tool-call.html +60 -0
  24. package/patterns/alert-dialog/alert-dialog.examples.html +105 -0
  25. package/patterns/alert-dialog/alert-dialog.html +57 -0
  26. package/patterns/app-nav/app-nav.examples.html +421 -0
  27. package/patterns/app-nav/app-nav.html +50 -0
  28. package/patterns/approvals/approvals.examples.html +131 -0
  29. package/patterns/approvals/approvals.html +58 -0
  30. package/patterns/audit-log/audit-log.examples.html +74 -0
  31. package/patterns/audit-log/audit-log.examples.js +60 -0
  32. package/patterns/audit-log/audit-log.html +63 -0
  33. package/patterns/bulk-action-toolbar/bulk-action-toolbar.examples.html +89 -0
  34. package/patterns/bulk-action-toolbar/bulk-action-toolbar.examples.js +69 -0
  35. package/patterns/bulk-action-toolbar/bulk-action-toolbar.html +62 -0
  36. package/patterns/changelog-feed/changelog-feed.examples.html +151 -0
  37. package/patterns/changelog-feed/changelog-feed.html +57 -0
  38. package/patterns/chart-in-card/chart-in-card.examples.html +193 -0
  39. package/patterns/chart-in-card/chart-in-card.examples.js +46 -0
  40. package/patterns/chart-in-card/chart-in-card.html +66 -0
  41. package/patterns/chat-shell/chat-shell.examples.html +129 -0
  42. package/patterns/chat-shell/chat-shell.html +46 -0
  43. package/patterns/comments-and-collaboration/comments-and-collaboration.examples.html +241 -0
  44. package/patterns/comments-and-collaboration/comments-and-collaboration.html +66 -0
  45. package/patterns/component-tokens/component-tokens.examples.html +483 -0
  46. package/patterns/component-tokens/component-tokens.html +42 -0
  47. package/patterns/conversion-funnel/conversion-funnel.examples.html +158 -0
  48. package/patterns/conversion-funnel/conversion-funnel.examples.js +4 -0
  49. package/patterns/conversion-funnel/conversion-funnel.html +64 -0
  50. package/patterns/data-tables-inline-edit-and-tree/data-tables-inline-edit-and-tree.examples.html +196 -0
  51. package/patterns/data-tables-inline-edit-and-tree/data-tables-inline-edit-and-tree.html +58 -0
  52. package/patterns/diff-review/diff-review.examples.html +147 -0
  53. package/patterns/diff-review/diff-review.html +56 -0
  54. package/patterns/editor-shell/editor-shell.examples.html +139 -0
  55. package/patterns/editor-shell/editor-shell.html +62 -0
  56. package/patterns/export-flow/export-flow.examples.html +152 -0
  57. package/patterns/export-flow/export-flow.examples.js +37 -0
  58. package/patterns/export-flow/export-flow.html +65 -0
  59. package/patterns/filter-bar/filter-bar.examples.html +116 -0
  60. package/patterns/filter-bar/filter-bar.examples.js +23 -0
  61. package/patterns/filter-bar/filter-bar.html +70 -0
  62. package/patterns/form-drawer/form-drawer.examples.html +210 -0
  63. package/patterns/form-drawer/form-drawer.html +64 -0
  64. package/patterns/form-system/form-system.examples.html +383 -0
  65. package/patterns/form-system/form-system.html +48 -0
  66. package/patterns/forms-conditional-and-autocomplete/forms-conditional-and-autocomplete.examples.html +189 -0
  67. package/patterns/forms-conditional-and-autocomplete/forms-conditional-and-autocomplete.html +64 -0
  68. package/patterns/inline-dialog/inline-dialog.examples.html +143 -0
  69. package/patterns/inline-dialog/inline-dialog.html +59 -0
  70. package/patterns/kanban-board/kanban-board.examples.html +224 -0
  71. package/patterns/kanban-board/kanban-board.html +58 -0
  72. package/patterns/layout/layout.examples.html +365 -0
  73. package/patterns/layout/layout.html +54 -0
  74. package/patterns/marketing-engagement/marketing-engagement.examples.html +280 -0
  75. package/patterns/marketing-engagement/marketing-engagement.html +64 -0
  76. package/patterns/notifications-bell-and-digest/notifications-bell-and-digest.examples.html +213 -0
  77. package/patterns/notifications-bell-and-digest/notifications-bell-and-digest.html +58 -0
  78. package/patterns/overview/overview.examples.html +119 -0
  79. package/patterns/overview/overview.html +42 -0
  80. package/patterns/permissions-matrix/permissions-matrix.examples.html +91 -0
  81. package/patterns/permissions-matrix/permissions-matrix.examples.js +28 -0
  82. package/patterns/permissions-matrix/permissions-matrix.html +56 -0
  83. package/patterns/permissions-role-picker/permissions-role-picker.examples.html +81 -0
  84. package/patterns/permissions-role-picker/permissions-role-picker.html +56 -0
  85. package/patterns/permissions-sharing/permissions-sharing.examples.html +175 -0
  86. package/patterns/permissions-sharing/permissions-sharing.html +66 -0
  87. package/patterns/profile-public-and-verification/profile-public-and-verification.examples.html +227 -0
  88. package/patterns/profile-public-and-verification/profile-public-and-verification.html +70 -0
  89. package/patterns/record-detail-drawer/record-detail-drawer.examples.html +164 -0
  90. package/patterns/record-detail-drawer/record-detail-drawer.html +64 -0
  91. package/patterns/responsive-shell-sidebar/README.md +37 -0
  92. package/patterns/responsive-shell-sidebar/responsive-shell-sidebar.css +39 -0
  93. package/patterns/responsive-shell-sidebar/responsive-shell-sidebar.examples.html +208 -0
  94. package/patterns/responsive-shell-sidebar/responsive-shell-sidebar.html +47 -0
  95. package/patterns/retention-cohort/retention-cohort.examples.html +149 -0
  96. package/patterns/retention-cohort/retention-cohort.examples.js +37 -0
  97. package/patterns/retention-cohort/retention-cohort.html +62 -0
  98. package/patterns/search-discovery/search-discovery.examples.html +263 -0
  99. package/patterns/search-discovery/search-discovery.html +58 -0
  100. package/patterns/system-banners/system-banners.examples.html +150 -0
  101. package/patterns/system-banners/system-banners.html +52 -0
  102. package/patterns/timezone-selector/timezone-selector.examples.html +82 -0
  103. package/patterns/timezone-selector/timezone-selector.examples.js +72 -0
  104. package/patterns/timezone-selector/timezone-selector.html +57 -0
  105. package/patterns/wizard-drawer/wizard-drawer.examples.html +144 -0
  106. package/patterns/wizard-drawer/wizard-drawer.examples.js +33 -0
  107. package/patterns/wizard-drawer/wizard-drawer.html +72 -0
  108. package/styles/type/roles.css +8 -8
@@ -0,0 +1,50 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" theme="auto">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>App Nav (pattern) — AdiaUI</title>
7
+
8
+ <!-- Token base -->
9
+ <link rel="stylesheet" href="../../styles/resets.css">
10
+ <link rel="stylesheet" href="../../styles/tokens.css">
11
+
12
+ <!-- Component CSS — primitives this pattern composes -->
13
+ <link rel="stylesheet" href="../../components/code/code.css">
14
+ <link rel="stylesheet" href="../../components/nav/nav.css">
15
+ <link rel="stylesheet" href="../../components/nav-group/nav-group.css">
16
+ <link rel="stylesheet" href="../../components/nav-item/nav-item.css">
17
+ <link rel="stylesheet" href="../../components/tag/tag.css">
18
+
19
+ <!-- Component JS -->
20
+ <script type="module" src="../../components/code/code.js"></script>
21
+ <script type="module" src="../../components/nav/nav.js"></script>
22
+ <script type="module" src="../../components/nav-group/nav-group.js"></script>
23
+ <script type="module" src="../../components/nav-item/nav-item.js"></script>
24
+ <script type="module" src="../../components/tag/tag.js"></script>
25
+
26
+ <style>
27
+ :where(html, body) { margin: 0; min-height: 100vh; background: var(--md-sys-color-neutral-background); color: var(--md-sys-color-neutral-on-surface); font-family: var(--a-font-family); }
28
+ main { max-width: 960px; margin-inline: auto; padding: var(--a-space-6) var(--a-space-5); }
29
+ </style>
30
+ </head>
31
+ <body>
32
+
33
+ <main id="demo-root">
34
+ <p>Loading examples…</p>
35
+ </main>
36
+
37
+ <script type="module">
38
+ const root = document.getElementById('demo-root');
39
+ try {
40
+ const res = await fetch('./app-nav.examples.html');
41
+ if (!res.ok) throw new Error(`fetch failed (${res.status})`);
42
+ root.innerHTML = await res.text();
43
+ } catch (err) {
44
+ root.innerHTML = `<p style="color:var(--a-danger-strong);">Failed to load app-nav.examples.html — ${err.message}</p>`;
45
+ console.error('[app-nav.html]', err);
46
+ }
47
+ </script>
48
+
49
+ </body>
50
+ </html>
@@ -0,0 +1,131 @@
1
+ <header>
2
+ <div>
3
+ <h1>Approvals</h1>
4
+ <div data-actions>
5
+ <tag-ui size="sm">card-ui</tag-ui> <tag-ui size="sm">list-ui</tag-ui> <tag-ui size="sm">button-ui</tag-ui> <tag-ui size="sm">tag-ui</tag-ui> <tag-ui size="sm">avatar-ui</tag-ui>
6
+ </div>
7
+ </div>
8
+ <p>The queue + detail surface for items waiting on a human to approve, reject, or request changes — expense reports, PRs, access requests, content reviews. Always asynchronous; the approver sees a list and acts on each item independently.</p>
9
+ </header>
10
+
11
+ <section data-section data-property="when-to-use">
12
+ <h2 variant="section">When to use</h2>
13
+ <p data-note>
14
+ When work has to pause for a human gate. Distinct from confirms
15
+ (12e, blocking and sync) — approvals are out-of-band, batched,
16
+ and may carry a reviewer queue that the requester can see status
17
+ of but not skip.
18
+ </p>
19
+ </section>
20
+
21
+ <section data-section data-property="queue">
22
+ <h2 variant="section">Approval queue</h2>
23
+ <p data-note>List of pending items, most-urgent first. Each row carries the requester avatar + title + amount/scope + age + a pair of approve/reject buttons. Bulk select for batch approve.</p>
24
+ <div data-artifact-container>
25
+ <div data-artifact-item data-artifact-label="approval queue">
26
+ <card-ui data-chunk="approvals-queue">
27
+ <header>
28
+ <span slot="heading" variant="section">Pending your approval</span>
29
+ <text-ui slot="description" color="subtle">5 items · 2 high priority</text-ui>
30
+ <button-ui slot="action" text="Approve all" variant="ghost" size="sm" icon-leading="check"></button-ui>
31
+ </header>
32
+ <section bleed>
33
+ <!-- Canonical list-item rows: text + slot="description" + slot="icon" +
34
+ slot="action". The action slot promotes the row to a 3-column grid
35
+ (icon | content | action) and right-aligns the action cluster, so
36
+ Reject/Approve land in a consistent trailing column across every
37
+ row regardless of the High badge. -->
38
+ <list-ui divider contained>
39
+ <list-item-ui text="Q2 onboarding budget — $48,000">
40
+ <avatar-ui slot="icon" size="sm" text="Jordan Lee"></avatar-ui>
41
+ <span slot="description">Jordan Lee · Engineering · 2 hours ago</span>
42
+ <row-ui slot="action" gap="2" align="center">
43
+ <tag-ui size="sm" variant="warning">High</tag-ui>
44
+ <button-ui text="Reject" variant="ghost" size="sm"></button-ui>
45
+ <button-ui text="Approve" variant="primary" size="sm" icon-leading="check"></button-ui>
46
+ </row-ui>
47
+ </list-item-ui>
48
+ <list-item-ui text="Vendor contract — Adobe Creative Cloud">
49
+ <avatar-ui slot="icon" size="sm" text="Avery Chen"></avatar-ui>
50
+ <span slot="description">Avery Chen · Design · 5 hours ago</span>
51
+ <row-ui slot="action" gap="2" align="center">
52
+ <button-ui text="Reject" variant="ghost" size="sm"></button-ui>
53
+ <button-ui text="Approve" variant="primary" size="sm" icon-leading="check"></button-ui>
54
+ </row-ui>
55
+ </list-item-ui>
56
+ <list-item-ui text="Access request — production database">
57
+ <avatar-ui slot="icon" size="sm" text="Sam Park"></avatar-ui>
58
+ <span slot="description">Sam Park · Data · 1 day ago</span>
59
+ <row-ui slot="action" gap="2" align="center">
60
+ <tag-ui size="sm" variant="warning">High</tag-ui>
61
+ <button-ui text="Reject" variant="ghost" size="sm"></button-ui>
62
+ <button-ui text="Approve" variant="primary" size="sm" icon-leading="check"></button-ui>
63
+ </row-ui>
64
+ </list-item-ui>
65
+ <list-item-ui text="Travel — SF → NYC, May 12–14">
66
+ <avatar-ui slot="icon" size="sm" text="Riley Tan"></avatar-ui>
67
+ <span slot="description">Riley Tan · Sales · 2 days ago</span>
68
+ <row-ui slot="action" gap="2" align="center">
69
+ <button-ui text="Reject" variant="ghost" size="sm"></button-ui>
70
+ <button-ui text="Approve" variant="primary" size="sm" icon-leading="check"></button-ui>
71
+ </row-ui>
72
+ </list-item-ui>
73
+ </list-ui>
74
+ </section>
75
+ </card-ui>
76
+ </div>
77
+ </div>
78
+ </section>
79
+
80
+ <section data-section data-property="detail">
81
+ <h2 variant="section">Approval detail</h2>
82
+ <p data-note>The single-item review surface. Header carries the request title + status + reviewer chain. Body shows the request data (line items / diff / metadata). Footer is a primary Approve, a secondary Reject, and a "Request changes" path for fixable issues.</p>
83
+ <div data-artifact-container>
84
+ <div data-artifact-item data-artifact-label="approval detail">
85
+ <card-ui data-chunk="approvals-detail" style="max-width: 720px;">
86
+ <header>
87
+ <span slot="heading" variant="section">Q2 onboarding budget — $48,000</span>
88
+ <text-ui slot="description" color="subtle">Submitted by Jordan Lee · 2 hours ago</text-ui>
89
+ <tag-ui slot="action" variant="warning">Pending review</tag-ui>
90
+ </header>
91
+ <section>
92
+ <col-ui gap="3">
93
+ <description-list-ui>
94
+ <dt>Requester</dt><dd>Jordan Lee · Engineering Lead</dd>
95
+ <dt>Justification</dt><dd>Onboarding 4 new senior engineers in Q2; covers laptops, software licenses, training, and team setup costs.</dd>
96
+ <dt>Budget breakdown</dt><dd>Hardware $24k · Software $12k · Training $8k · Team setup $4k</dd>
97
+ <dt>Reviewer chain</dt><dd>You → CFO → CEO (auto-approve under $50k after CFO)</dd>
98
+ </description-list-ui>
99
+ </col-ui>
100
+ </section>
101
+ <footer divider>
102
+ <button-ui slot="action" text="Reject" variant="ghost"></button-ui>
103
+ <button-ui slot="action" text="Request changes" variant="ghost"></button-ui>
104
+ <button-ui slot="action" text="Approve" variant="primary" icon-leading="check"></button-ui>
105
+ </footer>
106
+ </card-ui>
107
+ </div>
108
+ </div>
109
+ </section>
110
+
111
+ <section data-section data-property="composition">
112
+ <h2 variant="section">Composition rules</h2>
113
+ <ul>
114
+ <li><strong>Approve is primary, not destructive</strong> — even when approval has financial weight, the reviewer's main job is to approve eligible items. Reject is the secondary action; type-to-confirm is reserved for irreversible terminal states (deny + lock).</li>
115
+ <li><strong>"Request changes" lives in the footer too</strong> — most rejected requests can be fixed and re-submitted. Surfacing the third path keeps the reviewer from forcing a binary outcome on a fixable issue.</li>
116
+ <li><strong>Show the reviewer chain</strong> — "You → CFO → CEO" sets the right expectation: this approval isn't terminal. Without the chain, requesters chase ghosts.</li>
117
+ <li><strong>Bulk approve only for low-stakes items</strong> — "Approve all" should be ghost-styled, not primary. The default is one-by-one because the queue is curated; if it isn't, fix the upstream policy.</li>
118
+ <li><strong>Pending status uses warning, not info</strong> — pending blocks downstream work. The queue should look like work, not a passive list.</li>
119
+ </ul>
120
+ </section>
121
+
122
+ <section data-section data-property="cross-references">
123
+ <h2 variant="section">See also</h2>
124
+ <ul>
125
+ <li><a href="/site/components/list">list-ui</a> · <a href="/site/components/list-item">list-item-ui</a> — queue row anatomy.</li>
126
+ <li><a href="/site/components/description-list">description-list-ui</a> — request metadata layout.</li>
127
+ <li><a href="/site/components/tag">tag-ui</a> — pending / approved / rejected status tags.</li>
128
+ <li><a href="/site/patterns/permissions-sharing">permissions-sharing</a> — adjacent pattern (access requests as a sub-flavor of approvals, see 33f).</li>
129
+ <li><a href="/site/patterns/bulk-action-toolbar">bulk-action-toolbar</a> — shared row-selection affordance for batched approvals.</li>
130
+ </ul>
131
+ </section>
@@ -0,0 +1,58 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" theme="auto">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Approvals (pattern) — AdiaUI</title>
7
+
8
+ <!-- Token base -->
9
+ <link rel="stylesheet" href="../../styles/resets.css">
10
+ <link rel="stylesheet" href="../../styles/tokens.css">
11
+
12
+ <!-- Component CSS — primitives this pattern composes -->
13
+ <link rel="stylesheet" href="../../components/avatar/avatar.css">
14
+ <link rel="stylesheet" href="../../components/button/button.css">
15
+ <link rel="stylesheet" href="../../components/card/card.css">
16
+ <link rel="stylesheet" href="../../components/col/col.css">
17
+ <link rel="stylesheet" href="../../components/description-list/description-list.css">
18
+ <link rel="stylesheet" href="../../components/list/list.css">
19
+ <link rel="stylesheet" href="../../components/row/row.css">
20
+ <link rel="stylesheet" href="../../components/tag/tag.css">
21
+ <link rel="stylesheet" href="../../components/text/text.css">
22
+
23
+ <!-- Component JS -->
24
+ <script type="module" src="../../components/avatar/avatar.js"></script>
25
+ <script type="module" src="../../components/button/button.js"></script>
26
+ <script type="module" src="../../components/card/card.js"></script>
27
+ <script type="module" src="../../components/col/col.js"></script>
28
+ <script type="module" src="../../components/description-list/description-list.js"></script>
29
+ <script type="module" src="../../components/list/list.js"></script>
30
+ <script type="module" src="../../components/row/row.js"></script>
31
+ <script type="module" src="../../components/tag/tag.js"></script>
32
+ <script type="module" src="../../components/text/text.js"></script>
33
+
34
+ <style>
35
+ :where(html, body) { margin: 0; min-height: 100vh; background: var(--md-sys-color-neutral-background); color: var(--md-sys-color-neutral-on-surface); font-family: var(--a-font-family); }
36
+ main { max-width: 960px; margin-inline: auto; padding: var(--a-space-6) var(--a-space-5); }
37
+ </style>
38
+ </head>
39
+ <body>
40
+
41
+ <main id="demo-root">
42
+ <p>Loading examples…</p>
43
+ </main>
44
+
45
+ <script type="module">
46
+ const root = document.getElementById('demo-root');
47
+ try {
48
+ const res = await fetch('./approvals.examples.html');
49
+ if (!res.ok) throw new Error(`fetch failed (${res.status})`);
50
+ root.innerHTML = await res.text();
51
+ } catch (err) {
52
+ root.innerHTML = `<p style="color:var(--a-danger-strong);">Failed to load approvals.examples.html — ${err.message}</p>`;
53
+ console.error('[approvals.html]', err);
54
+ }
55
+ </script>
56
+
57
+ </body>
58
+ </html>
@@ -0,0 +1,74 @@
1
+ <header>
2
+ <div>
3
+ <h1>Audit log</h1>
4
+ <div data-actions>
5
+ <tag-ui size="sm">card-ui</tag-ui> <tag-ui size="sm">table-ui</tag-ui> <tag-ui size="sm">tag-ui</tag-ui> <tag-ui size="sm">select-ui</tag-ui> <tag-ui size="sm">input-ui</tag-ui>
6
+ </div>
7
+ </div>
8
+ <p>Timestamped actor / action / resource rows for compliance, security review, and operational tracing. table-ui carries the rows; tag-ui carries the action verb's severity; the card header carries filters. Distinct from <a href="/site/components/timeline">timeline-ui</a> (single-actor chronological events) and from <a href="/site/components/feed">feed-ui</a> (notifications / posts surface).</p>
9
+ </header>
10
+
11
+ <section data-section data-property="when-to-use">
12
+ <h2 variant="section">When to use</h2>
13
+ <p data-note>Multi-actor history needing scan + filter + occasional drill-down. Audit logs are compliance evidence: every row is a who / when / what / where on a single resource class. The table-ui format scales to thousands of rows + cursor pagination; if the use case is single-actor narrative or fewer than ~20 events, prefer timeline-ui.</p>
14
+ </section>
15
+
16
+ <section data-section data-property="basic">
17
+ <h2 variant="section">Basic — admin actions on workspace</h2>
18
+ <p data-note>Standard layout: title + filters in the header, table in a bleed section, footer carrying status + pagination. Action column uses a tag-ui colored by verb severity (success/warning/danger). Resource column uses code-ui for monospace IDs.</p>
19
+ <div data-artifact-container>
20
+ <div data-artifact-item data-artifact-label="audit log — admin">
21
+ <card-ui data-chunk="audit-log-basic">
22
+ <header>
23
+ <span slot="heading" variant="section">Audit log</span>
24
+ <text-ui slot="description" color="subtle">Last 90 days · admin actions only</text-ui>
25
+ <row-ui slot="action" gap="2" align="center">
26
+ <select-ui name="actor" style="min-width: 8rem;">
27
+ <option value="">All actors</option>
28
+ <option value="kim">kim@example.com</option>
29
+ <option value="alex">alex@example.com</option>
30
+ <option value="bot">automation-bot</option>
31
+ </select-ui>
32
+ <select-ui name="verb" style="min-width: 8rem;">
33
+ <option value="">All actions</option>
34
+ <option value="create">create</option>
35
+ <option value="update">update</option>
36
+ <option value="delete">delete</option>
37
+ </select-ui>
38
+ </row-ui>
39
+ </header>
40
+ <section bleed>
41
+ <table-ui id="audit-log-table"></table-ui>
42
+ </section>
43
+ <footer divider>
44
+ <text-ui color="subtle" size="sm">Showing 8 of 247 events</text-ui>
45
+ <row-ui slot="action" gap="2">
46
+ <button-ui text="Export" variant="ghost" size="sm" icon-leading="download-simple"></button-ui>
47
+ <button-ui text="Load older" variant="ghost" size="sm"></button-ui>
48
+ </row-ui>
49
+ </footer>
50
+ </card-ui>
51
+ </div>
52
+ </div>
53
+ </section>
54
+
55
+ <section data-section data-property="composition">
56
+ <h2 variant="section">Composition rules</h2>
57
+ <ul>
58
+ <li><strong>table-ui driven by JS</strong> — never author <code>&lt;tbody&gt;</code>/<code>&lt;tr&gt;</code> in light DOM; the HTML parser foster-parents those out of custom elements. Set <code>[columns]</code> and <code>[data]</code> via the JS API (see audit-log.examples.js).</li>
59
+ <li><strong>Action verb gets a tag-ui</strong> — colored by severity: <code>create</code>/<code>update</code> = primary, <code>delete</code>/<code>revoke</code> = danger, <code>read</code>/<code>export</code> = muted. The color is the audit signal at a glance; never use uncolored text.</li>
60
+ <li><strong>Resource ID in native <code>&lt;code&gt;</code></strong> — monospace makes scanning vertically aligned IDs trivial. <code>&lt;code-ui&gt;</code> is the BLOCK component (CODE label + Copy chrome); for a table cell use the native inline element. Never truncate trailing chars (they're how the user disambiguates similar IDs).</li>
61
+ <li><strong>Time column is relative + absolute</strong> — "2 hours ago" with the absolute timestamp in a tooltip. Use relative-time-ui for the surface; copy the ISO string to clipboard on row click for forensic work.</li>
62
+ <li><strong>IP / user-agent column is hidden by default</strong> — too wide for the scan layout. Surface in a row-detail drawer (drawer-ui) or per-row "Details" affordance, not as a primary column.</li>
63
+ <li><strong>Pagination is cursor-based</strong> — "Load older" appends rather than swaps pages. Audit logs grow append-only; the user is typically scrolling backward in time, not jumping to page 47.</li>
64
+ </ul>
65
+ </section>
66
+
67
+ <section data-section data-property="cross-references">
68
+ <h2 variant="section">Cross-references</h2>
69
+ <ul>
70
+ <li><a href="/site/components/table">table-ui</a> · <a href="/site/components/tag">tag-ui</a> · <a href="/site/components/relative-time">relative-time-ui</a> — primitives composed here.</li>
71
+ <li><a href="/site/components/timeline">timeline-ui</a> — single-actor chronological event log alternative.</li>
72
+ <li><a href="/site/patterns/export-flow">export-flow</a> — the "Export" affordance in the footer wires to this pattern.</li>
73
+ </ul>
74
+ </section>
@@ -0,0 +1,60 @@
1
+ /** Audit-log pattern — populate table-ui via [columns] with col.render functions. */
2
+
3
+ const VERB_TONE = {
4
+ create: 'primary',
5
+ update: 'primary',
6
+ delete: 'danger',
7
+ revoke: 'danger',
8
+ rotate: 'warning',
9
+ export: 'muted',
10
+ read: 'muted',
11
+ login: 'muted',
12
+ };
13
+
14
+ const ROWS = [
15
+ { time: '2 minutes ago', actor: 'kim@example.com', verb: 'delete', resource: 'api_key_sk_live_…A4F2', ip: '10.0.4.21' },
16
+ { time: '14 minutes ago', actor: 'alex@example.com', verb: 'update', resource: 'workspace_settings', ip: '10.0.4.88' },
17
+ { time: '47 minutes ago', actor: 'automation-bot', verb: 'export', resource: 'invoices.2024-q4.csv', ip: '127.0.0.1' },
18
+ { time: '1 hour ago', actor: 'alex@example.com', verb: 'create', resource: 'webhook_evt_92h2k', ip: '10.0.4.88' },
19
+ { time: '2 hours ago', actor: 'kim@example.com', verb: 'rotate', resource: 'api_key_sk_live_…9821', ip: '10.0.4.21' },
20
+ { time: '4 hours ago', actor: 'kim@example.com', verb: 'login', resource: 'session_a4e8f0', ip: '10.0.4.21' },
21
+ { time: 'Yesterday', actor: 'alex@example.com', verb: 'revoke', resource: 'invite_inv_3kf02', ip: '10.0.4.88' },
22
+ { time: '2 days ago', actor: 'automation-bot', verb: 'read', resource: 'audit_log', ip: '127.0.0.1' },
23
+ ];
24
+
25
+ const escape = (s) => String(s ?? '').replace(/[&<>"']/g, (c) => (
26
+ { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]
27
+ ));
28
+
29
+ export default function setup(router) {
30
+ const root = (router?.querySelector?.('#audit-log-table')) || document.querySelector('#audit-log-table');
31
+ if (!root) return;
32
+ root.columns = [
33
+ { key: 'time', label: 'When', align: 'left' },
34
+ {
35
+ key: 'actor', label: 'Actor', align: 'left',
36
+ render: (v) => {
37
+ const initial = (v[0] || '?').toUpperCase();
38
+ return `<span style="display:inline-flex;align-items:center;gap:.5rem;">
39
+ <avatar-ui size="sm" text="${escape(initial)}" color="muted"></avatar-ui>
40
+ <span>${escape(v)}</span>
41
+ </span>`;
42
+ },
43
+ },
44
+ {
45
+ key: 'verb', label: 'Action', align: 'left',
46
+ render: (v) => {
47
+ const tone = VERB_TONE[v] || 'muted';
48
+ return `<tag-ui size="sm" variant="${tone}">${escape(v)}</tag-ui>`;
49
+ },
50
+ },
51
+ {
52
+ key: 'resource', label: 'Resource', align: 'left',
53
+ // Native <code> for inline monospace — code-ui is the BLOCK component
54
+ // (CODE label + Copy header chrome) which is wrong for a table cell.
55
+ render: (v) => `<code>${escape(v)}</code>`,
56
+ },
57
+ { key: 'ip', label: 'IP', align: 'left' },
58
+ ];
59
+ root.data = ROWS;
60
+ }
@@ -0,0 +1,63 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" theme="auto">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Audit log (pattern) — AdiaUI</title>
7
+
8
+ <link rel="stylesheet" href="../../styles/resets.css">
9
+ <link rel="stylesheet" href="../../styles/tokens.css">
10
+
11
+ <link rel="stylesheet" href="../../components/avatar/avatar.css">
12
+ <link rel="stylesheet" href="../../components/button/button.css">
13
+ <link rel="stylesheet" href="../../components/card/card.css">
14
+ <link rel="stylesheet" href="../../components/col/col.css">
15
+ <link rel="stylesheet" href="../../components/code/code.css">
16
+ <link rel="stylesheet" href="../../components/field/field.css">
17
+ <link rel="stylesheet" href="../../components/icon/icon.css">
18
+ <link rel="stylesheet" href="../../components/input/input.css">
19
+ <link rel="stylesheet" href="../../components/row/row.css">
20
+ <link rel="stylesheet" href="../../components/select/select.css">
21
+ <link rel="stylesheet" href="../../components/table/table.css">
22
+ <link rel="stylesheet" href="../../components/tag/tag.css">
23
+ <link rel="stylesheet" href="../../components/text/text.css">
24
+
25
+ <script type="module" src="../../components/avatar/avatar.js"></script>
26
+ <script type="module" src="../../components/button/button.js"></script>
27
+ <script type="module" src="../../components/card/card.js"></script>
28
+ <script type="module" src="../../components/col/col.js"></script>
29
+ <script type="module" src="../../components/code/code.js"></script>
30
+ <script type="module" src="../../components/field/field.js"></script>
31
+ <script type="module" src="../../components/icon/icon.js"></script>
32
+ <script type="module" src="../../components/input/input.js"></script>
33
+ <script type="module" src="../../components/row/row.js"></script>
34
+ <script type="module" src="../../components/select/select.js"></script>
35
+ <script type="module" src="../../components/table/table.js"></script>
36
+ <script type="module" src="../../components/tag/tag.js"></script>
37
+ <script type="module" src="../../components/text/text.js"></script>
38
+
39
+ <style>
40
+ :where(html, body) { margin: 0; min-height: 100vh; background: var(--md-sys-color-neutral-background); color: var(--md-sys-color-neutral-on-surface); font-family: var(--a-font-family); }
41
+ main { max-width: 960px; margin-inline: auto; padding: var(--a-space-6) var(--a-space-5); }
42
+ </style>
43
+ </head>
44
+ <body>
45
+
46
+ <main id="demo-root">
47
+ <p>Loading examples…</p>
48
+ </main>
49
+
50
+ <script type="module">
51
+ const root = document.getElementById('demo-root');
52
+ try {
53
+ const res = await fetch('./audit-log.examples.html');
54
+ if (!res.ok) throw new Error(`fetch failed (${res.status})`);
55
+ root.innerHTML = await res.text();
56
+ } catch (err) {
57
+ root.innerHTML = `<p style="color:var(--a-danger-strong);">Failed to load audit-log.examples.html — ${err.message}</p>`;
58
+ console.error('[audit-log.html]', err);
59
+ }
60
+ </script>
61
+
62
+ </body>
63
+ </html>
@@ -0,0 +1,89 @@
1
+ <header>
2
+ <div>
3
+ <h1>Bulk Action Toolbar</h1>
4
+ <div data-actions>
5
+ <tag-ui size="sm">table-toolbar-ui</tag-ui> <tag-ui size="sm">toolbar-ui</tag-ui> <tag-ui size="sm">table-ui</tag-ui>
6
+ </div>
7
+ </div>
8
+ <p>When a table has row selection, swap the standard <code>&lt;table-toolbar-ui&gt;</code> for a contextual bulk-action bar at the same surface position. Selection count anchors the left, actions flow from the right, destructive action last.</p>
9
+ </header>
10
+
11
+ <section data-section data-property="when-to-use">
12
+ <h2 variant="section">When to use</h2>
13
+ <p data-note>
14
+ Any table where users act on multiple rows at once: archive, delete, export, move.
15
+ Swapping the toolbar (rather than stacking a second bar) keeps the surface compact and
16
+ signals "selection mode" with the strongest possible affordance — the chrome itself changes.
17
+ </p>
18
+ </section>
19
+
20
+ <section data-section data-property="toolbar-swap" data-chunk="bulk-action-toolbar-swap">
21
+ <h2 variant="section">Toolbar swap on selection</h2>
22
+ <p data-note>Tick the checkboxes below — the default <code>&lt;table-toolbar-ui&gt;</code> swaps for the bulk-action bar at the same height. Untick to return.</p>
23
+ <card-ui raw>
24
+ <section bleed>
25
+ <table-toolbar-ui id="pat-bulk-default" for="pat-bulk-table" text="Resources" count="6" no-filter no-sort no-columns></table-toolbar-ui>
26
+
27
+ <toolbar-ui id="pat-bulk-actions" data-bulk-bar role="toolbar" aria-label="Bulk actions" hidden>
28
+ <text-ui strong><span data-bulk-count>0</span> selected</text-ui>
29
+ <span data-spacer style="flex:1 1 auto"></span>
30
+ <button-ui text="Archive" icon="archive" variant="ghost" size="sm" data-bulk-action="archive"></button-ui>
31
+ <button-ui text="Export" icon="download-simple" variant="ghost" size="sm" data-bulk-action="export"></button-ui>
32
+ <button-ui text="Move" icon="folder-simple" variant="ghost" size="sm" data-bulk-action="move"></button-ui>
33
+ <divider-ui vertical></divider-ui>
34
+ <button-ui text="Delete" icon="trash" variant="ghost" size="sm" data-bulk-action="delete"></button-ui>
35
+ </toolbar-ui>
36
+
37
+ <table-ui id="pat-bulk-table" selectable sortable striped></table-ui>
38
+ </section>
39
+ </card-ui>
40
+ </section>
41
+
42
+ <section data-section data-property="anatomy">
43
+ <h2 variant="section">Anatomy</h2>
44
+ <p data-note>Both toolbars sit inside the same <code>&lt;section bleed&gt;</code> so they share one surface. Toggle visibility on the <code>select</code> event from the table.</p>
45
+ <code-ui language="html">&lt;card-ui&gt;
46
+ &lt;section bleed&gt;
47
+ &lt;!-- Default state — modern foundation --&gt;
48
+ &lt;table-toolbar-ui for="resources" text="Resources" count="6"&gt;&lt;/table-toolbar-ui&gt;
49
+
50
+ &lt;!-- Bulk action bar — swaps in when rows are selected --&gt;
51
+ &lt;toolbar-ui data-bulk-bar hidden role="toolbar" aria-label="Bulk actions"&gt;
52
+ &lt;text-ui strong&gt;&lt;span data-bulk-count&gt;0&lt;/span&gt; selected&lt;/text-ui&gt;
53
+ &lt;span data-spacer&gt;&lt;/span&gt;
54
+ &lt;button-ui text="Archive" icon="archive" variant="ghost" size="sm"&gt;&lt;/button-ui&gt;
55
+ &lt;button-ui text="Export" icon="download-simple" variant="ghost" size="sm"&gt;&lt;/button-ui&gt;
56
+ &lt;button-ui text="Move" icon="folder-simple" variant="ghost" size="sm"&gt;&lt;/button-ui&gt;
57
+ &lt;divider-ui vertical&gt;&lt;/divider-ui&gt;
58
+ &lt;button-ui text="Delete" icon="trash" variant="ghost" size="sm"&gt;&lt;/button-ui&gt;
59
+ &lt;/toolbar-ui&gt;
60
+
61
+ &lt;table-ui id="resources" selectable sortable&gt;&lt;/table-ui&gt;
62
+ &lt;/section&gt;
63
+ &lt;/card-ui&gt;</code-ui>
64
+ <code-ui language="javascript">// Wire the swap. table-ui dispatches 'select' with detail.selected (number[]).
65
+ const table = document.getElementById('resources');
66
+ const tbar = document.querySelector('table-toolbar-ui');
67
+ const bulk = document.querySelector('[data-bulk-bar]');
68
+ const countEl = bulk.querySelector('[data-bulk-count]');
69
+
70
+ table.addEventListener('select', (e) =&gt; {
71
+ const n = e.detail.selected?.length ?? 0;
72
+ countEl.textContent = String(n);
73
+ tbar.hidden = n &gt; 0;
74
+ bulk.hidden = n === 0;
75
+ });</code-ui>
76
+ </section>
77
+
78
+ <section data-section data-property="rules">
79
+ <h2 variant="section">Rules</h2>
80
+ <ul>
81
+ <li>Default state uses <code>&lt;table-toolbar-ui&gt;</code> — the modern foundation. It already wires Filter / Sort / Columns / Search to the table via <code>[for]</code>; don't hand-roll those.</li>
82
+ <li>Bulk bar uses <code>&lt;toolbar-ui&gt;</code> with <code>role="toolbar"</code> and <code>aria-label="Bulk actions"</code>. Match its <code>--toolbar-py</code> / <code>--toolbar-px</code> to <code>--table-toolbar-py</code> / <code>--table-toolbar-px</code> so the swap is seamless.</li>
83
+ <li>Both toolbars sit inside the same <code>&lt;section bleed&gt;</code> as the table — one surface, flush alignment with the first table cell.</li>
84
+ <li>Selection count leads the bulk bar; <code>&lt;span data-spacer&gt;</code> pushes actions right.</li>
85
+ <li>Destructive actions go last and are visually separated with a <code>&lt;divider-ui vertical&gt;</code>.</li>
86
+ <li>Keep bulk actions to 3–5; overflow belongs in a <code>&lt;popover-ui&gt;</code> "More" menu trailing the divider.</li>
87
+ <li>Drive the swap from the table's <code>select</code> event (<code>detail.selected: number[]</code>) — toggle <code>hidden</code> on each toolbar; don't unmount.</li>
88
+ </ul>
89
+ </section>
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Bulk action toolbar — populates the demo table and wires the
3
+ * selection-driven toolbar swap.
4
+ *
5
+ * Default state shows <table-toolbar-ui> (Filter/Sort/Columns/Search,
6
+ * wired via [for]). When rows are selected, swap to <toolbar-ui>
7
+ * with the bulk actions; selection count anchors the left.
8
+ */
9
+
10
+ const columns = [
11
+ { key: 'name', label: 'Name', sortable: true, flex: 2 },
12
+ { key: 'owner', label: 'Owner', sortable: true, flex: 1 },
13
+ { key: 'status', label: 'Status', sortable: true, flex: 1, render: (val) => {
14
+ const v = { Active: 'success', Draft: 'muted', Archived: 'warning' }[val] || '';
15
+ return `<badge-ui text="${val}" variant="${v}"></badge-ui>`;
16
+ }},
17
+ { key: 'updated', label: 'Updated', sortable: true, flex: 1 },
18
+ ];
19
+
20
+ const data = [
21
+ { name: 'Q4 roadmap', owner: 'Alex Chen', status: 'Active', updated: '2 hours ago' },
22
+ { name: 'Design review', owner: 'Sarah Miller', status: 'Active', updated: '5 hours ago' },
23
+ { name: 'Brand guidelines', owner: 'James Park', status: 'Draft', updated: 'Yesterday' },
24
+ { name: 'Launch checklist', owner: 'Maria Lopez', status: 'Archived', updated: '3 days ago' },
25
+ { name: 'API v2 spec', owner: 'Alex Chen', status: 'Active', updated: '1 week ago' },
26
+ { name: 'Hiring plan', owner: 'Sarah Miller', status: 'Draft', updated: '2 weeks ago' },
27
+ ];
28
+
29
+ export default function setup(router) {
30
+ const table = router.querySelector('#pat-bulk-table');
31
+ const tbar = router.querySelector('#pat-bulk-default');
32
+ const bulk = router.querySelector('#pat-bulk-actions');
33
+ if (!table || !tbar || !bulk) return;
34
+
35
+ table.columns = columns;
36
+ table.data = data;
37
+
38
+ // Match the bulk-action toolbar height + horizontal alignment to
39
+ // <table-toolbar-ui>'s default variant so the swap is seamless.
40
+ // table-toolbar-ui ships --table-toolbar-py: var(--a-space-2) and
41
+ // --table-toolbar-px: 0 (alignment to flush table cells in a bleed
42
+ // section). Mirror those on the toolbar-ui that swaps in. Set via
43
+ // setProperty rather than an inline <style> block — pattern pages
44
+ // adjacent to harvest pages get the same "JS hydration is fine,
45
+ // styling stored in HTML is not" treatment by default.
46
+ bulk.style.setProperty('--toolbar-py', 'var(--a-space-2)');
47
+ bulk.style.setProperty('--toolbar-px', '0');
48
+ bulk.style.setProperty('--toolbar-gap', 'var(--a-space-2)');
49
+
50
+ const countEl = bulk.querySelector('[data-bulk-count]');
51
+
52
+ table.addEventListener('select', (e) => {
53
+ const n = e.detail?.selected?.length ?? 0;
54
+ if (countEl) countEl.textContent = String(n);
55
+ tbar.hidden = n > 0;
56
+ bulk.hidden = n === 0;
57
+ });
58
+
59
+ // Demo-only: bulk-action buttons return the table to default-toolbar
60
+ // state so the swap is exercisable in both directions. Uses the
61
+ // public clearSelection() method (added 2026-04-29) — used to require
62
+ // re-assigning `data` to trigger the internal #selected.clear() side
63
+ // effect, but the symmetric API now exists.
64
+ bulk.addEventListener('press', (e) => {
65
+ const target = e.target?.closest?.('[data-bulk-action]');
66
+ if (!target) return;
67
+ table.clearSelection();
68
+ });
69
+ }