@redvars/peacock 3.5.1 → 3.6.0

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 (198) hide show
  1. package/dist/{BaseButton-DuASuVth.js → BaseButton-BNFAYn-S.js} +2 -2
  2. package/dist/{BaseButton-DuASuVth.js.map → BaseButton-BNFAYn-S.js.map} +1 -1
  3. package/dist/BaseInput-14YmcfK7.js +27 -0
  4. package/dist/BaseInput-14YmcfK7.js.map +1 -0
  5. package/dist/banner.js +2 -3
  6. package/dist/banner.js.map +1 -1
  7. package/dist/{button-DouvOfEU.js → button-colors-Ccys3hvS.js} +5 -294
  8. package/dist/button-colors-Ccys3hvS.js.map +1 -0
  9. package/dist/button-group.js +226 -6
  10. package/dist/button-group.js.map +1 -1
  11. package/dist/button.js +294 -8
  12. package/dist/button.js.map +1 -1
  13. package/dist/calendar-column-view.js +634 -0
  14. package/dist/calendar-column-view.js.map +1 -0
  15. package/dist/calendar-event-BrQ_SEKD.js +199 -0
  16. package/dist/calendar-event-BrQ_SEKD.js.map +1 -0
  17. package/dist/calendar-month-view.js +376 -0
  18. package/dist/calendar-month-view.js.map +1 -0
  19. package/dist/calendar.js +339 -0
  20. package/dist/calendar.js.map +1 -0
  21. package/dist/canvas.js +361 -0
  22. package/dist/canvas.js.map +1 -0
  23. package/dist/cb-compound-expression.js +125 -0
  24. package/dist/cb-compound-expression.js.map +1 -0
  25. package/dist/cb-divider.js +150 -0
  26. package/dist/cb-divider.js.map +1 -0
  27. package/dist/cb-expression.js +75 -0
  28. package/dist/cb-expression.js.map +1 -0
  29. package/dist/cb-predicate.js +137 -0
  30. package/dist/cb-predicate.js.map +1 -0
  31. package/dist/code-editor.js +2 -1
  32. package/dist/code-editor.js.map +1 -1
  33. package/dist/condition-builder.js +58 -0
  34. package/dist/condition-builder.js.map +1 -0
  35. package/dist/custom-elements-jsdocs.json +7976 -4294
  36. package/dist/custom-elements.json +14358 -7589
  37. package/dist/dropdown-button.js +216 -0
  38. package/dist/dropdown-button.js.map +1 -0
  39. package/dist/event-manager-D-QCmUgR.js +113 -0
  40. package/dist/event-manager-D-QCmUgR.js.map +1 -0
  41. package/dist/fab.js +1 -1
  42. package/dist/flow-designer-dZnLJOQT.js +1656 -0
  43. package/dist/flow-designer-dZnLJOQT.js.map +1 -0
  44. package/dist/flow-designer-node-XMe-jlKg.js +548 -0
  45. package/dist/flow-designer-node-XMe-jlKg.js.map +1 -0
  46. package/dist/flow-designer-node.js +4 -0
  47. package/dist/flow-designer-node.js.map +1 -0
  48. package/dist/flow-designer.js +16 -0
  49. package/dist/flow-designer.js.map +1 -0
  50. package/dist/html-editor.js +358 -0
  51. package/dist/html-editor.js.map +1 -0
  52. package/dist/icon-button-CK1ZuE-2.js +247 -0
  53. package/dist/icon-button-CK1ZuE-2.js.map +1 -0
  54. package/dist/index.js +29 -6
  55. package/dist/index.js.map +1 -1
  56. package/dist/{is-dark-mode-DicqGkCJ.js → is-dark-mode-DOcaw4Yq.js} +2 -27
  57. package/dist/is-dark-mode-DOcaw4Yq.js.map +1 -0
  58. package/dist/modal.js +418 -0
  59. package/dist/modal.js.map +1 -0
  60. package/dist/{navigation-rail-Lxetd5-Z.js → navigation-rail-DyO0oAZU.js} +306 -2197
  61. package/dist/navigation-rail-DyO0oAZU.js.map +1 -0
  62. package/dist/notification-manager.js +268 -0
  63. package/dist/notification-manager.js.map +1 -0
  64. package/dist/peacock-loader.js +84 -8
  65. package/dist/peacock-loader.js.map +1 -1
  66. package/dist/popover-NC7b1lTq.js +1971 -0
  67. package/dist/popover-NC7b1lTq.js.map +1 -0
  68. package/dist/popover-content.js +125 -0
  69. package/dist/popover-content.js.map +1 -0
  70. package/dist/popover.js +4 -0
  71. package/dist/popover.js.map +1 -0
  72. package/dist/split-button.js +388 -0
  73. package/dist/split-button.js.map +1 -0
  74. package/dist/src/__controllers/floating-controller.d.ts +35 -0
  75. package/dist/src/calendar/base-event.d.ts +10 -0
  76. package/dist/src/calendar/calendar-column-view.d.ts +41 -0
  77. package/dist/src/calendar/calendar-event.d.ts +7 -0
  78. package/dist/src/calendar/calendar-month-view.d.ts +31 -0
  79. package/dist/src/calendar/calendar.d.ts +65 -0
  80. package/dist/src/calendar/event-manager.d.ts +17 -0
  81. package/dist/src/calendar/index.d.ts +4 -0
  82. package/dist/src/calendar/types.d.ts +13 -0
  83. package/dist/src/calendar/utils.d.ts +31 -0
  84. package/dist/src/canvas/canvas.d.ts +92 -0
  85. package/dist/src/canvas/index.d.ts +2 -0
  86. package/dist/src/condition-builder/cb-compound-expression.d.ts +31 -0
  87. package/dist/src/condition-builder/cb-divider.d.ts +26 -0
  88. package/dist/src/condition-builder/cb-expression.d.ts +31 -0
  89. package/dist/src/condition-builder/cb-predicate.d.ts +30 -0
  90. package/dist/src/condition-builder/condition-builder.d.ts +27 -0
  91. package/dist/src/condition-builder/index.d.ts +5 -0
  92. package/dist/src/dropdown-button/dropdown-button.d.ts +68 -0
  93. package/dist/src/dropdown-button/index.d.ts +1 -0
  94. package/dist/src/flow-designer/commands.d.ts +66 -0
  95. package/dist/src/flow-designer/flow-designer-node.d.ts +46 -0
  96. package/dist/src/flow-designer/flow-designer.d.ts +133 -0
  97. package/dist/src/flow-designer/index.d.ts +7 -0
  98. package/dist/src/flow-designer/layout.d.ts +30 -0
  99. package/dist/src/flow-designer/types.d.ts +142 -0
  100. package/dist/src/flow-designer/validation.d.ts +43 -0
  101. package/dist/src/flow-designer/workflow-utils.d.ts +40 -0
  102. package/dist/src/html-editor/html-editor.d.ts +56 -0
  103. package/dist/src/html-editor/index.d.ts +2 -0
  104. package/dist/src/index.d.ts +13 -0
  105. package/dist/src/menu/menu/menu.d.ts +5 -7
  106. package/dist/src/menu/menu-item/menu-item.d.ts +14 -13
  107. package/dist/src/modal/index.d.ts +1 -0
  108. package/dist/src/modal/modal.d.ts +63 -0
  109. package/dist/src/notification-manager/index.d.ts +1 -0
  110. package/dist/src/notification-manager/notification-manager.d.ts +44 -0
  111. package/dist/src/popover/index.d.ts +2 -0
  112. package/dist/src/popover/popover-content.d.ts +29 -0
  113. package/dist/src/popover/popover.d.ts +62 -0
  114. package/dist/src/split-button/index.d.ts +1 -0
  115. package/dist/src/split-button/split-button.d.ts +72 -0
  116. package/dist/src/tooltip/tooltip.d.ts +2 -15
  117. package/dist/test/flow-designer.test.d.ts +1 -0
  118. package/dist/tsconfig.tsbuildinfo +1 -1
  119. package/package.json +4 -2
  120. package/readme.md +2 -2
  121. package/src/__controllers/floating-controller.ts +237 -0
  122. package/src/banner/banner.scss +2 -3
  123. package/src/button/button/button.ts +1 -0
  124. package/src/calendar/base-event.ts +49 -0
  125. package/src/calendar/calendar-column-view.scss +326 -0
  126. package/src/calendar/calendar-column-view.ts +392 -0
  127. package/src/calendar/calendar-event.ts +20 -0
  128. package/src/calendar/calendar-month-view.scss +192 -0
  129. package/src/calendar/calendar-month-view.ts +244 -0
  130. package/src/calendar/calendar.scss +71 -0
  131. package/src/calendar/calendar.ts +298 -0
  132. package/src/calendar/event-manager.ts +117 -0
  133. package/src/calendar/index.ts +4 -0
  134. package/src/calendar/types.ts +14 -0
  135. package/src/calendar/utils.ts +180 -0
  136. package/src/canvas/canvas.scss +60 -0
  137. package/src/canvas/canvas.ts +391 -0
  138. package/src/canvas/index.ts +2 -0
  139. package/src/condition-builder/cb-compound-expression.scss +37 -0
  140. package/src/condition-builder/cb-compound-expression.ts +80 -0
  141. package/src/condition-builder/cb-divider.scss +93 -0
  142. package/src/condition-builder/cb-divider.ts +56 -0
  143. package/src/condition-builder/cb-expression.scss +14 -0
  144. package/src/condition-builder/cb-expression.ts +49 -0
  145. package/src/condition-builder/cb-predicate.scss +35 -0
  146. package/src/condition-builder/cb-predicate.ts +102 -0
  147. package/src/condition-builder/condition-builder.scss +13 -0
  148. package/src/condition-builder/condition-builder.ts +38 -0
  149. package/src/condition-builder/index.ts +5 -0
  150. package/src/dropdown-button/demo/index.html +110 -0
  151. package/src/dropdown-button/dropdown-button.scss +22 -0
  152. package/src/dropdown-button/dropdown-button.ts +206 -0
  153. package/src/dropdown-button/index.ts +1 -0
  154. package/src/flow-designer/DEMO.md +239 -0
  155. package/src/flow-designer/commands.ts +278 -0
  156. package/src/flow-designer/flow-designer-node.ts +172 -0
  157. package/src/flow-designer/flow-designer.scss +457 -0
  158. package/src/flow-designer/flow-designer.ts +611 -0
  159. package/src/flow-designer/index.ts +41 -0
  160. package/src/flow-designer/layout.ts +357 -0
  161. package/src/flow-designer/types.ts +166 -0
  162. package/src/flow-designer/validation.ts +284 -0
  163. package/src/flow-designer/workflow-utils.ts +282 -0
  164. package/src/html-editor/html-editor.scss +146 -0
  165. package/src/html-editor/html-editor.ts +276 -0
  166. package/src/html-editor/index.ts +3 -0
  167. package/src/index.ts +25 -0
  168. package/src/menu/menu/menu.scss +2 -2
  169. package/src/menu/menu/menu.ts +91 -101
  170. package/src/menu/menu-item/menu-item.scss +4 -0
  171. package/src/menu/menu-item/menu-item.ts +82 -78
  172. package/src/modal/index.ts +1 -0
  173. package/src/modal/modal.scss +206 -0
  174. package/src/modal/modal.ts +201 -0
  175. package/src/notification-manager/index.ts +1 -0
  176. package/src/notification-manager/notification-manager.scss +113 -0
  177. package/src/notification-manager/notification-manager.ts +199 -0
  178. package/src/peacock-loader.ts +71 -0
  179. package/src/popover/index.ts +2 -0
  180. package/src/popover/popover-content.scss +69 -0
  181. package/src/popover/popover-content.ts +51 -0
  182. package/src/popover/popover.scss +7 -0
  183. package/src/popover/popover.ts +170 -0
  184. package/src/split-button/index.ts +1 -0
  185. package/src/split-button/split-button-colors.scss +56 -0
  186. package/src/split-button/split-button-sizes.scss +28 -0
  187. package/src/split-button/split-button.scss +79 -0
  188. package/src/split-button/split-button.ts +236 -0
  189. package/src/table/table.ts +2 -2
  190. package/src/tooltip/tooltip.scss +4 -3
  191. package/src/tooltip/tooltip.ts +46 -104
  192. package/dist/button-DouvOfEU.js.map +0 -1
  193. package/dist/button-group-CEdMwvJJ.js +0 -464
  194. package/dist/button-group-CEdMwvJJ.js.map +0 -1
  195. package/dist/is-dark-mode-DicqGkCJ.js.map +0 -1
  196. package/dist/navigation-rail-Lxetd5-Z.js.map +0 -1
  197. package/dist/src/menu/menu/MenuSurfaceController.d.ts +0 -18
  198. package/src/menu/menu/MenuSurfaceController.ts +0 -61
