luxen-ui 0.9.0 → 0.9.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 (39) hide show
  1. package/cdn/custom-elements.json +148 -123
  2. package/cdn/elements/dropdown-item/dropdown-item.js +1 -1
  3. package/cdn/elements/dropdown-item/dropdown-item.js.map +1 -1
  4. package/cdn/elements/prose-editor/prose-editor.d.ts +24 -2
  5. package/cdn/elements/prose-editor/prose-editor.d.ts.map +1 -1
  6. package/cdn/elements/prose-editor/prose-editor.js +40 -39
  7. package/cdn/elements/prose-editor/prose-editor.js.map +1 -1
  8. package/cdn/elements/tree/tree.d.ts +11 -1
  9. package/cdn/elements/tree/tree.d.ts.map +1 -1
  10. package/cdn/elements/tree/tree.js +1 -3
  11. package/cdn/elements/tree/tree.js.map +1 -1
  12. package/cdn/elements/tree-item/tree-item.d.ts +17 -1
  13. package/cdn/elements/tree-item/tree-item.d.ts.map +1 -1
  14. package/cdn/elements/tree-item/tree-item.js +2 -1
  15. package/cdn/elements/tree-item/tree-item.js.map +1 -1
  16. package/cdn/standalone.css +4 -0
  17. package/cdn/standalone.js +99 -50
  18. package/cdn/standalone.js.map +1 -1
  19. package/cdn/styles/elements/button.css +4 -0
  20. package/dist/css/elements/button.css +4 -0
  21. package/dist/custom-elements.json +148 -123
  22. package/dist/elements/dropdown-item/dropdown-item.css +1 -0
  23. package/dist/elements/prose-editor/prose-editor.d.ts +24 -2
  24. package/dist/elements/prose-editor/prose-editor.d.ts.map +1 -1
  25. package/dist/elements/prose-editor/prose-editor.js +81 -48
  26. package/dist/elements/tree/tree.css +1 -1
  27. package/dist/elements/tree/tree.d.ts +11 -1
  28. package/dist/elements/tree/tree.d.ts.map +1 -1
  29. package/dist/elements/tree/tree.js +37 -11
  30. package/dist/elements/tree-item/tree-item.css +5 -1
  31. package/dist/elements/tree-item/tree-item.d.ts +17 -1
  32. package/dist/elements/tree-item/tree-item.d.ts.map +1 -1
  33. package/dist/elements/tree-item/tree-item.js +51 -10
  34. package/dist/metadata/index.json +22 -3
  35. package/dist/metadata/tree-item.json +20 -1
  36. package/dist/metadata/tree.json +1 -1
  37. package/dist/templates/elements/tree.md +18 -0
  38. package/package.json +4 -2
  39. package/postcss-plugin-prefix.js +43 -1
@@ -75,6 +75,10 @@
75
75
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
76
76
  */
77
77
 
78
+ &:not(:disabled) {
79
+ cursor: pointer;
80
+ }
81
+
78
82
  &:disabled {
79
83
  cursor: not-allowed;
80
84
  opacity: 0.4;
@@ -75,6 +75,10 @@
75
75
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
76
76
  */
77
77
 
78
+ &:not(:disabled) {
79
+ cursor: pointer;
80
+ }
81
+
78
82
  &:disabled {
79
83
  cursor: not-allowed;
80
84
  opacity: 0.4;
@@ -172,127 +172,6 @@
172
172
  }
173
173
  ]
174
174
  },
