@realmorg/ui 0.0.0-stage → 2.0.0-alpha.2

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 (66) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +44 -2
  3. package/components/accordion.html +121 -0
  4. package/components/alert-dialog.html +196 -0
  5. package/components/alert.html +69 -0
  6. package/components/aspect-ratio.html +23 -0
  7. package/components/attachment.html +84 -0
  8. package/components/avatar.html +127 -0
  9. package/components/badge.html +56 -0
  10. package/components/breadcrumb.html +129 -0
  11. package/components/bubble.html +67 -0
  12. package/components/button-group.html +84 -0
  13. package/components/button.html +188 -0
  14. package/components/calendar.html +170 -0
  15. package/components/card.html +113 -0
  16. package/components/carousel.html +186 -0
  17. package/components/chart.html +162 -0
  18. package/components/checkbox.html +93 -0
  19. package/components/code-block.html +58 -0
  20. package/components/collapsible.html +76 -0
  21. package/components/combobox.html +224 -0
  22. package/components/command.html +189 -0
  23. package/components/context-menu.html +67 -0
  24. package/components/dialog.html +281 -0
  25. package/components/direction.html +17 -0
  26. package/components/drawer.html +224 -0
  27. package/components/dropdown-menu.html +372 -0
  28. package/components/empty.html +106 -0
  29. package/components/field.html +117 -0
  30. package/components/hover-card.html +94 -0
  31. package/components/input-group.html +75 -0
  32. package/components/input-otp.html +139 -0
  33. package/components/input.html +82 -0
  34. package/components/item.html +170 -0
  35. package/components/kbd.html +35 -0
  36. package/components/label.html +40 -0
  37. package/components/marker.html +35 -0
  38. package/components/menubar.html +116 -0
  39. package/components/message-scroller.html +62 -0
  40. package/components/message.html +77 -0
  41. package/components/navigation-menu.html +204 -0
  42. package/components/pagination.html +154 -0
  43. package/components/popover.html +133 -0
  44. package/components/progress.html +39 -0
  45. package/components/radio-group.html +114 -0
  46. package/components/resizable.html +156 -0
  47. package/components/scroll-area.html +50 -0
  48. package/components/select.html +91 -0
  49. package/components/separator.html +25 -0
  50. package/components/sheet.html +143 -0
  51. package/components/sidebar.html +343 -0
  52. package/components/skeleton.html +30 -0
  53. package/components/slider.html +81 -0
  54. package/components/spinner.html +47 -0
  55. package/components/switch.html +89 -0
  56. package/components/table.html +75 -0
  57. package/components/tabs.html +164 -0
  58. package/components/textarea.html +68 -0
  59. package/components/toaster.html +128 -0
  60. package/components/toggle-group.html +65 -0
  61. package/components/toggle.html +81 -0
  62. package/components/tooltip.html +97 -0
  63. package/dist/theme.css +145 -0
  64. package/dist/ui.html +6944 -0
  65. package/package.json +40 -3
  66. package/theme.css +145 -0
