@adia-ai/web-components 0.6.36 → 0.6.38

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 (159) hide show
  1. package/CHANGELOG.md +48 -1
  2. package/components/accordion/accordion-item.a2ui.json +3 -0
  3. package/components/accordion/accordion-item.yaml +5 -0
  4. package/components/action-list/action-item.a2ui.json +5 -1
  5. package/components/action-list/action-item.yaml +7 -0
  6. package/components/badge/badge.a2ui.json +10 -0
  7. package/components/badge/badge.css +70 -0
  8. package/components/badge/badge.yaml +20 -0
  9. package/components/blockquote/blockquote.a2ui.json +121 -0
  10. package/components/blockquote/blockquote.class.js +68 -0
  11. package/components/blockquote/blockquote.css +46 -0
  12. package/components/blockquote/blockquote.d.ts +31 -0
  13. package/components/blockquote/blockquote.js +17 -0
  14. package/components/blockquote/blockquote.yaml +124 -0
  15. package/components/button/button.css +11 -3
  16. package/components/calendar-picker/calendar-picker.a2ui.json +15 -0
  17. package/components/calendar-picker/calendar-picker.class.js +7 -1
  18. package/components/calendar-picker/calendar-picker.yaml +14 -0
  19. package/components/card/card.a2ui.json +17 -1
  20. package/components/card/card.yaml +24 -1
  21. package/components/color-input/color-input.a2ui.json +2 -2
  22. package/components/color-input/color-input.class.js +9 -2
  23. package/components/color-input/color-input.yaml +2 -2
  24. package/components/combobox/combobox.class.js +4 -0
  25. package/components/context-menu/context-menu.a2ui.json +159 -0
  26. package/components/context-menu/context-menu.class.js +275 -0
  27. package/components/context-menu/context-menu.css +56 -0
  28. package/components/context-menu/context-menu.d.ts +70 -0
  29. package/components/context-menu/context-menu.js +17 -0
  30. package/components/context-menu/context-menu.yaml +136 -0
  31. package/components/date-range-picker/date-range-picker.a2ui.json +15 -0
  32. package/components/date-range-picker/date-range-picker.class.js +2 -0
  33. package/components/date-range-picker/date-range-picker.yaml +14 -0
  34. package/components/datetime-picker/datetime-picker.a2ui.json +15 -0
  35. package/components/datetime-picker/datetime-picker.class.js +3 -1
  36. package/components/datetime-picker/datetime-picker.d.ts +2 -0
  37. package/components/datetime-picker/datetime-picker.yaml +14 -0
  38. package/components/empty-state/empty-state.a2ui.json +9 -0
  39. package/components/empty-state/empty-state.class.js +2 -0
  40. package/components/empty-state/empty-state.yaml +15 -0
  41. package/components/feed/feed-item.a2ui.json +5 -0
  42. package/components/feed/feed-item.yaml +10 -0
  43. package/components/feed/feed.class.js +13 -5
  44. package/components/feed/feed.css +14 -0
  45. package/components/field/field.a2ui.json +6 -0
  46. package/components/field/field.yaml +10 -0
  47. package/components/index.js +11 -0
  48. package/components/inline-edit/inline-edit.a2ui.json +159 -0
  49. package/components/inline-edit/inline-edit.class.js +184 -0
  50. package/components/inline-edit/inline-edit.css +62 -0
  51. package/components/inline-edit/inline-edit.d.ts +52 -0
  52. package/components/inline-edit/inline-edit.js +12 -0
  53. package/components/inline-edit/inline-edit.yaml +125 -0
  54. package/components/integration-card/integration-card.class.js +9 -0
  55. package/components/integration-card/integration-card.test.js +4 -3
  56. package/components/list/list-item.a2ui.json +8 -1
  57. package/components/list/list-item.yaml +12 -0
  58. package/components/list/list.css +36 -6
  59. package/components/mark/mark.a2ui.json +109 -0
  60. package/components/mark/mark.class.js +22 -0
  61. package/components/mark/mark.css +39 -0
  62. package/components/mark/mark.d.ts +27 -0
  63. package/components/mark/mark.js +12 -0
  64. package/components/mark/mark.yaml +87 -0
  65. package/components/modal/modal.a2ui.json +9 -0
  66. package/components/modal/modal.yaml +14 -0
  67. package/components/nav-group/nav-group.a2ui.json +3 -0
  68. package/components/nav-group/nav-group.css +7 -1
  69. package/components/nav-group/nav-group.yaml +5 -0
  70. package/components/nav-item/nav-item.a2ui.json +3 -0
  71. package/components/nav-item/nav-item.yaml +5 -0
  72. package/components/number-format/number-format.a2ui.json +180 -0
  73. package/components/number-format/number-format.class.js +96 -0
  74. package/components/number-format/number-format.css +18 -0
  75. package/components/number-format/number-format.d.ts +68 -0
  76. package/components/number-format/number-format.js +17 -0
  77. package/components/number-format/number-format.yaml +204 -0
  78. package/components/pagination/pagination.a2ui.json +19 -2
  79. package/components/pagination/pagination.class.js +90 -37
  80. package/components/pagination/pagination.css +32 -127
  81. package/components/pagination/pagination.d.ts +8 -2
  82. package/components/pagination/pagination.test.js +195 -0
  83. package/components/pagination/pagination.yaml +22 -1
  84. package/components/password-strength/password-strength.a2ui.json +152 -0
  85. package/components/password-strength/password-strength.class.js +157 -0
  86. package/components/password-strength/password-strength.css +80 -0
  87. package/components/password-strength/password-strength.d.ts +59 -0
  88. package/components/password-strength/password-strength.js +17 -0
  89. package/components/password-strength/password-strength.yaml +153 -0
  90. package/components/popover/popover.css +43 -23
  91. package/components/popover/popover.yaml +8 -4
  92. package/components/qr-code/QR-TEST.svg +4 -0
  93. package/components/qr-code/qr-code.a2ui.json +154 -0
  94. package/components/qr-code/qr-code.class.js +129 -0
  95. package/components/qr-code/qr-code.css +41 -0
  96. package/components/qr-code/qr-code.d.ts +83 -0
  97. package/components/qr-code/qr-code.js +17 -0
  98. package/components/qr-code/qr-code.yaml +203 -0
  99. package/components/qr-code/qr-encoder.js +633 -0
  100. package/components/relative-time/relative-time.a2ui.json +120 -0
  101. package/components/relative-time/relative-time.class.js +136 -0
  102. package/components/relative-time/relative-time.css +22 -0
  103. package/components/relative-time/relative-time.d.ts +51 -0
  104. package/components/relative-time/relative-time.js +17 -0
  105. package/components/relative-time/relative-time.yaml +133 -0
  106. package/components/segmented/segmented.class.js +15 -3
  107. package/components/select/select.a2ui.json +3 -0
  108. package/components/select/select.class.js +4 -0
  109. package/components/select/select.yaml +5 -0
  110. package/components/skip-nav/skip-nav.a2ui.json +92 -0
  111. package/components/skip-nav/skip-nav.class.js +45 -0
  112. package/components/skip-nav/skip-nav.css +54 -0
  113. package/components/skip-nav/skip-nav.d.ts +27 -0
  114. package/components/skip-nav/skip-nav.js +12 -0
  115. package/components/skip-nav/skip-nav.yaml +68 -0
  116. package/components/slider/slider.a2ui.json +22 -1
  117. package/components/slider/slider.class.js +264 -122
  118. package/components/slider/slider.css +82 -2
  119. package/components/slider/slider.d.ts +19 -3
  120. package/components/slider/slider.test.js +55 -0
  121. package/components/slider/slider.yaml +38 -6
  122. package/components/stat/stat.css +18 -14
  123. package/components/stepper/stepper-item.a2ui.json +3 -0
  124. package/components/stepper/stepper-item.yaml +5 -0
  125. package/components/table/table.class.js +29 -6
  126. package/components/table/table.css +31 -4
  127. package/components/table-toolbar/table-toolbar.class.js +3 -1
  128. package/components/tag/tag.a2ui.json +3 -2
  129. package/components/tag/tag.css +35 -11
  130. package/components/tag/tag.d.ts +14 -0
  131. package/components/tag/tag.test.js +35 -11
  132. package/components/tag/tag.yaml +13 -7
  133. package/components/timeline/timeline-item.a2ui.json +8 -1
  134. package/components/timeline/timeline-item.yaml +12 -0
  135. package/components/toast/toast.class.js +12 -4
  136. package/components/toc/toc.a2ui.json +159 -0
  137. package/components/toc/toc.class.js +222 -0
  138. package/components/toc/toc.css +92 -0
  139. package/components/toc/toc.d.ts +61 -0
  140. package/components/toc/toc.js +17 -0
  141. package/components/toc/toc.yaml +180 -0
  142. package/components/toolbar/toolbar.class.js +3 -0
  143. package/components/tree/tree-item.a2ui.json +5 -1
  144. package/components/tree/tree-item.yaml +7 -0
  145. package/components/tree/tree.a2ui.json +3 -0
  146. package/components/tree/tree.yaml +5 -0
  147. package/components/visually-hidden/visually-hidden.a2ui.json +71 -0
  148. package/components/visually-hidden/visually-hidden.class.js +14 -0
  149. package/components/visually-hidden/visually-hidden.css +25 -0
  150. package/components/visually-hidden/visually-hidden.d.ts +26 -0
  151. package/components/visually-hidden/visually-hidden.js +12 -0
  152. package/components/visually-hidden/visually-hidden.yaml +54 -0
  153. package/core/anchor.js +19 -3
  154. package/dist/web-components.min.css +1 -1
  155. package/dist/web-components.min.js +100 -89
  156. package/package.json +1 -1
  157. package/styles/colors/semantics.css +11 -2
  158. package/styles/components.css +11 -0
  159. package/styles/resets.css +10 -0