175
- {
176
- "kind": "javascript-module",
177
- "path": "src/html/elements/badge/badge.ts",
178
- "declarations": [
179
- {
180
- "kind": "class",
181
- "description": "",
182
- "name": "Badge",
183
- "members": [
184
- {
185
- "kind": "field",
186
- "name": "variant",
187
- "type": {
188
- "text": "BadgeVariant | undefined"
189
- },
190
- "description": "Style variant: `info`, `success`, `warning`, `danger`, or `neutral` (default)",
191
- "attribute": "variant",
192
- "reflects": true
193
- },
194
- {
195
- "kind": "field",
196
- "name": "pill",
197
- "type": {
198
- "text": "boolean"
199
- },
200
- "default": "false",
201
- "description": "Display as pill shape",
202
- "attribute": "pill",
203
- "reflects": true
204
- },
205
- {
206
- "kind": "field",
207
- "name": "size",
208
- "type": {
209
- "text": "BadgeSize | undefined"
210
- },
211
- "description": "Badge size: `sm`, `lg`. Default is md.",
212
- "attribute": "size",
213
- "reflects": true
214
- },
215
- {
216
- "kind": "field",
217
- "name": "appearance",
218
- "type": {
219
- "text": "BadgeAppearance | undefined"
220
- },
221
- "description": "Visual appearance: `filled`, `filled-outlined`, `accent`. Default is outlined.",
222
- "attribute": "appearance",
223
- "reflects": true
224
- }
225
- ],
226
- "attributes": [
227
- {
228
- "name": "variant",
229
- "type": {
230
- "text": "BadgeVariant | undefined"
231
- },
232
- "description": "Style variant: `info`, `success`, `warning`, `danger`, or `neutral` (default)",
233
- "fieldName": "variant"
234
- },
235
- {
236
- "name": "pill",
237
- "type": {
238
- "text": "boolean"
239
- },
240
- "default": "false",
241
- "description": "Display as pill shape",
242
- "fieldName": "pill"
243
- },
244
- {
245
- "name": "size",
246
- "type": {
247
- "text": "BadgeSize | undefined"
248
- },
249
- "description": "Badge size: `sm`, `lg`. Default is md.",
250
- "fieldName": "size"
251
- },
252
- {
253
- "name": "appearance",
254
- "type": {
255
- "text": "BadgeAppearance | undefined"
256
- },
257
- "description": "Visual appearance: `filled`, `filled-outlined`, `accent`. Default is outlined.",
258
- "fieldName": "appearance"
259
- }
260
- ],
261
- "superclass": {
262
- "name": "LuxenElement",
263
- "module": "/src/html/shared/luxen-element.js"
264
- },
265
- "tagName": "l-badge",
266
- "customElement": true,
267
- "summary": "A badge component for displaying small status indicators."
268
- }
269
- ],
270
- "exports": [
271
- {
272
- "kind": "js",
273
- "name": "Badge",
274
- "declaration": {
275
- "name": "Badge",
276
- "module": "src/html/elements/badge/badge.ts"
277
- }
278
- }
279
- ]
280
- },
281
- {
282
- "kind": "javascript-module",
283
- "path": "src/html/elements/badge/index.ts",
284
- "declarations": [],
285
- "exports": [
286
- {
287
- "kind": "js",
288
- "name": "*",
289
- "declaration": {
290
- "name": "*",
291
- "module": "src/html/elements/badge/badge.js"
292
- }
293
- }
294
- ]
295
- },
296
175
  {
297
176
  "kind": "javascript-module",
298
177
  "path": "src/html/elements/button/button.meta.ts",
@@ -446,6 +325,127 @@
446
325
  }
447
326
  ]
448
327
  },