@@ -0,0 +1,276 @@
1
+ import { html, nothing } from 'lit';
2
+ import { property, query, state } from 'lit/decorators.js';
3
+ import { classMap } from 'lit/directives/class-map.js';
4
+
5
+ import IndividualComponent from '@/IndividualComponent.js';
6
+ import BaseInput from '../input/BaseInput.js';
7
+ import { redispatchEvent } from '../__utils/dispatch-event-utils.js';
8
+
9
+ import styles from './html-editor.scss';
10
+
11
+ /**
12
+ * @label HTML Editor
13
+ * @tag wc-html-editor
14
+ * @rawTag html-editor
15
+ *
16
+ * @summary A WYSIWYG HTML editor component with a Material 3 styled toolbar.
17
+ * @overview
18
+ * <p>The HTML Editor provides a rich-text editing experience using the browser's built-in
19
+ * <code>contenteditable</code> API. It wraps the editable area in a Material 3 styled
20
+ * <code>wc-field</code> and exposes a toolbar with common formatting actions.</p>
21
+ *
22
+ * <p>Get and set the HTML content via the <code>value</code> property. The component
23
+ * dispatches a <code>change</code> event whenever the content is modified.</p>
24
+ *
25
+ * @cssprop --html-editor-min-height - Minimum height of the editable area. Defaults to 8rem.
26
+ * @cssprop --html-editor-toolbar-background - Background color of the toolbar.
27
+ * @cssprop --html-editor-toolbar-border-color - Border color between toolbar and editing area.
28
+ *
29
+ * @fires {Event} change - Fired whenever the editable content changes.
30
+ *
31
+ * @example
32
+ * ```html
33
+ * <wc-html-editor label="Description" value="<p>Hello <strong>world</strong></p>"></wc-html-editor>
34
+ * ```
35
+ * @tags input editor
36
+ */
37
+ @IndividualComponent
38
+ export class HtmlEditor extends BaseInput {
39
+ static styles = [styles];
40
+
41
+ /** Current HTML value of the editor. */
42
+ @property({ type: String })
43
+ value = '';
44
+
45
+ /** Label displayed above the editor. */
46
+ @property({ type: String })
47
+ label = '';
48
+
49
+ /** Placeholder text shown when the editor is empty. */
50
+ @property({ type: String })
51
+ placeholder = 'Enter text\u2026';
52
+
53
+ /** Visual style of the wrapping field. */
54
+ @property({ type: String })
55
+ variant: 'filled' | 'outlined' | 'default' = 'default';
56
+
57
+ /** Helper text displayed below the editor. */
58
+ @property({ type: String, attribute: 'helper-text' })
59
+ helperText = '';
60
+
61
+ /** Whether to show an error state. */
62
+ @property({ type: Boolean })
63
+ error = false;
64
+
65
+ /** Error message text. */
66
+ @property({ type: String, attribute: 'error-text' })
67
+ errorText = '';
68
+
69
+ @state() private _focused = false;
70
+
71
+ @query('.html-editor-content')
72
+ private _editorEl!: HTMLDivElement;
73
+
74
+ // ─── Lifecycle ─────────────────────────────────────────────────────────────
75
+
76
+ protected firstUpdated() {
77
+ if (this.value && this._editorEl) {
78
+ this._editorEl.innerHTML = this.value;
79
+ }
80
+ }
81
+
82
+ protected updated(changed: Map<string, unknown>) {
83
+ if (changed.has('value') && this._editorEl) {
84
+ if (this._editorEl.innerHTML !== this.value) {
85
+ this._editorEl.innerHTML = this.value ?? '';
86
+ }
87
+ }
88
+ if (changed.has('disabled') || changed.has('readonly')) {
89
+ if (this._editorEl) {
90
+ this._editorEl.contentEditable =
91
+ this.disabled || this.readonly ? 'false' : 'true';
92
+ }
93
+ }
94
+ }
95
+
96
+ // ─── Private helpers ───────────────────────────────────────────────────────
97
+
98
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
99
+ private _execCmd(command: string, value?: string) {
100
+ if (this.disabled || this.readonly) return;
101
+ this._editorEl.focus();
102
+ // execCommand is deprecated but remains broadly supported for rich-text editing
103
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
104
+ (document as any).execCommand(command, false, value ?? null);
105
+ }
106
+
107
+ private _handleInput() {
108
+ this.value = this._editorEl.innerHTML;
109
+ redispatchEvent(
110
+ this,
111
+ new Event('change', { bubbles: true, composed: true }),
112
+ );
113
+ }
114
+
115
+ private _handleFocus() {
116
+ this._focused = true;
117
+ }
118
+
119
+ private _handleBlur() {
120
+ this._focused = false;
121
+ }
122
+
123
+ private _insertLink() {
124
+ // eslint-disable-next-line no-alert
125
+ const url = window.prompt('Enter URL:', 'https://');
126
+ if (url) this._execCmd('createLink', url);
127
+ }
128
+
129
+ // ─── Toolbar button ────────────────────────────────────────────────────────
130
+
131
+ private _toolbarButton(
132
+ icon: string,
133
+ title: string,
134
+ command: string,
135
+ value?: string,
136
+ ) {
137
+ return html`
138
+ <button
139
+ class="toolbar-btn"
140
+ title=${title}
141
+ aria-label=${title}
142
+ ?disabled=${this.disabled || this.readonly}
143
+ @mousedown=${(e: Event) => e.preventDefault()}
144
+ @click=${(e: Event) => {
145
+ e.preventDefault();
146
+ this._execCmd(command, value);
147
+ }}
148
+ >
149
+ <wc-icon name=${icon} size="sm"></wc-icon>
150
+ </button>
151
+ `;
152
+ }
153
+
154
+ // ─── Toolbar ───────────────────────────────────────────────────────────────
155
+
156
+ private _renderToolbar() {
157
+ return html`
158
+ <div
159
+ class="html-editor-toolbar"
160
+ role="toolbar"
161
+ aria-label="Formatting toolbar"
162
+ >
163
+ ${this._toolbarButton('format_bold', 'Bold', 'bold')}
164
+ ${this._toolbarButton('format_italic', 'Italic', 'italic')}
165
+ ${this._toolbarButton('format_underlined', 'Underline', 'underline')}
166
+ ${this._toolbarButton(
167
+ 'format_strikethrough',
168
+ 'Strikethrough',
169
+ 'strikeThrough',
170
+ )}
171
+
172
+ <span class="toolbar-divider"></span>
173
+
174
+ ${this._toolbarButton('format_align_left', 'Align left', 'justifyLeft')}
175
+ ${this._toolbarButton(
176
+ 'format_align_center',
177
+ 'Align center',
178
+ 'justifyCenter',
179
+ )}
180
+ ${this._toolbarButton(
181
+ 'format_align_right',
182
+ 'Align right',
183
+ 'justifyRight',
184
+ )}
185
+
186
+ <span class="toolbar-divider"></span>
187
+
188
+ ${this._toolbarButton(
189
+ 'format_list_bulleted',
190
+ 'Unordered list',
191
+ 'insertUnorderedList',
192
+ )}
193
+ ${this._toolbarButton(
194
+ 'format_list_numbered',
195
+ 'Ordered list',
196
+ 'insertOrderedList',
197
+ )}
198
+
199
+ <span class="toolbar-divider"></span>
200
+
201
+ ${this._toolbarButton(
202
+ 'format_indent_increase',
203
+ 'Indent',
204
+ 'indent',
205
+ )}
206
+ ${this._toolbarButton('format_indent_decrease', 'Outdent', 'outdent')}
207
+
208
+ <span class="toolbar-divider"></span>
209
+
210
+ <button
211
+ class="toolbar-btn"
212
+ title="Insert link"
213
+ aria-label="Insert link"
214
+ ?disabled=${this.disabled || this.readonly}
215
+ @mousedown=${(e: Event) => e.preventDefault()}
216
+ @click=${() => this._insertLink()}
217
+ >
218
+ <wc-icon name="link" size="sm"></wc-icon>
219
+ </button>
220
+
221
+ <span class="toolbar-divider"></span>
222
+
223
+ ${this._toolbarButton('undo', 'Undo', 'undo')}
224
+ ${this._toolbarButton('redo', 'Redo', 'redo')}
225
+ </div>
226
+ `;
227
+ }
228
+
229
+ // ─── Render ────────────────────────────────────────────────────────────────
230
+
231
+ render() {
232
+ const isEmpty = !this.value || this.value === '<br>';
233
+
234
+ return html`
235
+ <wc-field
236
+ label=${this.label}
237
+ ?required=${this.required}
238
+ ?disabled=${this.disabled}
239
+ ?readonly=${this.readonly}
240
+ ?skeleton=${this.skeleton}
241
+ ?focused=${this._focused}
242
+ ?error=${this.error}
243
+ error-text=${this.errorText}
244
+ helper-text=${this.helperText}
245
+ variant=${this.variant}
246
+ ?populated=${!isEmpty}
247
+ .host=${this}
248
+ class=${classMap({
249
+ 'html-editor-field': true,
250
+ disabled: this.disabled,
251
+ readonly: this.readonly,
252
+ })}
253
+ >
254
+ ${this._renderToolbar()}
255
+
256
+ <div
257
+ class=${classMap({
258
+ 'html-editor-content': true,
259
+ 'is-empty': isEmpty,
260
+ })}
261
+ contenteditable=${this.disabled || this.readonly ? 'false' : 'true'}
262
+ data-placeholder=${this.placeholder}
263
+ @input=${this._handleInput}
264
+ @focus=${this._handleFocus}
265
+ @blur=${this._handleBlur}
266
+ ></div>
267
+
268
+ ${this.disabled
269
+ ? html`<wc-tag class="read-only-tag" color="red">Disabled</wc-tag>`
270
+ : this.readonly
271
+ ? html`<wc-tag class="read-only-tag" color="red">Read Only</wc-tag>`
272
+ : nothing}
273
+ </wc-field>
274
+ `;
275
+ }
276
+ }
@@ -0,0 +1,3 @@
1
+ import { HtmlEditor } from './html-editor.js';
2
+
3
+ export { HtmlEditor };
package/src/index.ts CHANGED
@@ -7,6 +7,8 @@ export { Elevation } from './elevation/index.js';
7
7
  export { Button, ButtonGroup, IconButton } from './button/index.js';