@@ -0,0 +1,56 @@
1
+ /* Host styles (scoped — host IS a context-menu-ui descendant of itself). */
2
+ @scope (context-menu-ui) {
3
+ :scope {
4
+ /* Element itself does not render — it's just a behavioral wrapper.
5
+ Targets (wrapped child / [for] match) render in flow normally. */
6
+ display: contents;
7
+ }
8
+
9
+ /* Authored items live in light DOM as the source of truth, but render
10
+ ONLY when the surface opens (the surface clones them into the popover).
11
+ Hide them in flow so they don't appear next to the target. */
12
+ :scope > menu-item-ui,
13
+ :scope > menu-divider-ui {
14
+ display: none;
15
+ }
16
+ }
17
+
18
+ /* Surface styles are NOT scoped to context-menu-ui — the surface is
19
+ appended to document.body (escapes the host's `display: contents` so
20
+ fixed-position + top-layer work), so any @scope rule pinned to
21
+ context-menu-ui as the scope root would miss.
22
+
23
+ Chrome MUST visually match menu-ui's surface — same vocabulary, same
24
+ tokens, same animation. menu-ui is the canonical menu surface; this
25
+ is just the right-click trigger surface for the same menu shape. */
26
+ [data-context-menu-surface] {
27
+ margin: 0;
28
+ padding: var(--a-space-1);
29
+ border: 1px solid var(--a-border-subtle);
30
+ border-radius: var(--a-radius-lg);
31
+ background: var(--a-bg-subtle);
32
+ box-shadow: var(--a-shadow-lg);
33
+ min-width: 10rem;
34
+ font-family: inherit;
35
+ font-size: var(--a-ui-size);
36
+ color: var(--a-fg);
37
+ opacity: 1;
38
+ translate: 0 0;
39
+ transition: opacity var(--a-duration-fast) var(--a-easing-out),
40
+ translate var(--a-duration-fast) var(--a-easing-out);
41
+ }
42
+
43
+ [data-context-menu-surface]:popover-open {
44
+ @starting-style {
45
+ opacity: 0;
46
+ translate: 0 -4px;
47
+ }
48
+ }
49
+
50
+ [data-context-menu-surface]:not(:popover-open) {
51
+ display: none !important;
52
+ }
53
+
54
+ @media (prefers-reduced-motion: reduce) {
55
+ [data-context-menu-surface] { transition: none; }
56
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * `<context-menu-ui>` — Right-click activated menu — the OS-native context-menu pattern as a
3
+ web component. Distinct from `menu-ui` (which is button-triggered):
4
+ same item shape (`menu-item-ui` children), different trigger surface
5
+ (`contextmenu` event), and pointer-anchored positioning instead of
6
+ element-anchored. Pattern: WAI-APG Menu.
7
+
8
+ Two binding modes:
9
+ **A. Wrap.** Default-slot child becomes the target:
10
+ `<context-menu-ui><my-table>...</my-table>...items</context-menu-ui>`.
11
+ **B. Selector.** Point at one or more existing elements via [for]:
12
+ `<context-menu-ui for="#my-table">...items</context-menu-ui>`.
13
+
14
+ On `contextmenu` event on a target: `preventDefault()`, position the
15
+ menu at the pointer coords, show via Popover API. Touch long-press
16
+ (configurable via [long-press-ms]) does the same. Shift+F10 / Menu
17
+ key opens at the focused target's center for keyboard users.
18
+
19
+ *
20
+ * @see https://ui-kit.exe.xyz/site/components/context-menu
21
+ *
22
+ * Type declarations generated by scripts/build/dts-codegen.mjs from
23
+ * the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
24
+ * run `npm run build:components`, then `npm run codegen:dts` to
25
+ * regenerate; or hand-author this file fully if rich event types are
26
+ * needed beyond what the yaml `events:` block can express.
27
+ */
28
+
29
+ import { UIElement } from '../../core/element.js';
30
+
31
+ export interface ContextMenuCloseEventDetail {
32
+ /** "select" | "outside" | "escape" */
33
+ reason: string;
34
+ }
35
+
36
+ export type ContextMenuCloseEvent = CustomEvent<ContextMenuCloseEventDetail>;
37
+ export interface ContextMenuOpenEventDetail {
38
+ /** The target element the menu was opened on. */
39
+ target: string;
40
+ /** Pointer x coord (viewport-relative); null for keyboard activation. */
41
+ x: number;
42
+ /** Pointer y coord; null for keyboard activation. */
43
+ y: number;
44
+ }
45
+
46
+ export type ContextMenuOpenEvent = CustomEvent<ContextMenuOpenEventDetail>;
47
+ export interface ContextMenuSelectEventDetail {
48
+ /** Selected item's text. */
49
+ text: string;
50
+ /** Selected item's value. */
51
+ value: string;
52
+ }
53
+
54
+ export type ContextMenuSelectEvent = CustomEvent<ContextMenuSelectEventDetail>;
55
+
56
+ export class UIContextMenu extends UIElement {
57
+ /** CSS selector(s) for target element(s). Empty = use default-slot child. */
58
+ for: string;
59
+ /** Programmatic open state. Set true to open at target center. */
60
+ open: boolean;
61
+
62
+ addEventListener<K extends keyof HTMLElementEventMap>(
63
+ type: K,
64
+ listener: (this: UIContextMenu, ev: HTMLElementEventMap[K]) => unknown,
65
+ options?: boolean | AddEventListenerOptions,
66
+ ): void;
67
+ addEventListener(type: 'context-menu-close', listener: (ev: ContextMenuCloseEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
68
+ addEventListener(type: 'context-menu-open', listener: (ev: ContextMenuOpenEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
69
+ addEventListener(type: 'context-menu-select', listener: (ev: ContextMenuSelectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
70
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `<context-menu-ui>` — auto-registers the tag on import.
3
+ *
4
+ * For non-side-effect class import (test isolation, tag override), use
5
+ * the `class` subpath:
6
+ *
7
+ * import { UIContextMenu } from '@adia-ai/web-components/components/context-menu/class';
8
+ *
9
+ * @see ../../USAGE.md#registration--auto-vs-explicit
10
+ */
11
+
12
+ import { defineIfFree } from '../../core/register.js';
13
+ import { UIContextMenu } from './context-menu.class.js';
14
+
15
+ defineIfFree('context-menu-ui', UIContextMenu);
16
+
17
+ export { UIContextMenu };
@@ -0,0 +1,136 @@
1
+ $schema: ../../../../scripts/schemas/component.yaml.schema.json
2
+ name: UIContextMenu
3
+ tag: context-menu-ui
4
+ status: stable
5
+ component: ContextMenu
6
+ category: container
7
+ version: 1
8
+ description: |
9
+ Right-click activated menu — the OS-native context-menu pattern as a
10
+ web component. Distinct from `menu-ui` (which is button-triggered):
11
+ same item shape (`menu-item-ui` children), different trigger surface
12
+ (`contextmenu` event), and pointer-anchored positioning instead of
13
+ element-anchored. Pattern: WAI-APG Menu.
14
+
15
+ Two binding modes:
16
+ **A. Wrap.** Default-slot child becomes the target:
17
+ `<context-menu-ui><my-table>...</my-table>...items</context-menu-ui>`.
18
+ **B. Selector.** Point at one or more existing elements via [for]:
19
+ `<context-menu-ui for="#my-table">...items</context-menu-ui>`.
20
+
21
+ On `contextmenu` event on a target: `preventDefault()`, position the
22
+ menu at the pointer coords, show via Popover API. Touch long-press
23
+ (configurable via [long-press-ms]) does the same. Shift+F10 / Menu
24
+ key opens at the focused target's center for keyboard users.
25
+ composes:
26
+ - menu-item-ui
27
+ props:
28
+ for:
29
+ description: CSS selector(s) for target element(s). Empty = use default-slot child.
30
+ type: string
31
+ default: ""
32
+ reflect: true
33
+ open:
34
+ description: Programmatic open state. Set true to open at target center.
35
+ type: boolean
36
+ default: false
37
+ reflect: true
38
+ long-press-ms:
39
+ description: Long-press duration (ms) on touch devices to open the menu.
40
+ type: number
41
+ default: 500
42
+ reflect: false
43
+ attribute: long-press-ms
44
+ events:
45
+ context-menu-open:
46
+ description: Fired when the menu opens (right-click / long-press / keyboard).
47
+ detail:
48
+ target:
49
+ type: Element
50
+ description: The target element the menu was opened on.
51
+ x:
52
+ type: number
53
+ description: Pointer x coord (viewport-relative); null for keyboard activation.
54
+ y:
55
+ type: number
56
+ description: Pointer y coord; null for keyboard activation.
57
+ context-menu-close:
58
+ description: Fired when the menu closes (item-select / outside-click / Escape).
59
+ detail:
60
+ reason:
61
+ type: string
62
+ description: '"select" | "outside" | "escape"'
63
+ context-menu-select:
64
+ description: Fired when an item is activated. Same shape as menu-ui's `action` event.
65
+ detail:
66
+ value:
67
+ type: string
68
+ description: Selected item's value.
69
+ text:
70
+ type: string
71
+ description: Selected item's text.
72
+ slots:
73
+ default:
74
+ description: |
75
+ Two-purpose slot: the wrapped target element (mode A — first
76
+ non-menu-item-ui child) AND the menu-item-ui items. Items are
77
+ promoted to the popover surface on open.
78
+ states:
79
+ - name: idle
80
+ description: Default. Menu closed; trigger listeners attached.
81
+ - name: open
82
+ description: Menu visible at pointer position; focus inside.
83
+ attribute: open
84
+ traits: []
85
+ tokens:
86
+ --context-menu-bg:
87
+ description: Menu surface background color.
88
+ default: var(--a-bg-subtle)
89
+ --context-menu-border:
90
+ description: Menu surface border color.
91
+ default: var(--a-border-subtle)
92
+ --context-menu-radius:
93
+ description: Menu surface border radius.
94
+ default: var(--a-radius-md)
95
+ --context-menu-shadow:
96
+ description: Menu surface shadow.
97
+ default: var(--a-shadow-lg)
98
+ a2ui:
99
+ rules:
100
+ - rule: 'Use <context-menu-ui> for right-click menus on a target (table row, file item, canvas object). For button-triggered menus use <menu-ui>; for popover content that is not a menu use <popover-ui> directly.'
101
+ reason: 'Trigger surface boundary.'
102
+ - rule: 'Items are <menu-item-ui> children inside the default slot — same shape as <menu-ui> items.'
103
+ reason: 'Single menu vocabulary.'
104
+ - rule: 'Bind target via wrap (default-slot first non-menu-item-ui child) OR [for] selector. The selector form is useful for whole-table or whole-canvas menus where wrapping isn''t practical.'
105
+ reason: 'Two binding shapes.'
106
+ anti_patterns:
107
+ - wrong: '<context-menu-ui>...just items...</context-menu-ui>'
108
+ why: 'No target binding — the menu never opens.'
109
+ fix: 'Wrap a target: `<context-menu-ui><my-target></my-target>...items</context-menu-ui>` OR point at one: `<context-menu-ui for="#my-target">...items</context-menu-ui>`.'
110
+ examples:
111
+ - name: file-actions
112
+ description: Right-click a file row for Open / Rename / Delete.
113
+ a2ui: |
114
+ [
115
+ { "id": "root", "component": "ContextMenu", "children": ["target", "item-open", "item-rename", "div", "item-delete"] },
116
+ { "id": "target", "component": "Text", "textContent": "Right-click me" },
117
+ { "id": "item-open", "component": "MenuItem", "value": "open", "text": "Open" },
118
+ { "id": "item-rename", "component": "MenuItem", "value": "rename", "text": "Rename" },
119
+ { "id": "div", "component": "MenuDivider" },
120
+ { "id": "item-delete", "component": "MenuItem", "value": "delete", "text": "Delete", "variant": "danger" }
121
+ ]
122
+ keywords:
123
+ - context-menu
124
+ - right-click
125
+ - menu
126
+ - popup-menu
127
+ synonyms:
128
+ right-click:
129
+ - context-menu
130
+ popup-menu:
131
+ - menu
132
+ - context-menu
133
+ related:
134
+ - menu
135
+ - menu-item
136
+ - popover
@@ -75,6 +75,21 @@
75
75
  "type": "string",
76
76
  "default": "Select range"
77
77
  },
78
+ "placement": {
79
+ "description": "Popover placement relative to the trigger. Default `bottom` centers the two-calendar panel under the trigger (ADR-0034 Rule 2 — ~800px panel >> trigger).",
80
+ "type": "string",
81
+ "enum": [
82
+ "top",
83
+ "bottom",
84
+ "left",
85
+ "right",
86
+ "top-start",
87
+ "top-end",
88
+ "bottom-start",
89
+ "bottom-end"
90
+ ],
91
+ "default": "bottom"
92
+ },
78
93
  "readonly": {
79
94
  "description": "Block edits; allow keyboard navigation for screen-reader inspection.",
80
95
  "type": "boolean",
@@ -144,6 +144,8 @@ export class UIDateRangePicker extends UIFormElement {
144
144
  placeholder: { type: String, default: 'Select range', reflect: true },
145
145
  format: { type: String, default: 'short', reflect: true },
146
146
  noPresets: { type: Boolean, default: false, attribute: 'no-presets', reflect: true },
147
+ // Popover placement — yaml documented reflect:true since v1.
148
+ placement: { type: String, default: 'bottom', reflect: true },
147
149
  };
148
150
  }
149
151
 
@@ -91,6 +91,20 @@ props:
91
91
  default: false
92
92
  attribute: no-presets
93
93
  reflect: true
94
+ placement:
95
+ description: Popover placement relative to the trigger. Default `bottom` centers the two-calendar panel under the trigger (ADR-0034 Rule 2 — ~800px panel >> trigger).
96
+ type: string
97
+ default: bottom
98
+ reflect: true
99
+ enum:
100
+ - top
101
+ - bottom
102
+ - left
103
+ - right
104
+ - top-start
105
+ - top-end
106
+ - bottom-start
107
+ - bottom-end
94
108
  events:
95
109
  change:
96
110
  description: Fired when the range commits (both `from` AND `to` selected, OR a preset clicked).
@@ -76,6 +76,21 @@
76
76
  "type": "string",
77
77
  "default": "Select date and time"
78
78
  },
79
+ "placement": {
80
+ "description": "Popover placement relative to the trigger. Default `bottom` centers the calendar+time panel under the trigger (ADR-0034 Rule 2 — ~600px panel >> trigger).",
81
+ "type": "string",
82
+ "enum": [
83
+ "top",
84
+ "bottom",
85
+ "left",
86
+ "right",
87
+ "top-start",
88
+ "top-end",
89
+ "bottom-start",
90
+ "bottom-end"
91
+ ],
92
+ "default": "bottom"
93
+ },
79
94
  "precision": {
80
95
  "description": "Time-pane precision. `minute` (default) emits `HH:mm`; `second` exposes the seconds segment and emits `HH:mm:ss`.",
81
96
  "type": "string",
@@ -136,6 +136,8 @@ export class UIDatetimePicker extends UIFormElement {
136
136
  placeholder: { type: String, default: 'Select date and time', reflect: false },
137
137
  format: { type: String, default: 'short', reflect: true },
138
138
  locale: { type: String, default: '', reflect: false },
139
+ // Popover placement — yaml documented reflect:true since v1.
140
+ placement: { type: String, default: 'bottom', reflect: true },
139
141
  };
140
142
  }
141
143
 
@@ -439,7 +441,7 @@ export class UIDatetimePicker extends UIFormElement {
439
441
  // helper. Without this, the popover renders at viewport (0,0).
440
442
  this.#anchorCleanup?.();
441
443
  this.#anchorCleanup = anchorPopover(this.#triggerRef, this.#popoverRef, {
442
- placement: this.getAttribute('placement') || 'bottom-start',
444
+ placement: this.getAttribute('placement') || 'bottom', // ADR-0034 Rule 2: panel >> trigger
443
445
  gap: 4,
444
446
  });
445
447
  document.addEventListener('pointerdown', this.#onOutside);
@@ -64,6 +64,8 @@ document locale. `h12` forces a 12-hour cycle with an AM/PM
64
64
  open: boolean;
65
65
  /** Text shown in the trigger when the value is empty. */
66
66
  placeholder: string;
67
+ /** Popover placement relative to the trigger. Default `bottom` centers the calendar+time panel under the trigger (ADR-0034 Rule 2 — ~600px panel >> trigger). */
68
+ placement: 'top' | 'bottom' | 'left' | 'right' | 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end';
67
69
  /** Time-pane precision. `minute` (default) emits `HH:mm`; `second` exposes the seconds segment and emits `HH:mm:ss`. */
68
70
  precision: 'minute' | 'second';
69
71
  /** Block edits; allow keyboard navigation for screen-reader inspection. */
@@ -104,6 +104,20 @@ props:
104
104
  description: BCP-47 locale tag used to derive hour-cycle when `hour-cycle` is empty. Falls back to `<html lang>` then to browser default.
105
105
  type: string
106
106
  default: ''
107
+ placement:
108
+ description: Popover placement relative to the trigger. Default `bottom` centers the calendar+time panel under the trigger (ADR-0034 Rule 2 — ~600px panel >> trigger).
109
+ type: string
110
+ default: bottom
111
+ reflect: true
112
+ enum:
113
+ - top
114
+ - bottom
115
+ - left
116
+ - right
117
+ - top-start
118
+ - top-end
119
+ - bottom-start
120
+ - bottom-end
107
121
  events:
108
122
  change:
109
123
  description: Fired when the value commits (date picked + time edited, OR Apply clicked in explicit-commit mode).
@@ -85,8 +85,17 @@
85
85
  "select"
86
86
  ],
87
87
  "slots": {
88
+ "description": {
89
+ "description": "Override slot for the description text. Use for multi-paragraph or richly-marked-up explanatory copy."
90
+ },
88
91
  "action": {
89
92
  "description": "User-provided action element (e.g. button) displayed below the description"
93
+ },
94
+ "heading": {
95
+ "description": "Override slot for the heading text. Use when richer markup than the plain [heading] attribute is needed (line breaks, inline links, bold emphasis)."
96
+ },
97
+ "icon": {
98
+ "description": "Override slot for the [icon] glyph. Use when the attribute-driven Phosphor icon isn't enough (e.g., custom SVG, illustration, or a styled icon-ui with non-default weight). Mutually exclusive with [icon]."
90
99
  }
91
100
  },
92
101
  "states": [
@@ -29,6 +29,8 @@ export class UIEmptyState extends UIElement {
29
29
  icon: { type: String, default: '', reflect: true },
30
30
  heading: { type: String, default: '', reflect: true },
31
31
  description: { type: String, default: '', reflect: true },
32
+ // Semantic variant — yaml documented reflect:true since v1.
33
+ variant: { type: String, default: '', reflect: true },
32
34
  // §223 (v0.5.9): minimal layout — single-line muted (no centered column,
33
35
  // no padding bump, no icon-size lg). Use for inline empty-table-row /
34
36
  // inline placeholder cells where the canvas placeholder is too loud.
@@ -53,6 +53,21 @@ props:
53
53
  reflect: true
54
54
  events: {}
55
55
  slots:
56
+ icon:
57
+ description: >-
58
+ Override slot for the [icon] glyph. Use when the attribute-driven
59
+ Phosphor icon isn't enough (e.g., custom SVG, illustration, or
60
+ a styled icon-ui with non-default weight). Mutually exclusive
61
+ with [icon].
62
+ heading:
63
+ description: >-
64
+ Override slot for the heading text. Use when richer markup than
65
+ the plain [heading] attribute is needed (line breaks, inline
66
+ links, bold emphasis).
67
+ description:
68
+ description: >-
69
+ Override slot for the description text. Use for multi-paragraph
70
+ or richly-marked-up explanatory copy.
56
71
  action:
57
72
  description: User-provided action element (e.g. button) displayed below the description
58
73
  states:
@@ -13,6 +13,11 @@
13
13
  }
14
14
  ],
15
15
  "properties": {
16
+ "action": {
17
+ "description": "Label of an inline action button (the \"deep-link\" / \"undo\" pattern).\nPair with `onAction` callback in `UIFeed.post()` — click invokes the\ncallback then auto-dismisses. Empty string renders no action button.\n",
18
+ "type": "string",
19
+ "default": ""
20
+ },
16
21
  "component": {
17
22
  "const": "FeedItem"
18
23
  },
@@ -42,6 +42,14 @@ props:
42
42
  description: Render an x close button (default true for sticky, false for auto-fade)
43
43
  type: boolean
44
44
  default: false
45
+ action:
46
+ description: |
47
+ Label of an inline action button (the "deep-link" / "undo" pattern).
48
+ Pair with `onAction` callback in `UIFeed.post()` — click invokes the
49
+ callback then auto-dismisses. Empty string renders no action button.
50
+ type: string
51
+ default: ""
52
+ reflect: true
45
53
  events:
46
54
  close:
47
55
  description: Fired after the item finishes its exit animation
@@ -74,3 +82,5 @@ a2ui:
74
82
  reason: 'Imperative ownership.'
75
83
  - rule: 'Different from <alert-ui> (inline persistent) and <toast-ui> (standalone ephemeral); feed-item is feed-scoped.'
76
84
  reason: 'Surface boundary.'
85
+ - rule: 'For "notification deep-link" pattern: post with action + onAction. Click navigates and auto-dismisses (router.push() / location.href / api.markRead()).'
86
+ reason: 'Deep-link pattern lives in the onAction callback, not in markup.'
@@ -296,9 +296,14 @@ export class UIFeed {
296
296
  * @param {string} [opts.position='bottom-right']
297
297
  * @param {boolean} [opts.dismissible] override default (true for sticky, false for auto)
298
298
  * @param {string} [opts.id]
299
- * @param {string} [opts.action] Phase 2 — action button label. When set,
300
- * duration is forced to null (sticky); the
301
- * item gets role=alertdialog + focus trap.
299
+ * @param {string} [opts.action] Phase 2 — action button label. When set
300
+ * without an explicit duration, the item is
301
+ * sticky (role=alertdialog + focus trap).
302
+ * Pass an explicit numeric `duration` to
303
+ * build the "undo" pattern: the action
304
+ * button shows, but the item auto-dismisses
305
+ * after the timeout. Negative-action UX
306
+ * (delete/archive) typically uses 6000-8000.
302
307
  * @param {function} [opts.onAction] Phase 2 — callback invoked when the
303
308
  * action button is pressed. Item dismisses
304
309
  * after the callback returns.
@@ -330,8 +335,11 @@ export class UIFeed {
330
335
  item.heading = heading;
331
336
  item.icon = icon;
332
337
  item.variant = v;
333
- // Action-required forces sticky.
334
- item.duration = action ? 0 : duration;
338
+ // Action-required default is sticky; explicit duration honored so callers
339
+ // can build the "undo" pattern (action + timed auto-dismiss). Default
340
+ // (duration unspecified) preserves the original alertdialog sticky behavior.
341
+ const explicitDuration = 'duration' in opts;
342
+ item.duration = action && !explicitDuration ? 0 : duration;
335
343
  if (dismissible != null) item.dismissible = !!dismissible;
336
344
  if (action) item.action = action;
337
345
  if (visibleCount >= max) item.setAttribute('data-queued', '');
@@ -40,6 +40,10 @@ feed-item-ui[data-closing] {
40
40
  pointer-events: none; /* Items re-enable pointer-events. */
41
41
  width: max-content;
42
42
  max-width: var(--feed-max-width, var(--feed-max-width-default));
43
+ /* Items size to their own content (see feed-item-ui width: max-content);
44
+ align them to the lane's anchored edge so short items hug the side
45
+ rather than left-aligning inside a wider container. */
46
+ align-items: flex-end;
43
47
  }
44
48
  /* Reset native popover defaults. UA stylesheet sets `margin: auto;
45
49
  inset: 0; border: solid; padding: 0.25em` which would center the
@@ -61,11 +65,13 @@ feed-item-ui[data-closing] {
61
65
  }
62
66
  :scope[position="bottom-left"] {
63
67
  bottom: var(--feed-offset, var(--feed-offset-default)); left: var(--feed-offset, var(--feed-offset-default)); right: auto;
68
+ align-items: flex-start;
64
69
  }
65
70
  :scope[position="bottom-center"] {
66
71
  bottom: var(--feed-offset, var(--feed-offset-default));
67
72
  left: 50%; right: auto;
68
73
  transform: translateX(-50%);
74
+ align-items: center;
69
75
  }
70
76
  :scope[position="top-right"] {
71
77
  top: var(--feed-offset, var(--feed-offset-default)); right: var(--feed-offset, var(--feed-offset-default)); bottom: auto;
@@ -74,12 +80,14 @@ feed-item-ui[data-closing] {
74
80
  :scope[position="top-left"] {
75
81
  top: var(--feed-offset, var(--feed-offset-default)); left: var(--feed-offset, var(--feed-offset-default)); right: auto; bottom: auto;
76
82
  flex-direction: column-reverse;
83
+ align-items: flex-start;
77
84
  }
78
85
  :scope[position="top-center"] {
79
86
  top: var(--feed-offset, var(--feed-offset-default)); bottom: auto;
80
87
  left: 50%; right: auto;
81
88
  transform: translateX(-50%);
82
89
  flex-direction: column-reverse;
90
+ align-items: center;
83
91
  }
84
92
  :scope[position="inline"] {
85
93
  position: relative;
@@ -119,6 +127,12 @@ feed-item-ui[data-closing] {
119
127
  display: flex;
120
128
  align-items: center;
121
129
  gap: var(--feed-item-gap);
130
+ /* Size to own content (a one-line "OK" stays narrow), capped at
131
+ max-width (long messages wrap at the bound). The lane container's
132
+ align-items (per [position]) re-aligns short items to the anchored
133
+ edge so they hug the corner instead of left-aligning under a
134
+ wider sibling. */
135
+ width: max-content;
122
136
  max-width: var(--feed-item-max-width);
123
137
  padding: var(--feed-item-py) var(--feed-item-px);
124
138
  background: var(--feed-item-bg);
@@ -103,6 +103,12 @@
103
103
  "action": {
104
104
  "description": "Button adjacent to the control for inline actions (clear, reset, help popover)."
105
105
  },
106
+ "error": {
107
+ "description": "Override slot for error markup richer than the plain [error] attribute string. Rendered below the control with danger-text styling. Mutually exclusive with [error]."
108
+ },
109
+ "hint": {
110
+ "description": "Override slot for hint markup richer than the plain [hint] attribute string (inline links, code spans, abbreviations). Rendered below the control at body-subtle typography. Mutually exclusive with [hint]."
111
+ },
106
112
  "trailing": {
107
113
  "description": "Secondary text or badge aligned with the label in the stacked layout (right-aligned) or between label and control in the inline layout."
108
114
  }
@@ -89,6 +89,16 @@ slots:
89
89
  description: >-
90
90
  Button adjacent to the control for inline actions (clear,
91
91
  reset, help popover).
92
+ hint:
93
+ description: >-
94
+ Override slot for hint markup richer than the plain [hint] attribute
95
+ string (inline links, code spans, abbreviations). Rendered below
96
+ the control at body-subtle typography. Mutually exclusive with [hint].
97
+ error:
98
+ description: >-
99
+ Override slot for error markup richer than the plain [error] attribute
100
+ string. Rendered below the control with danger-text styling. Mutually
101
+ exclusive with [error].
92
102
  states:
93
103
  - name: idle
94
104
  description: Default, ready for interaction.
@@ -79,11 +79,22 @@ export { UIChartLegend } from './chart-legend/chart-legend.js';
79
79
  export { UIPopover } from './popover/popover.js';
80
80
  export { UIAccordion, UIAccordionItem } from './accordion/accordion.js';
81
81
  export { UIDivider } from './divider/divider.js';
82
+ export { UIBlockquote } from './blockquote/blockquote.js';
83
+ export { UIRelativeTime } from './relative-time/relative-time.js';
84
+ export { UINumberFormat } from './number-format/number-format.js';
85
+ export { UIPasswordStrength } from './password-strength/password-strength.js';
86
+ export { UITableOfContents } from './toc/toc.js';
87
+ export { UIQRCode } from './qr-code/qr-code.js';
82
88
  export { UIPagination } from './pagination/pagination.js';
83
89
  export { UICode } from './code/code.js';
84
90
  export { UIList, UIListItem } from './list/list.js';
85
91
  export { UIListWindow } from './list-window/list-window.js';
86
92
  export { UIMenu, UIMenuItem, UIMenuDivider } from './menu/menu.js';
93
+ export { UIContextMenu } from './context-menu/context-menu.js';
94
+ export { UIVisuallyHidden } from './visually-hidden/visually-hidden.js';
95
+ export { UISkipNav } from './skip-nav/skip-nav.js';
96
+ export { UIMark } from './mark/mark.js';
97
+ export { UIInlineEdit } from './inline-edit/inline-edit.js';
87
98
  export { UIToolbar, UIToolbarGroup } from './toolbar/toolbar.js';
88
99
  export { UINav } from './nav/nav.js';
89
100
  export { UINavGroup } from './nav-group/nav-group.js';