328
+ {
329
+ "kind": "javascript-module",
330
+ "path": "src/html/elements/badge/badge.ts",
331
+ "declarations": [
332
+ {
333
+ "kind": "class",
334
+ "description": "",
335
+ "name": "Badge",
336
+ "members": [
337
+ {
338
+ "kind": "field",
339
+ "name": "variant",
340
+ "type": {
341
+ "text": "BadgeVariant | undefined"
342
+ },
343
+ "description": "Style variant: `info`, `success`, `warning`, `danger`, or `neutral` (default)",
344
+ "attribute": "variant",
345
+ "reflects": true
346
+ },
347
+ {
348
+ "kind": "field",
349
+ "name": "pill",
350
+ "type": {
351
+ "text": "boolean"
352
+ },
353
+ "default": "false",
354
+ "description": "Display as pill shape",
355
+ "attribute": "pill",
356
+ "reflects": true
357
+ },
358
+ {
359
+ "kind": "field",
360
+ "name": "size",
361
+ "type": {
362
+ "text": "BadgeSize | undefined"
363
+ },
364
+ "description": "Badge size: `sm`, `lg`. Default is md.",
365
+ "attribute": "size",
366
+ "reflects": true
367
+ },
368
+ {
369
+ "kind": "field",
370
+ "name": "appearance",
371
+ "type": {
372
+ "text": "BadgeAppearance | undefined"
373
+ },
374
+ "description": "Visual appearance: `filled`, `filled-outlined`, `accent`. Default is outlined.",
375
+ "attribute": "appearance",
376
+ "reflects": true
377
+ }
378
+ ],
379
+ "attributes": [
380
+ {
381
+ "name": "variant",
382
+ "type": {
383
+ "text": "BadgeVariant | undefined"
384
+ },
385
+ "description": "Style variant: `info`, `success`, `warning`, `danger`, or `neutral` (default)",
386
+ "fieldName": "variant"
387
+ },
388
+ {
389
+ "name": "pill",
390
+ "type": {
391
+ "text": "boolean"
392
+ },
393
+ "default": "false",
394
+ "description": "Display as pill shape",
395
+ "fieldName": "pill"
396
+ },
397
+ {
398
+ "name": "size",
399
+ "type": {
400
+ "text": "BadgeSize | undefined"
401
+ },
402
+ "description": "Badge size: `sm`, `lg`. Default is md.",
403
+ "fieldName": "size"
404
+ },
405
+ {
406
+ "name": "appearance",
407
+ "type": {
408
+ "text": "BadgeAppearance | undefined"
409
+ },
410
+ "description": "Visual appearance: `filled`, `filled-outlined`, `accent`. Default is outlined.",
411
+ "fieldName": "appearance"
412
+ }
413
+ ],
414
+ "superclass": {
415
+ "name": "LuxenElement",
416
+ "module": "/src/html/shared/luxen-element.js"
417
+ },
418
+ "tagName": "l-badge",
419
+ "customElement": true,
420
+ "summary": "A badge component for displaying small status indicators."
421
+ }
422
+ ],
423
+ "exports": [
424
+ {
425
+ "kind": "js",
426
+ "name": "Badge",
427
+ "declaration": {
428
+ "name": "Badge",
429
+ "module": "src/html/elements/badge/badge.ts"
430
+ }
431
+ }
432
+ ]
433
+ },
434
+ {
435
+ "kind": "javascript-module",
436
+ "path": "src/html/elements/badge/index.ts",
437
+ "declarations": [],
438
+ "exports": [
439
+ {
440
+ "kind": "js",
441
+ "name": "*",
442
+ "declaration": {
443
+ "name": "*",
444
+ "module": "src/html/elements/badge/badge.js"
445
+ }
446
+ }
447
+ ]
448
+ },
449
449
  {
450
450
  "kind": "javascript-module",
451
451
  "path": "src/html/elements/button-group/button-group.ts",
@@ -6040,7 +6040,7 @@
6040
6040
  "declarations": [
6041
6041
  {
6042
6042
  "kind": "class",
6043
- "description": "A hierarchical tree view composed of `<l-tree-item>` children.",
6043
+ "description": "A hierarchical tree view composed of `<l-tree-item>` children.\n\nThe host carries `role=\"tree\"`, so give it an accessible name with\n`aria-label` or `aria-labelledby` (e.g. `<l-tree aria-label=\"Files\">`).",
6044
6044
  "name": "Tree",
6045
6045
  "cssProperties": [
6046
6046
  {
@@ -6357,6 +6357,31 @@
6357
6357
  "text": "number"
6358
6358
  }
6359
6359
  },
6360
+ {
6361
+ "kind": "method",
6362
+ "name": "setPosition",
6363
+ "parameters": [
6364
+ {
6365
+ "name": "level",
6366
+ "type": {
6367
+ "text": "number"
6368
+ }
6369
+ },
6370
+ {
6371
+ "name": "posInSet",
6372
+ "type": {
6373
+ "text": "number"
6374
+ }
6375
+ },
6376
+ {
6377
+ "name": "setSize",
6378
+ "type": {
6379
+ "text": "number"
6380
+ }
6381
+ }
6382
+ ],
6383
+ "description": "Set by `<l-tree>`: ARIA position within the tree. `level` is 1-based depth,\n`posInSet`/`setSize` describe the item's rank among its siblings. These let\nscreen readers announce \"level 2, 3 of 5\" even when `lazy` children keep the\nfull set out of the DOM."
6384
+ },
6360
6385
  {
6361
6386
  "kind": "field",
6362
6387
  "name": "hasChildren",
@@ -6405,7 +6430,7 @@
6405
6430
  {
6406
6431
  "kind": "method",
6407
6432
  "name": "toggle",
6408
- "description": "Toggle expand state. Emits `lazy-load` the first time a lazy item opens."
6433
+ "description": "Toggle expand state. Opening a `lazy` item emits `lazy-load` (via `updated`)."
6409
6434
  }
6410
6435
  ],
6411
6436
  "events": [
@@ -19,6 +19,7 @@
19
19
  line-height: 1.5;
20
20
  color: var(--l-color-text-primary, CanvasText);
21
21
  white-space: nowrap;
22
+ text-align: start;
22
23
  }
23
24
 
24
25
  .item:focus-visible {
@@ -58,9 +58,10 @@ export declare class ProseEditor extends LuxenFormAssociatedElement {
58
58
  accessor autofocus: boolean;
59
59
  /** Placeholder shown when the editor is empty. */
60
60
  accessor placeholder: string;
61
- accessor _emojiPickerActive: boolean;
62
61
  private _editorRoot?;
63
62
  private _emojiPicker?;
63
+ private _emojiPickerPromise?;
64
+ private _emojiOpenAtPointerDown;
64
65
  get validationTarget(): HTMLElement | undefined;
65
66
  private get _toolbar();
66
67
  firstUpdated(): void;
@@ -103,7 +104,28 @@ export declare class ProseEditor extends LuxenFormAssociatedElement {
103
104
  private _createEditorRoot;
104
105
  private _onFocus;
105
106
  private _onKeyDown;
106
- private _toggleEmojiPicker;
107
+ /**
108
+ * The picker is a `popover="auto"`, so the platform owns outside-click
109
+ * dismissal: native light-dismiss in the top layer, which survives
110
+ * `stopPropagation()` from an ancestor (modal `<l-dialog>`, ProseMirror, Vue
111
+ * delegation), unlike emoji-mart's `document` click listener. (`Escape` is
112
+ * handled by `_onKeyDown` instead — ProseMirror `preventDefault()`s it, which
113
+ * would cancel the native close.)
114
+ *
115
+ * We can't wire the button as a native popover invoker (`popoverTargetElement`
116
+ * / `popovertarget`) because the picker is parented into another shadow tree
117
+ * (the open `<dialog>` — see `_topLayerContainer`) while the button lives in
118
+ * this element's shadow root; a cross-tree invoker reference doesn't resolve.
119
+ * So the toggle is hand-rolled here. The catch: a pointer click on the button
120
+ * is "outside" the popover, so native light-dismiss has already closed an open
121
+ * picker by the time this `click` fires (light-dismiss runs on `pointerup`).
122
+ * We therefore read the open state captured at `pointerdown` to know whether
123
+ * this click should re-open or stay closed. Keyboard activation (`detail === 0`)
124
+ * has no pointer light-dismiss, so it reads the live state and toggles directly.
125
+ */
126
+ private _onEmojiButtonPointerDown;
127
+ private _onEmojiButtonClick;
128
+ private _ensureEmojiPicker;
107
129
  /**
108
130
  * The element the emoji picker is appended to. Inside a modal `<l-dialog>` the
109
131
  * picker must live within the dialog's flat-tree subtree, otherwise the modal's
@@ -1 +1 @@
1
- {"version":3,"file":"prose-editor.d.ts","sourceRoot":"","sources":["../../../src/html/elements/prose-editor/prose-editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAO1E,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAIxD,OAAO,EAAE,0BAA0B,EAAE,MAAM,+CAA+C,CAAC;AAE3F,OAAO,kBAAkB,CAAC;AAK1B,KAAK,kBAAkB,GACnB,WAAW,GACX,WAAW,GACX,WAAW,GACX,MAAM,GACN,QAAQ,GACR,WAAW,GACX,QAAQ,GACR,WAAW,GACX,YAAY,GACZ,aAAa,GACb,YAAY,GACZ,YAAY,GACZ,iBAAiB,GACjB,MAAM,GACN,OAAO,GACP,YAAY,GACZ,MAAM,GACN,MAAM,GACN,SAAS,CAAC;AA2Bd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,WAAY,SAAQ,0BAA0B;IACzD,OAAgB,MAAM,4BAAwB;IAE9C,oEAAoE;IACpE,MAAM,EAAG,MAAM,CAAC;IAEhB,4BAA4B;IAE5B,QAAQ,CAAC,WAAW,SAAM;IAE1B,+DAA+D;IAE/D,QAAQ,CAAC,WAAW,SAAM;IAE1B,mGAAmG;IAEnG,QAAQ,CAAC,WAAW,SAAW;IAE/B,8EAA8E;IAO9E,QAAQ,CAAC,OAAO,EAAE,kBAAkB,EAAE,CAAM;IAE5C,8DAA8D;IAE9D,QAAQ,CAAC,aAAa,EAAE,SAAS,GAAG,SAAS,CAAa;IAE1D,sDAAsD;IAEtD,QAAQ,CAAC,gBAAgB,EAAE,KAAK,GAAG,QAAQ,CAAS;IAEpD,oCAAoC;IAEpC,QAAQ,CAAC,SAAS,UAAS;IAE3B,kDAAkD;IAElD,QAAQ,CAAC,WAAW,SAAM;IAG1B,QAAQ,CAAC,kBAAkB,UAAS;IAEpC,OAAO,CAAC,WAAW,CAAC,CAAiB;IACrC,OAAO,CAAC,YAAY,CAAC,CAAc;IAEnC,IAAa,gBAAgB,IAAI,WAAW,GAAG,SAAS,CAEvD;IAED,OAAO,KAAK,QAAQ,GAEnB;IAEQ,YAAY;IA4BZ,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC;IASrC,oBAAoB;IAW7B,mFAAmF;IACnF,OAAO,IAAI,MAAM;IAMjB,mDAAmD;IACnD,OAAO,IAAI,WAAW;IAItB,0BAA0B;IAC1B,KAAK;IAII,KAAK;IAIL,IAAI;IAIb,UAAU;IAIV,YAAY;IAIZ,eAAe;IAIf,YAAY;IAIZ,eAAe;IAIf,aAAa,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC;IAI9B,gBAAgB;IAIhB,iBAAiB;IAIjB,gBAAgB;IAIhB,eAAe;IAIf,iBAAiB;IAIjB,IAAI;IAIJ,IAAI;IAIJ,UAAU;IAaD,iBAAiB;IAK1B,mFAAmF;IACnF,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,eAAe;IAQvB;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,QAAQ,CAId;IAEF,OAAO,CAAC,UAAU,CAEhB;YAIY,kBAAkB;IAwChC;;;;;;OAMG;IACH,OAAO,CAAC,kBAAkB;IAe1B,OAAO,KAAK,YAAY,GAEvB;YAEa,oBAAoB;IAwBlC,OAAO,CAAC,aAAa;IAwBrB,OAAO,CAAC,kBAAkB;IAwIjB,MAAM;CAyBhB"}
1
+ {"version":3,"file":"prose-editor.d.ts","sourceRoot":"","sources":["../../../src/html/elements/prose-editor/prose-editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAO1E,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAIxD,OAAO,EAAE,0BAA0B,EAAE,MAAM,+CAA+C,CAAC;AAE3F,OAAO,kBAAkB,CAAC;AAK1B,KAAK,kBAAkB,GACnB,WAAW,GACX,WAAW,GACX,WAAW,GACX,MAAM,GACN,QAAQ,GACR,WAAW,GACX,QAAQ,GACR,WAAW,GACX,YAAY,GACZ,aAAa,GACb,YAAY,GACZ,YAAY,GACZ,iBAAiB,GACjB,MAAM,GACN,OAAO,GACP,YAAY,GACZ,MAAM,GACN,MAAM,GACN,SAAS,CAAC;AA2Bd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,WAAY,SAAQ,0BAA0B;IACzD,OAAgB,MAAM,4BAAwB;IAE9C,oEAAoE;IACpE,MAAM,EAAG,MAAM,CAAC;IAEhB,4BAA4B;IAE5B,QAAQ,CAAC,WAAW,SAAM;IAE1B,+DAA+D;IAE/D,QAAQ,CAAC,WAAW,SAAM;IAE1B,mGAAmG;IAEnG,QAAQ,CAAC,WAAW,SAAW;IAE/B,8EAA8E;IAO9E,QAAQ,CAAC,OAAO,EAAE,kBAAkB,EAAE,CAAM;IAE5C,8DAA8D;IAE9D,QAAQ,CAAC,aAAa,EAAE,SAAS,GAAG,SAAS,CAAa;IAE1D,sDAAsD;IAEtD,QAAQ,CAAC,gBAAgB,EAAE,KAAK,GAAG,QAAQ,CAAS;IAEpD,oCAAoC;IAEpC,QAAQ,CAAC,SAAS,UAAS;IAE3B,kDAAkD;IAElD,QAAQ,CAAC,WAAW,SAAM;IAE1B,OAAO,CAAC,WAAW,CAAC,CAAiB;IACrC,OAAO,CAAC,YAAY,CAAC,CAAc;IACnC,OAAO,CAAC,mBAAmB,CAAC,CAAuB;IACnD,OAAO,CAAC,uBAAuB,CAAS;IAExC,IAAa,gBAAgB,IAAI,WAAW,GAAG,SAAS,CAEvD;IAED,OAAO,KAAK,QAAQ,GAEnB;IAEQ,YAAY;IAkCZ,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC;IAMrC,oBAAoB;IAW7B,mFAAmF;IACnF,OAAO,IAAI,MAAM;IAMjB,mDAAmD;IACnD,OAAO,IAAI,WAAW;IAItB,0BAA0B;IAC1B,KAAK;IAII,KAAK;IAIL,IAAI;IAIb,UAAU;IAIV,YAAY;IAIZ,eAAe;IAIf,YAAY;IAIZ,eAAe;IAIf,aAAa,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC;IAI9B,gBAAgB;IAIhB,iBAAiB;IAIjB,gBAAgB;IAIhB,eAAe;IAIf,iBAAiB;IAIjB,IAAI;IAIJ,IAAI;IAIJ,UAAU;IAaD,iBAAiB;IAK1B,mFAAmF;IACnF,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,eAAe;IAQvB;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,QAAQ,CAId;IAEF,OAAO,CAAC,UAAU,CAMhB;IAIF;;;;;;;;;;;;;;;;;;OAkBG;IACH,OAAO,CAAC,yBAAyB,CAE/B;YAEY,mBAAmB;IAWjC,OAAO,CAAC,kBAAkB;IA0C1B;;;;;;OAMG;IACH,OAAO,CAAC,kBAAkB;IAe1B,OAAO,KAAK,YAAY,GAEvB;YAEa,oBAAoB;IAiBlC,OAAO,CAAC,aAAa;IA0BrB,OAAO,CAAC,kBAAkB;IA0IjB,MAAM;CAyBhB"}
@@ -15,11 +15,11 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
15
15
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
16
16
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
17
17
  };
18
- var _ProseEditor_initialHtml_accessor_storage, _ProseEditor_initialJson_accessor_storage, _ProseEditor_editorClass_accessor_storage, _ProseEditor_toolbar_accessor_storage, _ProseEditor_toolbarPreset_accessor_storage, _ProseEditor_toolbarPlacement_accessor_storage, _ProseEditor_autofocus_accessor_storage, _ProseEditor_placeholder_accessor_storage, _ProseEditor__emojiPickerActive_accessor_storage;
18
+ var _ProseEditor_initialHtml_accessor_storage, _ProseEditor_initialJson_accessor_storage, _ProseEditor_editorClass_accessor_storage, _ProseEditor_toolbar_accessor_storage, _ProseEditor_toolbarPreset_accessor_storage, _ProseEditor_toolbarPlacement_accessor_storage, _ProseEditor_autofocus_accessor_storage, _ProseEditor_placeholder_accessor_storage;
19
19
  import { unsafeCSS } from 'lit';
20
20
  import { html } from 'lit/static-html.js';
21
21
  import { staticTag } from '../../static-tag.js';
22
- import { property, state } from 'lit/decorators.js';
22
+ import { property } from 'lit/decorators.js';
23
23
  import { classMap } from 'lit/directives/class-map.js';
24
24
  import { map } from 'lit/directives/map.js';
25
25
  import { computePosition, flip, offset, shift } from '@floating-ui/dom';
@@ -102,15 +102,42 @@ export class ProseEditor extends LuxenFormAssociatedElement {
102
102
  _ProseEditor_toolbarPlacement_accessor_storage.set(this, 'top');
103
103
  _ProseEditor_autofocus_accessor_storage.set(this, false);
104
104
  _ProseEditor_placeholder_accessor_storage.set(this, '');
105
- _ProseEditor__emojiPickerActive_accessor_storage.set(this, false);
105
+ this._emojiOpenAtPointerDown = false;
106
106
  this._onFocus = () => {
107
107
  if (!this.disabled && document.activeElement !== this.editor?.view.dom) {
108
108
  this.focus();
109
109
  }
110
110
  };
111
111
  this._onKeyDown = (event) => {
112
- if (event.key === 'Escape')
113
- this._emojiPickerActive = false;
112
+ if (event.key !== 'Escape' || !this._emojiPicker?.matches(':popover-open'))
113
+ return;
114
+ this._emojiPicker.hidePopover();
115
+ // Consume the Escape so it dismisses only the picker, not the host dialog.
116
+ event.preventDefault();
117
+ event.stopPropagation();
118
+ };
119
+ // --- Emoji picker (lazy-loaded) ---
120
+ /**
121
+ * The picker is a `popover="auto"`, so the platform owns outside-click
122
+ * dismissal: native light-dismiss in the top layer, which survives
123
+ * `stopPropagation()` from an ancestor (modal `<l-dialog>`, ProseMirror, Vue
124
+ * delegation), unlike emoji-mart's `document` click listener. (`Escape` is
125
+ * handled by `_onKeyDown` instead — ProseMirror `preventDefault()`s it, which
126
+ * would cancel the native close.)
127
+ *
128
+ * We can't wire the button as a native popover invoker (`popoverTargetElement`
129
+ * / `popovertarget`) because the picker is parented into another shadow tree
130
+ * (the open `<dialog>` — see `_topLayerContainer`) while the button lives in
131
+ * this element's shadow root; a cross-tree invoker reference doesn't resolve.
132
+ * So the toggle is hand-rolled here. The catch: a pointer click on the button
133
+ * is "outside" the popover, so native light-dismiss has already closed an open
134
+ * picker by the time this `click` fires (light-dismiss runs on `pointerup`).
135
+ * We therefore read the open state captured at `pointerdown` to know whether
136
+ * this click should re-open or stay closed. Keyboard activation (`detail === 0`)
137
+ * has no pointer light-dismiss, so it reads the live state and toggles directly.
138
+ */
139
+ this._onEmojiButtonPointerDown = () => {
140
+ this._emojiOpenAtPointerDown = !!this._emojiPicker?.matches(':popover-open');
114
141
  };
115
142
  }
116
143
  /** Initial HTML content. */
@@ -137,8 +164,6 @@ export class ProseEditor extends LuxenFormAssociatedElement {
137
164
  /** Placeholder shown when the editor is empty. */
138
165
  get placeholder() { return __classPrivateFieldGet(this, _ProseEditor_placeholder_accessor_storage, "f"); }
139
166
  set placeholder(value) { __classPrivateFieldSet(this, _ProseEditor_placeholder_accessor_storage, value, "f"); }
140
- get _emojiPickerActive() { return __classPrivateFieldGet(this, _ProseEditor__emojiPickerActive_accessor_storage, "f"); }
141
- set _emojiPickerActive(value) { __classPrivateFieldSet(this, _ProseEditor__emojiPickerActive_accessor_storage, value, "f"); }
142
167
  get validationTarget() {
143
168
  return this._editorRoot;
144
169
  }
@@ -166,8 +191,14 @@ export class ProseEditor extends LuxenFormAssociatedElement {
166
191
  onTransaction: () => this.requestUpdate(),
167
192
  onSelectionUpdate: () => this.requestUpdate(),
168
193
  });
169
- document.addEventListener('keydown', this._onKeyDown);
170
194
  this.addEventListener('focus', this._onFocus);
195
+ // Escape is handled in JS, not by the popover's native close watcher:
196
+ // ProseMirror `preventDefault()`s Escape while the editor is focused, which
197
+ // cancels the native close (popover and dialog alike). Capture phase on
198
+ // `document` runs before that, can't be defeated by descendant
199
+ // `stopPropagation()`, and only needs the event to propagate — not its
200
+ // default action.
201
+ document.addEventListener('keydown', this._onKeyDown, true);
171
202
  this._syncValue();
172
203
  this.requestUpdate();
173
204
  }
@@ -175,14 +206,11 @@ export class ProseEditor extends LuxenFormAssociatedElement {
175
206
  if (changed.has('disabled') && this.editor) {
176
207
  this.editor.setEditable(!this.disabled);
177
208
  }
178
- if (changed.has('_emojiPickerActive')) {
179
- void this._positionEmojiPicker();
180
- }
181
209
  }
182
210
  disconnectedCallback() {
183
211
  super.disconnectedCallback();
184
- document.removeEventListener('keydown', this._onKeyDown);
185
212
  this.removeEventListener('focus', this._onFocus);
213
+ document.removeEventListener('keydown', this._onKeyDown, true);
186
214
  this.editor?.destroy();
187
215
  this._editorRoot?.remove();
188
216
  this._emojiPicker?.remove();
@@ -304,36 +332,45 @@ export class ProseEditor extends LuxenFormAssociatedElement {
304
332
  this._editorRoot = root;
305
333
  return root;
306
334
  }
307
- // --- Emoji picker (lazy-loaded) ---
308
- async _toggleEmojiPicker() {
309
- if (!this._emojiPicker) {
335
+ async _onEmojiButtonClick(event) {
336
+ const picker = await this._ensureEmojiPicker();
337
+ const wasOpen = event.detail === 0 ? picker.matches(':popover-open') : this._emojiOpenAtPointerDown;
338
+ if (wasOpen) {
339
+ if (picker.matches(':popover-open'))
340
+ picker.hidePopover();
341
+ }
342
+ else if (!picker.matches(':popover-open')) {
343
+ picker.showPopover();
344
+ }
345
+ }
346
+ _ensureEmojiPicker() {
347
+ if (this._emojiPicker)
348
+ return Promise.resolve(this._emojiPicker);
349
+ return (this._emojiPickerPromise ?? (this._emojiPickerPromise = (async () => {
310
350
  const [{ Picker }, { default: data }] = await Promise.all([
311
351
  import('emoji-mart'),
312
352
  import('@emoji-mart/data'),
313
353
  ]);
314
354
  const dark = window.matchMedia('(prefers-color-scheme: dark)').matches;
315
- this._emojiPicker = new Picker({
355
+ const picker = new Picker({
316
356
  parent: this._topLayerContainer(),
317
357
  data,
318
358
  theme: dark ? 'dark' : 'light',
319
359
  onEmojiSelect: ({ native }) => {
320
360
  this.editor.chain().focus().insertContent(native).run();
321
- this._emojiPickerActive = false;
322
- },
323
- onClickOutside: (event) => {
324
- if (!event.composedPath().includes(this._emojiButton)) {
325
- this._emojiPickerActive = false;
326
- }
361
+ picker.hidePopover();
327
362
  },
328
363
  });
329
- // A popover promotes the picker into the top-layer, so it paints above a
330
- // modal <l-dialog> and escapes any ancestor overflow clipping. Top-layer
331
- // alone isn't enough inside a modal: showModal() makes everything outside
332
- // the dialog's flat-tree subtree inert (visible but unclickable), so the
333
- // picker is parented to the nearest open <dialog> see _topLayerContainer.
334
- // Neutralize the UA popover box; Floating UI drives the position below.
335
- this._emojiPicker.setAttribute('popover', 'manual');
336
- Object.assign(this._emojiPicker.style, {
364
+ // A popover="auto" promotes the picker into the top-layer (so it paints
365
+ // above a modal <l-dialog> and escapes ancestor overflow clipping) and
366
+ // hands outside-click dismissal to the platform's native light-dismiss.
367
+ // Top-layer alone isn't enough inside a modal: showModal()
368
+ // makes everything outside the dialog's flat-tree subtree inert (visible
369
+ // but unclickable), so the picker is parented to the nearest open <dialog>
370
+ // — see _topLayerContainer. Neutralize the UA popover box; Floating UI
371
+ // drives the position in _positionEmojiPicker.
372
+ picker.setAttribute('popover', 'auto');
373
+ Object.assign(picker.style, {
337
374
  position: 'fixed',
338
375
  inset: 'auto',
339
376
  margin: '0',
@@ -341,8 +378,13 @@ export class ProseEditor extends LuxenFormAssociatedElement {
341
378
  border: '0',
342
379
  background: 'transparent',
343
380
  });
344
- }
345
- this._emojiPickerActive = !this._emojiPickerActive;
381
+ picker.addEventListener('toggle', (event) => {
382
+ if (event.newState === 'open')
383
+ void this._positionEmojiPicker();
384
+ });
385
+ this._emojiPicker = picker;
386
+ return picker;
387
+ })()));
346
388
  }
347
389
  /**
348
390
  * The element the emoji picker is appended to. Inside a modal `<l-dialog>` the
@@ -373,18 +415,11 @@ export class ProseEditor extends LuxenFormAssociatedElement {
373
415
  }
374
416
  async _positionEmojiPicker() {
375
417
  const button = this._emojiButton;
376
- if (!button || !this._emojiPicker)
377
- return;
378
418
  const picker = this._emojiPicker;
379
- if (!this._emojiPickerActive) {
380
- if (picker.matches(':popover-open'))
381
- picker.hidePopover();
419
+ // Runs from the picker's `toggle` event once it is open, so it is already
420
+ // laid out in the top-layer and can be measured.
421
+ if (!button || !picker || !picker.matches(':popover-open'))
382
422
  return;
383
- }
384
- // Show first so the picker is laid out (and in the top-layer) before we
385
- // measure it; a closed popover is display:none and would size to 0.
386
- if (!picker.matches(':popover-open'))
387
- picker.showPopover();
388
423
  const { x, y } = await computePosition(button, picker, {
389
424
  strategy: 'fixed',
390
425
  placement: 'bottom',
@@ -393,7 +428,7 @@ export class ProseEditor extends LuxenFormAssociatedElement {
393
428
  Object.assign(picker.style, { left: `${x}px`, top: `${y}px` });
394
429
  }
395
430
  // --- Rendering ---
396
- _renderButton(command, label, icon, onClick, active = false) {
431
+ _renderButton(command, label, icon, onClick, active = false, onPointerDown) {
397
432
  const iconTag = staticTag('icon');
398
433
  return html `
399
434
  <button
@@ -404,6 +439,7 @@ export class ProseEditor extends LuxenFormAssociatedElement {
404
439
  aria-label=${label}
405
440
  aria-pressed=${active}
406
441
  title=${label}
442
+ @pointerdown=${onPointerDown}
407
443
  @click=${onClick}
408
444
  >
409
445
  <${iconTag} name=${icon}></${iconTag}>
@@ -447,7 +483,7 @@ export class ProseEditor extends LuxenFormAssociatedElement {
447
483
  case 'link':
448
484
  return this._renderButton('link', 'Link', 'ri:link', () => this.toggleLink(), editor.isActive('link'));
449
485
  case 'emoji':
450
- return this._renderButton('emoji', 'Emoji', 'ri:emotion-line', () => void this._toggleEmojiPicker());
486
+ return this._renderButton('emoji', 'Emoji', 'ri:emotion-line', (event) => void this._onEmojiButtonClick(event), false, this._onEmojiButtonPointerDown);
451
487
  case 'attachment':
452
488
  return this._renderButton('attachment', 'Attach file', 'ri:attachment-2', () => this.emit('add-file'));
453
489
  case 'undo':
@@ -484,7 +520,7 @@ export class ProseEditor extends LuxenFormAssociatedElement {
484
520
  `;
485
521
  }
486
522
  }
487
- _ProseEditor_initialHtml_accessor_storage = new WeakMap(), _ProseEditor_initialJson_accessor_storage = new WeakMap(), _ProseEditor_editorClass_accessor_storage = new WeakMap(), _ProseEditor_toolbar_accessor_storage = new WeakMap(), _ProseEditor_toolbarPreset_accessor_storage = new WeakMap(), _ProseEditor_toolbarPlacement_accessor_storage = new WeakMap(), _ProseEditor_autofocus_accessor_storage = new WeakMap(), _ProseEditor_placeholder_accessor_storage = new WeakMap(), _ProseEditor__emojiPickerActive_accessor_storage = new WeakMap();
523
+ _ProseEditor_initialHtml_accessor_storage = new WeakMap(), _ProseEditor_initialJson_accessor_storage = new WeakMap(), _ProseEditor_editorClass_accessor_storage = new WeakMap(), _ProseEditor_toolbar_accessor_storage = new WeakMap(), _ProseEditor_toolbarPreset_accessor_storage = new WeakMap(), _ProseEditor_toolbarPlacement_accessor_storage = new WeakMap(), _ProseEditor_autofocus_accessor_storage = new WeakMap(), _ProseEditor_placeholder_accessor_storage = new WeakMap();
488
524
  ProseEditor.styles = [hostStyles, styles];
489
525
  __decorate([
490
526
  property({ attribute: 'initial-html' })
@@ -515,6 +551,3 @@ __decorate([
515
551
  __decorate([
516
552
  property()
517
553
  ], ProseEditor.prototype, "placeholder", null);
518
- __decorate([
519
- state()
520
- ], ProseEditor.prototype, "_emojiPickerActive", null);
@@ -20,7 +20,7 @@
20
20
  }
21
21
 
22
22
  .tree:focus-visible {
23
- outline: 2px solid var(--l-focus-ring, Highlight);
23
+ outline: 2px solid var(--l-focus-ring);
24
24
  outline-offset: 2px;
25
25
  border-radius: 0.375rem;
26
26
  }