8
8
  export { Fab } from './fab/index.js';
9
9
  export { SegmentedButton, SegmentedButtonGroup } from './segmented-button/index.js';
10
+ export { SplitButton } from './split-button/index.js';
11
+ export { DropdownButton } from './dropdown-button/index.js';
10
12
 
11
13
  export { FocusRing } from './focus-ring/index.js';
12
14
  export { Ripple } from './ripple/index.js';
@@ -32,11 +34,13 @@ export { Container } from './container/index.js';
32
34
  export { NumberCounter } from './number-counter/index.js';
33
35
  export { EmptyState } from './empty-state/index.js';
34
36
  export { Tooltip } from './popover/index.js';
37
+ export { Popover, PopoverContent } from './popover/index.js';
35
38
  export { Breadcrumb, BreadcrumbItem } from './breadcrumb/index.js';
36
39
  export { Menu, MenuItem, SubMenu } from './menu/index.js';
37
40
 
38
41
  export { CodeHighlighter } from './code-highlighter/index.js';
39
42
  export { CodeEditor } from './code-editor/index.js';
43
+ export { HtmlEditor } from './html-editor/index.js';
40
44
  export { Image } from './image/index.js';
41
45
  export { Tab, TabGroup, TabPanel, Tabs } from './tabs/index.js';