@@ -0,0 +1,372 @@
1
+ <custom-element name="ui-dropdown-menu">
2
+ <script>
3
+ let wasOpen = false;
4
+
5
+ function press() {
6
+ wasOpen = host.querySelector(":scope > ui-dropdown-menu-content").matches(":popover-open");
7
+ }
8
+
9
+ function toggle(trigger) {
10
+ const content = host.querySelector(":scope > ui-dropdown-menu-content");
11
+ const open = wasOpen || content.matches(":popover-open");
12
+ wasOpen = false;
13
+ if (open) content.hidePopover();
14
+ else {
15
+ trigger.firstElementChild?.focus();
16
+ content.showPopover({ source: trigger });
17
+ }
18
+ }
19
+ </script>
20
+
21
+ <style>
22
+ :host {
23
+ display: contents;
24
+ }
25
+ </style>
26
+
27
+ <slot></slot>
28
+ </custom-element>
29
+
30
+ <custom-element name="ui-dropdown-menu-trigger">
31
+ <style>
32
+ :host {
33
+ display: inline-block;
34
+ vertical-align: middle;
35
+ }
36
+ </style>
37
+
38
+ <span part="trigger" onpointerdown="press()" onclick="toggle(host)"><slot></slot></span>
39
+ </custom-element>
40
+
41
+ <custom-element name="ui-dropdown-menu-content" role="menu">
42
+ <script>
43
+ defineProps({
44
+ placement: prop.enum(["bottom-start", "bottom-end", "top-start", "top-end"]).default("bottom-start"),
45
+ });
46
+
47
+ const ITEMS = ":is(ui-menu-item, ui-menu-checkbox-item, ui-menu-radio-item):not([disabled])";
48
+
49
+ function opened(event) {
50
+ if (event.newState === "open") host.querySelector(ITEMS)?.focus();
51
+ }
52
+
53
+ function leave(event) {
54
+ if (document.hasFocus() && !host.contains(event.relatedTarget)) host.hidePopover();
55
+ }
56
+
57
+ attrs.popover ??= "auto";
58
+
59
+ listen("toggle", opened);
60
+ listen("focusout", leave);
61
+ listen("select", () => host.hidePopover());
62
+ </script>
63
+
64
+ <style>
65
+ :host {
66
+ box-sizing: border-box;
67
+ min-width: calc(var(--spacing) * 32);
68
+ margin: calc(var(--spacing) * 1) 0 0;
69
+ padding: calc(var(--spacing) * 1);
70
+ border: 1px solid var(--border);
71
+ border-radius: var(--radius-md);
72
+ background: var(--popover);
73
+ color: var(--popover-foreground);
74
+ box-shadow: var(--shadow-md);
75
+ inset: auto;
76
+ position-area: bottom span-right;
77
+ position-try-fallbacks: flip-block, flip-inline;
78
+ }
79
+ :host(:state(attr-placement-is-bottom-end)) {
80
+ position-area: bottom span-left;
81
+ }
82
+ :host(:state(attr-placement-is-top-start)) {
83
+ position-area: top span-right;
84
+ margin: 0 0 calc(var(--spacing) * 1);
85
+ }
86
+ :host(:state(attr-placement-is-top-end)) {
87
+ position-area: top span-left;
88
+ margin: 0 0 calc(var(--spacing) * 1);
89
+ }
90
+ </style>
91
+
92
+ <div part="menu" focusgroup="menu block wrap"><slot></slot></div>
93
+ </custom-element>
94
+
95
+ <custom-element name="ui-menu-item" role="menuitem">
96
+ <script>
97
+ const props = defineProps({
98
+ value: prop.string(),
99
+ disabled: prop.boolean(),
100
+ variant: prop.enum(["default", "destructive"]).default("default"),
101
+ });
102
+
103
+ const ACTIVATE = new Set(["Enter", " "]);
104
+
105
+ function choose() {
106
+ if (!props.disabled) dispatch("select", { value: props.value });
107
+ }
108
+
109
+ function activate(event) {
110
+ if (!ACTIVATE.has(event.key)) return;
111
+ event.preventDefault();
112
+ choose();
113
+ }
114
+
115
+ attrs.tabindex ??= -1;
116
+
117
+ listen("click", choose);
118
+ listen("keydown", activate);
119
+
120
+ observe(() => {
121
+ internals.ariaDisabled = String(props.disabled);
122
+ });
123
+ </script>
124
+
125
+ <style>
126
+ :host {
127
+ display: flex;
128
+ align-items: center;
129
+ gap: calc(var(--spacing) * 2);
130
+ padding: calc(var(--spacing) * 1.5) calc(var(--spacing) * 2);
131
+ border-radius: var(--radius-sm);
132
+ font-size: var(--text-sm);
133
+ cursor: default;
134
+ outline: none;
135
+ user-select: none;
136
+ }
137
+ :host(:hover),
138
+ :host(:focus-visible) {
139
+ background: var(--accent);
140
+ color: var(--accent-foreground);
141
+ }
142
+ :host(:state(attr-variant-is-destructive)) {
143
+ color: var(--destructive);
144
+ }
145
+ :host(:state(attr-disabled)) {
146
+ pointer-events: none;
147
+ opacity: 0.5;
148
+ }
149
+ ::slotted(svg) {
150
+ width: calc(var(--spacing) * 4);
151
+ height: calc(var(--spacing) * 4);
152
+ color: var(--muted-foreground);
153
+ }
154
+ </style>
155
+
156
+ <slot></slot>
157
+ </custom-element>
158
+
159
+ <custom-element name="ui-menu-checkbox-item" role="menuitemcheckbox">
160
+ <script>
161
+ const props = defineProps({
162
+ value: prop.string(),
163
+ checked: prop.boolean().reflect(),
164
+ disabled: prop.boolean(),
165
+ });
166
+
167
+ const ACTIVATE = new Set(["Enter", " "]);
168
+
169
+ function choose() {
170
+ if (props.disabled) return;
171
+ props.checked = !props.checked;
172
+ dispatch("select", { value: props.value, checked: props.checked });
173
+ }
174
+
175
+ function activate(event) {
176
+ if (!ACTIVATE.has(event.key)) return;
177
+ event.preventDefault();
178
+ choose();
179
+ }
180
+
181
+ attrs.tabindex ??= -1;
182
+
183
+ listen("click", choose);
184
+ listen("keydown", activate);
185
+
186
+ observe(() => {
187
+ internals.ariaDisabled = String(props.disabled);
188
+ internals.ariaChecked = String(props.checked);
189
+ });
190
+ </script>
191
+
192
+ <style>
193
+ :host {
194
+ display: flex;
195
+ align-items: center;
196
+ gap: calc(var(--spacing) * 2);
197
+ padding: calc(var(--spacing) * 1.5) calc(var(--spacing) * 2) calc(var(--spacing) * 1.5) calc(var(--spacing) * 8);
198
+ position: relative;
199
+ border-radius: var(--radius-sm);
200
+ font-size: var(--text-sm);
201
+ cursor: default;
202
+ outline: none;
203
+ user-select: none;
204
+ }
205
+ :host(:hover),
206
+ :host(:focus-visible) {
207
+ background: var(--accent);
208
+ color: var(--accent-foreground);
209
+ }
210
+ :host(:state(attr-disabled)) {
211
+ pointer-events: none;
212
+ opacity: 0.5;
213
+ }
214
+ svg {
215
+ position: absolute;
216
+ left: calc(var(--spacing) * 2);
217
+ width: calc(var(--spacing) * 4);
218
+ height: calc(var(--spacing) * 4);
219
+ visibility: hidden;
220
+ }
221
+ :host(:state(attr-checked)) svg {
222
+ visibility: visible;
223
+ }
224
+ </style>
225
+
226
+ <svg aria-hidden="true" part="indicator" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 6 9 17l-5-5" /></svg>
227
+ <slot></slot>
228
+ </custom-element>
229
+
230
+ <custom-element name="ui-menu-radio-group" role="group">
231
+ <script>
232
+ const props = defineProps({ value: prop.string().reflect() });
233
+
234
+ const items = useChildren("ui-menu-radio-item", ["value"]);
235
+
236
+ function picked(event) {
237
+ if (event.target.localName === "ui-menu-radio-item") props.value = event.detail.value;
238
+ }
239
+
240
+ listen("select", picked);
241
+
242
+ observe(() => {
243
+ for (const item of items) item.toggleAttribute("checked", item.getAttribute("value") === props.value);
244
+ });
245
+ </script>
246
+
247
+ <style>
248
+ :host {
249
+ display: block;
250
+ }
251
+ </style>
252
+
253
+ <slot></slot>
254
+ </custom-element>
255
+
256
+ <custom-element name="ui-menu-radio-item" role="menuitemradio">
257
+ <script>
258
+ const props = defineProps({
259
+ value: prop.string(),
260
+ checked: prop.boolean(),
261
+ disabled: prop.boolean(),
262
+ });
263
+
264
+ const ACTIVATE = new Set(["Enter", " "]);
265
+
266
+ function choose() {
267
+ if (!props.disabled) dispatch("select", { value: props.value });
268
+ }
269
+
270
+ function activate(event) {
271
+ if (!ACTIVATE.has(event.key)) return;
272
+ event.preventDefault();
273
+ choose();
274
+ }
275
+
276
+ attrs.tabindex ??= -1;
277
+
278
+ listen("click", choose);
279
+ listen("keydown", activate);
280
+
281
+ observe(() => {
282
+ internals.ariaDisabled = String(props.disabled);
283
+ internals.ariaChecked = String(props.checked);
284
+ });
285
+ </script>
286
+
287
+ <style>
288
+ :host {
289
+ display: flex;
290
+ align-items: center;
291
+ gap: calc(var(--spacing) * 2);
292
+ padding: calc(var(--spacing) * 1.5) calc(var(--spacing) * 2) calc(var(--spacing) * 1.5) calc(var(--spacing) * 8);
293
+ position: relative;
294
+ border-radius: var(--radius-sm);
295
+ font-size: var(--text-sm);
296
+ cursor: default;
297
+ outline: none;
298
+ user-select: none;
299
+ }
300
+ :host(:hover),
301
+ :host(:focus-visible) {
302
+ background: var(--accent);
303
+ color: var(--accent-foreground);
304
+ }
305
+ :host(:state(attr-disabled)) {
306
+ pointer-events: none;
307
+ opacity: 0.5;
308
+ }
309
+ span {
310
+ position: absolute;
311
+ left: calc(var(--spacing) * 3.5);
312
+ width: calc(var(--spacing) * 2);
313
+ height: calc(var(--spacing) * 2);
314
+ border-radius: var(--radius-full);
315
+ background: currentColor;
316
+ visibility: hidden;
317
+ }
318
+ :host(:state(attr-checked)) span {
319
+ visibility: visible;
320
+ }
321
+ </style>
322
+
323
+ <span aria-hidden="true" part="indicator"></span>
324
+ <slot></slot>
325
+ </custom-element>
326
+
327
+ <custom-element name="ui-menu-label">
328
+ <style>
329
+ :host {
330
+ display: block;
331
+ padding: calc(var(--spacing) * 1.5) calc(var(--spacing) * 2);
332
+ font-size: var(--text-sm);
333
+ font-weight: var(--font-weight-medium);
334
+ }
335
+ </style>
336
+
337
+ <slot></slot>
338
+ </custom-element>
339
+
340
+ <custom-element name="ui-menu-group" role="group">
341
+ <style>
342
+ :host {
343
+ display: block;
344
+ }
345
+ </style>
346
+
347
+ <slot></slot>
348
+ </custom-element>
349
+
350
+ <custom-element name="ui-menu-separator" role="separator">
351
+ <style>
352
+ :host {
353
+ display: block;
354
+ height: 1px;
355
+ margin: calc(var(--spacing) * 1) calc(var(--spacing) * -1);
356
+ background: var(--border);
357
+ }
358
+ </style>
359
+ </custom-element>
360
+
361
+ <custom-element name="ui-menu-shortcut">
362
+ <style>
363
+ :host {
364
+ margin-inline-start: auto;
365
+ font-size: var(--text-xs);
366
+ letter-spacing: 0.1em;
367
+ color: var(--muted-foreground);
368
+ }
369
+ </style>
370
+
371
+ <slot></slot>
372
+ </custom-element>
@@ -0,0 +1,106 @@
1
+ <custom-element name="ui-empty">
2
+ <style>
3
+ :host {
4
+ display: flex;
5
+ flex-direction: column;
6
+ align-items: center;
7
+ justify-content: center;
8
+ gap: calc(var(--spacing) * 6);
9
+ min-width: 0;
10
+ padding: calc(var(--spacing) * 12) calc(var(--spacing) * 6);
11
+ border: 1px dashed var(--border);
12
+ border-radius: var(--radius-lg);
13
+ text-align: center;
14
+ text-wrap: balance;
15
+ }
16
+ </style>
17
+
18
+ <slot></slot>
19
+ </custom-element>
20
+
21
+ <custom-element name="ui-empty-header">
22
+ <style>
23
+ :host {
24
+ display: flex;
25
+ flex-direction: column;
26
+ align-items: center;
27
+ gap: calc(var(--spacing) * 2);
28
+ max-width: calc(var(--spacing) * 96);
29
+ }
30
+ </style>
31
+
32
+ <slot></slot>
33
+ </custom-element>
34
+
35
+ <custom-element name="ui-empty-media">
36
+ <style>
37
+ :host {
38
+ display: flex;
39
+ flex-shrink: 0;
40
+ align-items: center;
41
+ justify-content: center;
42
+ margin-bottom: calc(var(--spacing) * 2);
43
+ }
44
+ :host(:state(attr-variant-is-icon)) {
45
+ width: calc(var(--spacing) * 10);
46
+ height: calc(var(--spacing) * 10);
47
+ border-radius: var(--radius-lg);
48
+ background: var(--muted);
49
+ color: var(--foreground);
50
+ }
51
+ ::slotted(svg) {
52
+ width: calc(var(--spacing) * 6);
53
+ height: calc(var(--spacing) * 6);
54
+ }
55
+ </style>
56
+
57
+ <slot></slot>
58
+ </custom-element>
59
+
60
+ <custom-element name="ui-empty-title">
61
+ <script>
62
+ defineProps({ level: prop.number().int().min(1).max(6).default(3) });
63
+ </script>
64
+
65
+ <style>
66
+ :host {
67
+ display: block;
68
+ }
69
+ [part="title"] {
70
+ margin: 0;
71
+ font-size: var(--text-lg);
72
+ font-weight: var(--font-weight-medium);
73
+ letter-spacing: -0.01em;
74
+ }
75
+ </style>
76
+
77
+ <div part="title" role="heading" _aria-level="@level"><slot></slot></div>
78
+ </custom-element>
79
+
80
+ <custom-element name="ui-empty-description">
81
+ <style>
82
+ :host {
83
+ display: block;
84
+ font-size: var(--text-sm);
85
+ color: var(--muted-foreground);
86
+ }
87
+ </style>
88
+
89
+ <slot></slot>
90
+ </custom-element>
91
+
92
+ <custom-element name="ui-empty-content">
93
+ <style>
94
+ :host {
95
+ display: flex;
96
+ flex-direction: column;
97
+ align-items: center;
98
+ gap: calc(var(--spacing) * 4);
99
+ width: 100%;
100
+ max-width: calc(var(--spacing) * 96);
101
+ font-size: var(--text-sm);
102
+ }
103
+ </style>
104
+
105
+ <slot></slot>
106
+ </custom-element>
@@ -0,0 +1,117 @@
1
+ <custom-element name="ui-field" role="group">
2
+ <script>
3
+ const props = defineProps({
4
+ orientation: prop.enum(["vertical", "horizontal"]).default("vertical"),
5
+ invalid: prop.boolean(),
6
+ error: prop.string(),
7
+ });
8
+ </script>
9
+
10
+ <style>
11
+ :host {
12
+ display: flex;
13
+ flex-direction: column;
14
+ gap: calc(var(--spacing) * 3);
15
+ width: 100%;
16
+ }
17
+ :host(:state(attr-orientation-is-horizontal)) {
18
+ flex-flow: row wrap;
19
+ align-items: center;
20
+ }
21
+ :host(:state(attr-orientation-is-horizontal)) ::slotted(ui-label) {
22
+ flex: 1 1 auto;
23
+ }
24
+ ::slotted(ui-label) {
25
+ line-height: 1.375;
26
+ }
27
+ :host(:state(attr-invalid)) ::slotted(ui-label) {
28
+ color: var(--destructive);
29
+ }
30
+ .error {
31
+ flex-basis: 100%;
32
+ margin: 0;
33
+ font-size: var(--text-sm);
34
+ color: var(--destructive);
35
+ }
36
+ </style>
37
+
38
+ <slot></slot>
39
+ <p class="error" role="alert" part="error" _hidden="!@error"><slot name="@error"></slot></p>
40
+ </custom-element>
41
+
42
+ <custom-element name="ui-field-content">
43
+ <style>
44
+ :host {
45
+ display: flex;
46
+ flex: 1;
47
+ flex-direction: column;
48
+ gap: calc(var(--spacing) * 1.5);
49
+ line-height: 1.375;
50
+ }
51
+ </style>
52
+
53
+ <slot></slot>
54
+ </custom-element>
55
+
56
+ <custom-element name="ui-field-description">
57
+ <style>
58
+ :host {
59
+ display: block;
60
+ font-size: var(--text-sm);
61
+ line-height: 1.375;
62
+ color: var(--muted-foreground);
63
+ }
64
+ </style>
65
+
66
+ <slot></slot>
67
+ </custom-element>
68
+
69
+ <custom-element name="ui-field-group">
70
+ <style>
71
+ :host {
72
+ display: flex;
73
+ flex-direction: column;
74
+ gap: calc(var(--spacing) * 7);
75
+ width: 100%;
76
+ }
77
+ </style>
78
+
79
+ <slot></slot>
80
+ </custom-element>
81
+
82
+ <custom-element name="ui-field-set" role="group">
83
+ <script>
84
+ function named(slot) {
85
+ internals.ariaLabelledByElements = slot.assignedElements();
86
+ }
87
+ </script>
88
+
89
+ <style>
90
+ :host {
91
+ display: flex;
92
+ flex-direction: column;
93
+ gap: calc(var(--spacing) * 6);
94
+ }
95
+ ::slotted(ui-field-description) {
96
+ margin-top: calc(var(--spacing) * -4);
97
+ }
98
+ </style>
99
+
100
+ <slot name="legend" onslotchange="named(this)"></slot>
101
+ <slot></slot>
102
+ </custom-element>
103
+
104
+ <custom-element name="ui-field-legend" slot="legend">
105
+ <style>
106
+ :host {
107
+ display: block;
108
+ font-size: var(--text-base);
109
+ font-weight: var(--font-weight-medium);
110
+ }
111
+ :host(:state(attr-variant-is-label)) {
112
+ font-size: var(--text-sm);
113
+ }
114
+ </style>
115
+
116
+ <slot></slot>
117
+ </custom-element>
@@ -0,0 +1,94 @@
1
+ <custom-element name="ui-hover-card">
2
+ <script>
3
+ const props = defineProps({
4
+ openDelay: prop.number().attribute("open-delay").min(0).default(500),
5
+ closeDelay: prop.number().attribute("close-delay").min(0).default(300),
6
+ });
7
+
8
+ let timer;
9
+ let source;
10
+
11
+ function apply(open) {
12
+ const card = host.querySelector(":scope > ui-hover-card-content");
13
+ if (card.matches(":popover-open") === open) return;
14
+ if (open) card.showPopover({ source });
15
+ else card.hidePopover();
16
+ }
17
+
18
+ function schedule(open, trigger = source) {
19
+ source = trigger;
20
+ clearTimeout(timer);
21
+ timer = setTimeout(() => apply(open), open ? props.openDelay : props.closeDelay);
22
+ }
23
+
24
+ function dismiss(event) {
25
+ if (event.key !== "Escape") return;
26
+ clearTimeout(timer);
27
+ apply(false);
28
+ }
29
+
30
+ listen("keydown", dismiss, document);
31
+ </script>
32
+
33
+ <style>
34
+ :host {
35
+ display: contents;
36
+ }
37
+ </style>
38
+
39
+ <slot></slot>
40
+ </custom-element>
41
+
42
+ <custom-element name="ui-hover-card-trigger">
43
+ <style>
44
+ :host {
45
+ display: inline-block;
46
+ }
47
+ </style>
48
+
49
+ <span part="trigger" onpointerenter="schedule(true, host)" onpointerleave="schedule(false)" onfocusin="schedule(true, host)" onfocusout="schedule(false)"><slot></slot></span>
50
+ </custom-element>
51
+
52
+ <custom-element name="ui-hover-card-content">
53
+ <script>
54
+ defineProps({
55
+ placement: prop.enum(["bottom", "top", "left", "right"]).default("bottom"),
56
+ });
57
+
58
+ attrs.popover ??= "manual";
59
+ </script>
60
+
61
+ <style>
62
+ :host {
63
+ box-sizing: border-box;
64
+ width: calc(var(--spacing) * 64);
65
+ margin: calc(var(--spacing) * 1);
66
+ padding: 0;
67
+ border: 1px solid var(--border);
68
+ border-radius: var(--radius-md);
69
+ background: var(--popover);
70
+ color: var(--popover-foreground);
71
+ box-shadow: var(--shadow-md);
72
+ inset: auto;
73
+ position-area: bottom;
74
+ position-try-fallbacks: flip-block, flip-inline;
75
+ }
76
+ :host(:state(attr-width)) {
77
+ width: calc(var(--spacing) * var(--attr-width));
78
+ }
79
+ :host(:state(attr-placement-is-top)) {
80
+ position-area: top;
81
+ }
82
+ :host(:state(attr-placement-is-left)) {
83
+ position-area: left;
84
+ }
85
+ :host(:state(attr-placement-is-right)) {
86
+ position-area: right;
87
+ }
88
+ div {
89
+ padding: calc(var(--spacing) * 4);
90
+ }
91
+ </style>
92
+
93
+ <div part="content" onpointerenter="schedule(true)" onpointerleave="schedule(false)"><slot></slot></div>
94
+ </custom-element>