42
46
  export { Slider } from './slider/index.js';
@@ -49,8 +53,10 @@ export { SidebarMenu, SidebarMenuItem, SidebarSubMenu } from './sidebar-menu/ind
49
53
  export { Card } from './card/index.js';
50
54
  export { Banner } from './banner/index.js';
51
55
  export { Notification } from './notification/index.js';
56
+ export { NotificationManager } from './notification-manager/index.js';
52
57
  export { Snackbar } from './snackbar/index.js';
53
58
  export { Radio } from './radio/index.js';
59
+ export { Modal } from './modal/index.js';
54
60
  export { BottomSheet } from './bottom-sheet/index.js';
55
61
  export { SideSheet } from './side-sheet/index.js';
56
62
  export { Select } from './select/index.js';
@@ -59,4 +65,23 @@ export { SelectOptionElement } from './select/index.js';
59
65
  export { Search } from './search/index.js';
60
66
  export { Toolbar } from './toolbar/index.js';
61
67
  export { NavigationRail, NavigationRailItem } from './navigation-rail/index.js';
68
+ export { Calendar, CalendarColumnView, CalendarMonthView } from './calendar/index.js';
69
+ export type { CalendarViewType, EventType } from './calendar/index.js';
70
+ export { Canvas } from './canvas/index.js';
71
+ export type { CanvasShape } from './canvas/index.js';
72
+ export { FlowDesigner, FlowDesignerNode } from './flow-designer/index.js';
73
+ export type {
74
+ Workflow,
75
+ WorkflowNode,
76
+ WorkflowCommand,
77
+ PositionedNode,
78
+ ValidationError,
79
+ HistoryEntry,
80
+ EditorState,
81
+ NodeType,
82
+ WorkflowChangeEvent,
83
+ SwimlaneConfig,
84
+ NodeTemplate,
85
+ } from './flow-designer/index.js';
86
+ export { ConditionBuilder, CbPredicate, CbCompoundExpression, CbExpression, CbDivider } from './condition-builder/index.js';
62
87
 
@@ -12,8 +12,8 @@
12
12
  padding-block: var(--spacing-050);
13
13
  transform-origin: top center;
14
14
 
15
- --_menu-enter-duration: var(--duration-medium1, 250ms);
16
- --_menu-exit-duration: var(--duration-short4, 200ms);
15
+ --_menu-enter-duration: var(--duration-medium1);
16
+ --_menu-exit-duration: var(--duration-medium2);
17
17
  --_menu-enter-easing: cubic-bezier(0.05, 0.7, 0.1, 1);
18
18
  --_menu-exit-easing: cubic-bezier(0.3, 0, 0.8, 0.15);
19
19
 
@@ -3,8 +3,8 @@ import { property, query, state } from 'lit/decorators.js';
3
3
  import { classMap } from 'lit/directives/class-map.js';
4
4
  import type { Placement } from '@floating-ui/dom';
5
5
  import styles from './menu.scss';
6
+ import { FloatingController } from '../../__controllers/floating-controller.js';
6
7
  import { MenuItem } from '../menu-item/menu-item.js';
7
- import { MenuSurfaceController } from './MenuSurfaceController.js';
8
8
 
9
9
  type CloseReason =
10
10
  | { kind: 'click-selection' }
@@ -60,7 +60,18 @@ export class Menu extends LitElement {
60
60
 
61
61
  anchorElement: HTMLElement | null = null;
62
62
 
63
- private readonly _surfaceController = new MenuSurfaceController(this);
63
+ private readonly _floatingController = new FloatingController(this, {
64
+ trigger: 'manual',
65
+ closeOnClickOutside: false,
66
+ strategy: 'fixed',
67
+ onOpenChange: (isOpen) => {
68
+ if (isOpen || !this.open) {
69
+ return;
70
+ }
71
+
72
+ this.close({ kind: 'outside-click' });
73
+ },
74
+ });
64
75
 
65
76
  private _lastFocusedElement: HTMLElement | null = null;
66
77
 
@@ -72,22 +83,14 @@ export class Menu extends LitElement {
72
83
  this.setAttribute('role', 'menu');
73
84
 
74
85
  this.addEventListener('keydown', this._onKeyDown);
75
- this.addEventListener('focusout', this._onFocusOut);
76
- this.addEventListener('menu-item-activate', this._onItemActivate);
77
- this.addEventListener('menu-item-request-close', this._onItemRequestClose);
78
- window.addEventListener('click', this._onWindowClick, { capture: true });
86
+ this.addEventListener('click', this._onClick);
79
87
  this._syncAnchorAria();
80
88
  }
81
89
 
82
90
  disconnectedCallback() {
83
91
  this.removeEventListener('keydown', this._onKeyDown);
84
- this.removeEventListener('focusout', this._onFocusOut);
85
- this.removeEventListener('menu-item-activate', this._onItemActivate);
86
- this.removeEventListener(
87
- 'menu-item-request-close',
88
- this._onItemRequestClose,
89
- );
90
- window.removeEventListener('click', this._onWindowClick, { capture: true });
92
+ this.removeEventListener('click', this._onClick);
93
+ this._floatingController.close();
91
94
  super.disconnectedCallback();
92
95
  }
93
96
 
@@ -170,7 +173,12 @@ export class Menu extends LitElement {
170
173
  }
171
174
 
172
175
  private _syncRovingTabIndex() {
176
+ const ownedItems = this.items;
173
177
  const enabledItems = this._enabledItems();
178
+ for (const item of ownedItems) {
179
+ item.tabIndex = -1;
180
+ }
181
+
174
182
  if (!enabledItems.length) {
175
183
  this.activeIndex = -1;
176
184
  return;
@@ -180,10 +188,7 @@ export class Menu extends LitElement {
180
188
  this.activeIndex = 0;
181
189
  }
182
190
 
183
- for (let index = 0; index < enabledItems.length; index += 1) {
184
- const currentItem = enabledItems[index];
185
- currentItem.tabIndex = index === this.activeIndex ? 0 : -1;
186
- }
191
+ enabledItems[this.activeIndex].tabIndex = 0;
187
192
  }
188
193
 
189
194
  private _setActiveByOffset(offset: 1 | -1) {
@@ -227,50 +232,66 @@ export class Menu extends LitElement {
227
232
  return this._enabledItems()[0] ?? null;
228
233
  }
229
234
 
230
- private _ownsKeyboardEvent(event: KeyboardEvent) {
235
+ private _isEventFromThisMenu(event: Event) {
231
236
  const path = event.composedPath();
232
- const ownedItems = this.items;
237
+ const sourceMenu = path.find(
238
+ target =>
239
+ target instanceof HTMLElement &&
240
+ target.tagName.toLowerCase() === 'wc-menu',
241
+ );
233
242
 
234
- return path.some(target => target instanceof MenuItem && ownedItems.includes(target));
243
+ return sourceMenu === this;
235
244
  }
236
245
 
237
- private _onItemActivate = (event: Event) => {
238
- const customEvent = event as CustomEvent<{ item: MenuItem }>;
239
- const { item } = customEvent.detail;
246
+ private _ownedItemFromEvent(event: Event) {
247
+ if (!this._isEventFromThisMenu(event)) {
248
+ return null;
249
+ }
250
+
251
+ const path = event.composedPath();
240
252
  const ownedItems = this.items;
241
- if (!ownedItems.includes(item)) {
242
- return;
253
+
254
+ for (const target of path) {
255
+ if (target instanceof HTMLElement) {
256
+ if (target.tagName.toLowerCase() === 'wc-menu-item') {
257
+ const ownedItem = ownedItems.find(item => item === target);
258
+ if (ownedItem) {
259
+ return ownedItem;
260
+ }
261
+ }
262
+ }
243
263
  }
244
264
 
265
+ return null;
266
+ }
267
+
268
+ private _setActiveItem(item: MenuItem) {
245
269
  const enabledItems = this._enabledItems();
246
270
  const nextIndex = enabledItems.indexOf(item);
247
- if (nextIndex >= 0) {
248
- this.activeIndex = nextIndex;
249
- this._syncRovingTabIndex();
271
+ if (nextIndex < 0) {
272
+ return;
250
273
  }
251
- };
252
274
 
253
- private _onItemRequestClose = (event: Event) => {
254
- const customEvent = event as CustomEvent<{
255
- item: MenuItem;
256
- reason: 'click-selection' | 'keydown';
257
- key?: string;
258
- }>;
275
+ this.activeIndex = nextIndex;
276
+ this._syncRovingTabIndex();
277
+ }
259
278
 
260
- if (!this.items.includes(customEvent.detail.item)) {
279
+ private _onClick = (event: Event) => {
280
+ if (!this.open) {
261
281
  return;
262
282
  }
263
283
 
264
- if (customEvent.defaultPrevented) {
284
+ const item = this._ownedItemFromEvent(event);
285
+ if (!item) {
265
286
  return;
266
287
  }
267
288
 
268
- if (customEvent.detail.reason === 'click-selection') {
269
- this.close({ kind: 'click-selection' });
289
+ this._setActiveItem(item);
290
+ if (item.keepOpen) {
270
291
  return;
271
292
  }
272
293
 
273
- this.close({ kind: 'keydown', key: customEvent.detail.key ?? 'Enter' });
294
+ this.close({ kind: 'click-selection' });
274
295
  };
275
296
 
276
297
  private _onKeyDown = (event: KeyboardEvent) => {
@@ -278,10 +299,15 @@ export class Menu extends LitElement {
278
299
  return;
279
300
  }
280
301
 
281
- if (!this._ownsKeyboardEvent(event)) {
302
+ if (!this._isEventFromThisMenu(event)) {
282
303
  return;
283
304
  }
284
305
 
306
+ const eventItem = this._ownedItemFromEvent(event);
307
+ if (eventItem) {
308
+ this._setActiveItem(eventItem);
309
+ }
310
+
285
311
  switch (event.key) {
286
312
  case 'ArrowDown':
287
313
  event.preventDefault();
@@ -305,67 +331,27 @@ export class Menu extends LitElement {
305
331
  event.preventDefault();
306
332
  this.close({ kind: 'keydown', key: 'Escape' });
307
333
  break;
308
- default:
334
+ case 'Tab':
335
+ this.close({ kind: 'keydown', key: 'Tab' });
309
336
  break;
310
- }
311
- };
312
-
313
- private _onWindowClick = (event: MouseEvent) => {
314
- if (!this.open || this.stayOpenOnOutsideClick) {
315
- return;
316
- }
317
-
318
- const path = event.composedPath();
319
- const anchorEl = this._resolveAnchorElement();
320
- const inMenuTree = path.some(
321
- target => target === this || (target instanceof Node && this.contains(target)),
322
- );
323
-
324
- if (inMenuTree || (anchorEl && path.includes(anchorEl))) {
325
- return;
326
- }
327
-
328
- this.close({ kind: 'outside-click' });
329
- };
330
-
331
- private _isWithinMenuTree(node: Node | null) {
332
- if (!node) {
333
- return false;
334
- }
335
-
336
- let current: Node | null = node;
337
- while (current) {
338
- if (current === this || this.contains(current)) {
339
- return true;
340
- }
337
+ case 'Enter':
338
+ case ' ': {
339
+ event.preventDefault();
340
+ const activeItem = this._getActiveItem() ?? this._getFirstEnabledItem();
341
+ if (!activeItem) {
342
+ return;
343
+ }
341
344
 
342
- const root = current.getRootNode();
343
- if (root instanceof ShadowRoot) {
344
- current = root.host;
345
- } else {
346
- current = null;
345
+ this._setActiveItem(activeItem);
346
+ activeItem.click();
347
+ break;
347
348
  }
349
+ default:
350
+ break;
348
351
  }
352
+ };
349
353
 
350
- return false;
351
- }
352
-
353
- private _onFocusOut = (event: FocusEvent) => {
354
- if (!this.open || this.stayOpenOnFocusout) {
355
- return;
356
- }
357
-
358
- const next = event.relatedTarget;
359
- if (!next) {
360
- return;
361
- }
362
-
363
- if (next instanceof Node && this._isWithinMenuTree(next)) {
364
- return;
365
- }
366
354
 
367
- this.close({ kind: 'focusout' });
368
- };
369
355
 
370
356
  private _onSlotChange = () => {
371
357
  this._syncRovingTabIndex();
@@ -381,13 +367,14 @@ export class Menu extends LitElement {
381
367
  return;
382
368
  }
383
369
 
384
- this._surfaceController.start({
385
- reference: anchorEl,
386
- floating: this.menuListElement,
370
+ this._floatingController.setOptions({
387
371
  placement: this.placement,
388
372
  offset: this.offset,
389
373
  strategy: 'fixed',
374
+ closeOnClickOutside: !this.stayOpenOnOutsideClick,
390
375
  });
376
+ this._floatingController.setElements(anchorEl, this.menuListElement);
377
+ this._floatingController.open();
391
378
  }
392
379
 
393
380
  protected override updated(changedProperties: Map<string, unknown>) {
@@ -408,7 +395,7 @@ export class Menu extends LitElement {
408
395
 
409
396
  this._applyPositioning();
410
397
  } else {
411
- this._surfaceController.stop();
398
+ this._floatingController.close();
412
399
 
413
400
  const reason = this._closeReason;
414
401
  this.dispatchEvent(
@@ -429,7 +416,10 @@ export class Menu extends LitElement {
429
416
  }),
430
417
  );
431
418
 
432
- if (!this.isSubmenu) {
419
+ const shouldRestoreFocus =
420
+ reason.kind !== 'keydown' || reason.key !== 'Tab';
421
+
422
+ if (!this.isSubmenu && shouldRestoreFocus) {
433
423
  this._lastFocusedElement?.focus();
434
424
  }
435
425
  }
@@ -92,6 +92,10 @@
92
92
  --_container-state-opacity: 0.08;
93
93
  }
94
94
 
95
+ &.pressed:not(:where(.disabled)) {
96
+ --_container-state-opacity: 0.12;
97
+ }
98
+
95
99
  &.selected {
96
100
  --_container-color: var(--menu-item-container-selected-color);
97
101
  --_label-text-color: var(--menu-item-label-selected-color);