@threadlabs/looma 0.31.1 → 0.31.3

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.
@@ -11155,6 +11155,26 @@ var TableHeader = Node$1.create({
11155
11155
  });
11156
11156
  //#endregion
11157
11157
  //#region src/editor/extensions/table-formatting.ts
11158
+ /** Table chrome lives outside the horizontal scroller, within the table's document block. */
11159
+ var LoomaTableView = class extends TableView {
11160
+ toolbarHost;
11161
+ constructor(node, cellMinWidth) {
11162
+ super(node, cellMinWidth);
11163
+ const scroller = this.dom;
11164
+ this.dom = document.createElement("div");
11165
+ this.dom.dataset.loomaTableBlock = "";
11166
+ this.toolbarHost = document.createElement("div");
11167
+ this.toolbarHost.dataset.loomaTableToolbarHost = "";
11168
+ this.toolbarHost.contentEditable = "false";
11169
+ this.dom.append(scroller, this.toolbarHost);
11170
+ }
11171
+ ignoreMutation(mutation) {
11172
+ return mutation.type !== "selection" && this.toolbarHost.contains(mutation.target) || super.ignoreMutation(mutation);
11173
+ }
11174
+ stopEvent(event) {
11175
+ return event.target instanceof globalThis.Node && this.toolbarHost.contains(event.target);
11176
+ }
11177
+ };
11158
11178
  /**
11159
11179
  * Looma's table behavior policy: resizable columns, stable minimum cell width,
11160
11180
  * and no independently resizable trailing column. The narrow handle is also a
@@ -11165,6 +11185,7 @@ var TableHeader = Node$1.create({
11165
11185
  */
11166
11186
  var LoomaTable = Table.configure({
11167
11187
  resizable: true,
11188
+ View: LoomaTableView,
11168
11189
  handleWidth: 3,
11169
11190
  cellMinWidth: 112,
11170
11191
  lastColumnResizable: false
@@ -17,17 +17,8 @@ export declare const LOOMA_TABLE_CONTROLS_CLOSED: LoomaTableControls;
17
17
  * with nothing selected; narrow screens use the editing dock instead of the toolbar and overlay.
18
18
  */
19
19
  export declare function tableControls(state: ActiveTableUiState, selectionInTable: boolean, narrow: boolean): LoomaTableControls;
20
- /**
21
- * Centers the table toolbar on the table, at most 420px wide and 12px inside the viewport's sides,
22
- * above the table when there is room and below it otherwise.
23
- */
24
- export declare function tableToolbarPlacement(table: DOMRect, viewportWidth: number): {
25
- top: string;
26
- left: string;
27
- maxWidth: string;
28
- };
29
- /** The overlay covers the table exactly. */
30
- export declare const tableOverlayPlacement: (table: DOMRect) => {
20
+ /** Inline controls cover the table in the editor's coordinate system. */
21
+ export declare const tableOverlayPlacement: (table: DOMRect, editor: DOMRect) => {
31
22
  top: string;
32
23
  left: string;
33
24
  width: string;
@@ -22,4 +22,4 @@ export interface LoomaToolbarTooltip {
22
22
  destroy(): void;
23
23
  }
24
24
  /** Calls `onChange` after every state change. */
25
- export declare function createToolbarTooltip(onChange: (state: LoomaToolbarTooltipState) => void): LoomaToolbarTooltip;
25
+ export declare function createToolbarTooltip(onChange: (state: LoomaToolbarTooltipState) => void, delay?: () => number): LoomaToolbarTooltip;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threadlabs/looma",
3
- "version": "0.31.1",
3
+ "version": "0.31.3",
4
4
  "type": "module",
5
5
  "description": "Looma components as HTML, Vue, and plain DOM, with design tokens.",
6
6
  "license": "MIT",
@@ -64,6 +64,7 @@
64
64
  }
65
65
 
66
66
  :scope:where([data-component~="ui-editor-toolbar"])[data-ui-editor-toolbar-state~="floating"] {
67
+ box-sizing: border-box;
67
68
  position: relative;
68
69
  top: auto;
69
70
  width: max-content;
@@ -70,6 +70,15 @@
70
70
  box-shadow: var(--_ui-popover-shadow, var(--ui-overlay-shadow, var(--ui-elevation-overlay, var(--ui-shadow-lg))));
71
71
  }
72
72
 
73
+ /* A toolbar or card can supply its own surface, including an unclipped shadow. */
74
+ :scope:where([data-component~="ui-popover"])[data-ui-popover-state~="bare"] .surface {
75
+ overflow: visible;
76
+ padding: 0;
77
+ border: 0;
78
+ background: transparent;
79
+ box-shadow: none;
80
+ }
81
+
73
82
  :scope:where([data-component~="ui-popover"]):not([data-ui-popover-state~="internalOpen"]) {
74
83
  display: none;
75
84
  }
@@ -125,6 +134,8 @@
125
134
  @scope ([data-component~="ui-popover"]) to ([data-component]) {
126
135
  /* This component's hooks style the element they are set on, and never a nested one. */
127
136
 
137
+ /* A toolbar or card can supply its own surface, including an unclipped shadow. */
138
+
128
139
  @starting-style {
129
140
  }
130
141
 
@@ -12,6 +12,7 @@ export interface UiPopoverElement extends HTMLSpanElement {
12
12
  removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
13
13
  }
14
14
  export interface UiPopoverProps {
15
+ bare?: boolean | null;
15
16
  for?: string | null;
16
17
  open?: boolean | null;
17
18
  placement?: "top" | "top-start" | "top-end" | "bottom" | "bottom-start" | "bottom-end" | "left" | "left-start" | "left-end" | "right" | "right-start" | "right-end" | null;
@@ -7,7 +7,7 @@ const T0 = ["",[],["div",["class","surface"],4]];
7
7
  const T1 = ["",[]];
8
8
  let P0, P1;
9
9
  const S = { n: ["internalOpen"], t: ["b"], f: import.meta.url, g: "ui-popover", d: { "open": [detailCheck(checkObject([["open", checkBoolean, false], ["reason", checkConstrained(checkFormat("keyword", keywordFormat), "keyword", {"values":["action","programmatic","light-dismiss","escape"]}), false], ["trigger", checkConstrained(checkFormat("keyword", keywordFormat), "keyword", {"values":["keyboard","pointer","programmatic"]}), false]], false)), true, true, false], "close": [detailCheck(checkObject([["open", checkBoolean, false], ["reason", checkConstrained(checkFormat("keyword", keywordFormat), "keyword", {"values":["action","programmatic","light-dismiss","escape"]}), false], ["trigger", checkConstrained(checkFormat("keyword", keywordFormat), "keyword", {"values":["keyboard","pointer","programmatic"]}), false]], false)), true, true, false] }, x: dispatchDeclared, a: (host, I) => C(host, I), b: generatedInputs, i: [false] };
10
- const D = { g: "ui-popover", props: { "for": { "required":false,"default":"", c: checkString, t: "string", j: 0, m: "s" }, "open": { "required":false,"default":false, c: checkBoolean, t: "boolean", j: 0, m: "b" }, "placement": { "required":false,"values":["top","top-start","top-end","bottom","bottom-start","bottom-end","left","left-start","left-end","right","right-start","right-end"],"default":"bottom-start", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat)] }, "size": { "required":false,"values":["content","sm","md","lg"],"default":"content", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat)] } } };
10
+ const D = { g: "ui-popover", props: { "bare": { "required":false,"default":false, c: checkBoolean, t: "boolean", j: 0, m: "b" }, "for": { "required":false,"default":"", c: checkString, t: "string", j: 0, m: "s" }, "open": { "required":false,"default":false, c: checkBoolean, t: "boolean", j: 0, m: "b" }, "placement": { "required":false,"values":["top","top-start","top-end","bottom","bottom-start","bottom-end","left","left-start","left-end","right","right-start","right-end"],"default":"bottom-start", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat)] }, "size": { "required":false,"values":["content","sm","md","lg"],"default":"content", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat)] } } };
11
11
  const C = (host) => activateModule(controller, host);
12
12
 
13
13
  export function createUiPopover(options = {}, html) {
@@ -19,7 +19,7 @@ export function createUiPopover(options = {}, html) {
19
19
  }
20
20
  element.setAttribute("data-component", "ui-popover");
21
21
  element.append((P0 ??= buildTemplate(T0)).cloneNode(true));
22
- const v = [false, null, null, null, null];
22
+ const v = [false, null, null, null, null, null];
23
23
  const B = acceptProps(element, D, S.n, v, componentProps, [], html);
24
24
  const m1 = (d) => {
25
25
  const n = (P1 ??= buildTemplate(T1)).cloneNode(true);
@@ -30,8 +30,8 @@ export function createUiPopover(options = {}, html) {
30
30
  const p1 = (r, c, d) => {
31
31
  };
32
32
  const p0 = (r, c, d) => {
33
- if (c & 17) {
34
- const x0 = hostState(["internalOpen","size"], [v[0], v[4]]);
33
+ if (c & 35) {
34
+ const x0 = hostState(["bare","internalOpen","size"], [v[1], v[0], v[5]]);
35
35
  const s0 = x0;
36
36
  if (s0 !== r.v0) writeAttribute(r.n, "data-ui-popover-state", (r.v0 = s0) === "" ? null : s0);
37
37
  }
@@ -48,4 +48,4 @@ export function createUiPopover(options = {}, html) {
48
48
  manageProps(I);
49
49
  return element;
50
50
  }
51
- defineComponent("ui-popover", createUiPopover, ["for","open","placement","size"]);
51
+ defineComponent("ui-popover", createUiPopover, ["bare","for","open","placement","size"]);
@@ -217,6 +217,7 @@ useComponentHost(() => import('../components/ui-editor-toolbar/ui-editor-toolbar
217
217
  }
218
218
 
219
219
  :global(:scope[data-ui-editor-toolbar-state~="floating"]:where([data-hn-ui-editor-toolbar])) {
220
+ box-sizing: border-box;
220
221
  position: relative;
221
222
  top: auto;
222
223
  width: max-content;
@@ -5,6 +5,9 @@ type __VLS_Slots = {} & {
5
5
  default?: (props: typeof __VLS_1) => any;
6
6
  };
7
7
  declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
8
+ bare: {
9
+ type: PropType<boolean | null>;
10
+ };
8
11
  for: {
9
12
  type: PropType<string | null>;
10
13
  };
@@ -33,6 +36,9 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
33
36
  }>) => any;
34
37
  "update:open": (value: boolean) => any;
35
38
  }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
39
+ bare: {
40
+ type: PropType<boolean | null>;
41
+ };
36
42
  for: {
37
43
  type: PropType<string | null>;
38
44
  };
package/vue/UiPopover.vue CHANGED
@@ -27,6 +27,14 @@ defineOptions({ inheritAttrs: false })
27
27
 
28
28
  const propValidityContract = {
29
29
  props: {
30
+ bare: {
31
+ type: { kind: 'terminal', name: 'boolean' },
32
+ required: false,
33
+ target: { attribute: 'bare' },
34
+ description:
35
+ 'Delegates the surface to its content, with no padding, border, background, shadow, or overflow clipping.',
36
+ default: false,
37
+ },
30
38
  for: {
31
39
  type: { kind: 'terminal', name: 'string' },
32
40
  required: false,
@@ -75,6 +83,7 @@ const propValidityContract = {
75
83
  },
76
84
  } as const
77
85
  const props = defineProps({
86
+ bare: { type: null as unknown as PropType<boolean | null> },
78
87
  for: { type: null as unknown as PropType<string | null> },
79
88
  open: { type: null as unknown as PropType<boolean | null> },
80
89
  placement: {
@@ -98,18 +107,22 @@ const props = defineProps({
98
107
  [BINDING_INPUTS_PROP]: { type: null as unknown as PropType<InputBindings> },
99
108
  })
100
109
  const acceptedProps: Record<string, unknown> = {
110
+ bare: false,
101
111
  for: '',
102
112
  open: false,
103
113
  placement: 'bottom-start',
104
114
  size: 'content',
105
115
  }
106
116
  const inputTracking = vuePropInputs(acceptedProps, {
117
+ bare: false,
107
118
  for: '',
108
119
  open: false,
109
120
  placement: 'bottom-start',
110
121
  size: 'content',
111
122
  }, (name, value, source, values) => {
112
123
  switch (name) {
124
+ case 'bare':
125
+ return checkPropInput(value, { kind: 'terminal', name: 'boolean' }, true, true)
113
126
  case 'for':
114
127
  return checkPropInput(value, { kind: 'terminal', name: 'string' }, true, true)
115
128
  case 'open':
@@ -128,6 +141,11 @@ const propInputs = inputTracking.inputs
128
141
  const checkedProps = computed(() => {
129
142
  void inputTracking.version.value
130
143
  return ({
144
+ bare: propInputs.value<boolean | null>(
145
+ 'bare',
146
+ props['bare'],
147
+ props[BINDING_INPUTS_PROP],
148
+ ) as boolean,
131
149
  for: propInputs.value<string | null>('for', props['for'], props[BINDING_INPUTS_PROP]) as string,
132
150
  open: propInputs.value<boolean | null>(
133
151
  'open',
@@ -209,6 +227,7 @@ checkHydratedRoot('span', 'ui-popover')
209
227
  /** The values the styles' :host() and :state() rules test. */
210
228
  const hostState = computed(() =>
211
229
  [
230
+ checkedProps.value.bare && 'bare',
212
231
  internalOpen.value && 'internalOpen',
213
232
  checkedProps.value.size && 'size',
214
233
  checkedProps.value.size != null && `size=${encodeURIComponent(checkedProps.value.size)}`,
@@ -260,7 +279,7 @@ useComponentHost(() => import('../components/ui-popover/ui-popover.js'), {
260
279
  ? (incoming[name] ?? incoming[kebab] ?? null)
261
280
  : null
262
281
  },
263
- propNames: ['for', 'open', 'placement', 'size'],
282
+ propNames: ['bare', 'for', 'open', 'placement', 'size'],
264
283
  propValidity: (name: string) =>
265
284
  propValidityState({ contract: propValidityContract, values: { ...props } }, name),
266
285
  state: { internalOpen },
@@ -274,6 +293,7 @@ useComponentHost(() => import('../components/ui-popover/ui-popover.js'), {
274
293
  data-hn
275
294
  data-hn-ui-popover
276
295
  v-prop-validity="{ contract: propValidityContract, values: { ...props } }"
296
+ :data-ui-popover-prop-bare="reflectedProp('bare', 'bare', checkedProps.bare, undefined, false)"
277
297
  :data-ui-popover-prop-for="reflectedProp('for', 'for', checkedProps.for, undefined, false)"
278
298
  :data-ui-popover-prop-open="reflectedProp('open', 'open', checkedProps.open, undefined, false)"
279
299
  :data-ui-popover-prop-placement="reflectedProp('placement', 'placement', checkedProps.placement, undefined, false)"
@@ -313,6 +333,9 @@ useComponentHost(() => import('../components/ui-popover/ui-popover.js'), {
313
333
  syntax: "*";
314
334
  inherits: false;
315
335
  }
336
+
337
+ /* A toolbar or card can supply its own surface, including an unclipped shadow. */
338
+
316
339
  @scope (.ui-popover) to ([data-hn]) {
317
340
  :global(:scope:where([data-hn-ui-popover])) {
318
341
  --_ui-popover-surface: var(--ui-popover-surface);
@@ -350,6 +373,13 @@ useComponentHost(() => import('../components/ui-popover/ui-popover.js'), {
350
373
  var(--ui-overlay-shadow, var(--ui-elevation-overlay, var(--ui-shadow-lg)))
351
374
  );
352
375
  }
376
+ :global(:scope[data-ui-popover-state~="bare"] .surface:where([data-hn-ui-popover])) {
377
+ overflow: visible;
378
+ padding: 0;
379
+ border: 0;
380
+ background: transparent;
381
+ box-shadow: none;
382
+ }
353
383
 
354
384
  :global(:scope:not([data-ui-popover-state~="internalOpen"]):where([data-hn-ui-popover])) {
355
385
  display: none;
@@ -98,6 +98,6 @@ var UiEditorToolbar_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
98
98
  }]]);
99
99
  };
100
100
  }
101
- }), [["__scopeId", "data-v-01110420"]]);
101
+ }), [["__scopeId", "data-v-a75db733"]]);
102
102
  //#endregion
103
103
  export { UiEditorToolbar_default as t };
@@ -3,6 +3,7 @@ import { f as vPropValidity, i as checkPropInput, l as propValidityState, p as v
3
3
  import { computed, createElementBlock, createElementVNode, defineComponent, getCurrentInstance, mergeProps, openBlock, provide, ref, renderSlot, unref, watchSyncEffect, withDirectives } from "vue";
4
4
  //#region .build/vue/UiPopover.vue?vue&type=script&setup=true&lang.ts
5
5
  var _hoisted_1 = [
6
+ "data-ui-popover-prop-bare",
6
7
  "data-ui-popover-prop-for",
7
8
  "data-ui-popover-prop-open",
8
9
  "data-ui-popover-prop-placement",
@@ -19,6 +20,7 @@ var UiPopover_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PU
19
20
  inheritAttrs: false,
20
21
  __name: "UiPopover",
21
22
  props: {
23
+ bare: { type: null },
22
24
  for: { type: null },
23
25
  open: { type: null },
24
26
  placement: { type: null },
@@ -32,6 +34,16 @@ var UiPopover_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PU
32
34
  ],
33
35
  setup(__props, { emit: __emit }) {
34
36
  const propValidityContract = { props: {
37
+ bare: {
38
+ type: {
39
+ kind: "terminal",
40
+ name: "boolean"
41
+ },
42
+ required: false,
43
+ target: { attribute: "bare" },
44
+ description: "Delegates the surface to its content, with no padding, border, background, shadow, or overflow clipping.",
45
+ default: false
46
+ },
35
47
  for: {
36
48
  type: {
37
49
  kind: "terminal",
@@ -95,17 +107,23 @@ var UiPopover_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PU
95
107
  } };
96
108
  const props = __props;
97
109
  const inputTracking = vuePropInputs({
110
+ bare: false,
98
111
  for: "",
99
112
  open: false,
100
113
  placement: "bottom-start",
101
114
  size: "content"
102
115
  }, {
116
+ bare: false,
103
117
  for: "",
104
118
  open: false,
105
119
  placement: "bottom-start",
106
120
  size: "content"
107
121
  }, (name, value, source, values) => {
108
122
  switch (name) {
123
+ case "bare": return checkPropInput(value, {
124
+ kind: "terminal",
125
+ name: "boolean"
126
+ }, true, true);
109
127
  case "for": return checkPropInput(value, {
110
128
  kind: "terminal",
111
129
  name: "string"
@@ -131,6 +149,7 @@ var UiPopover_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PU
131
149
  const checkedProps = computed(() => {
132
150
  inputTracking.version.value;
133
151
  return {
152
+ bare: propInputs.value("bare", props["bare"], props[BINDING_INPUTS_PROP]),
134
153
  for: propInputs.value("for", props["for"], props[BINDING_INPUTS_PROP]),
135
154
  open: propInputs.value("open", props["open"], props[BINDING_INPUTS_PROP]),
136
155
  placement: propInputs.value("placement", props["placement"], props[BINDING_INPUTS_PROP]),
@@ -150,6 +169,7 @@ var UiPopover_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PU
150
169
  checkHydratedRoot("span", "ui-popover");
151
170
  /** The values the styles' :host() and :state() rules test. */
152
171
  const hostState = computed(() => [
172
+ checkedProps.value.bare && "bare",
153
173
  internalOpen.value && "internalOpen",
154
174
  checkedProps.value.size && "size",
155
175
  checkedProps.value.size != null && `size=${encodeURIComponent(checkedProps.value.size)}`
@@ -188,6 +208,7 @@ var UiPopover_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PU
188
208
  return incoming !== null && incoming !== void 0 ? incoming[name] ?? incoming[kebab] ?? null : null;
189
209
  },
190
210
  propNames: [
211
+ "bare",
191
212
  "for",
192
213
  "open",
193
214
  "placement",
@@ -203,6 +224,7 @@ var UiPopover_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PU
203
224
  return withDirectives((openBlock(), createElementBlock("span", mergeProps({ class: "ui-popover" }, unref(nativeAttrs)(_ctx.$attrs), {
204
225
  "data-hn": "",
205
226
  "data-hn-ui-popover": "",
227
+ "data-ui-popover-prop-bare": unref(reflectedProp)("bare", "bare", checkedProps.value.bare, void 0, false),
206
228
  "data-ui-popover-prop-for": unref(reflectedProp)("for", "for", checkedProps.value.for, void 0, false),
207
229
  "data-ui-popover-prop-open": unref(reflectedProp)("open", "open", checkedProps.value.open, void 0, false),
208
230
  "data-ui-popover-prop-placement": unref(reflectedProp)("placement", "placement", checkedProps.value.placement, void 0, false),
@@ -216,6 +238,6 @@ var UiPopover_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PU
216
238
  }]]);
217
239
  };
218
240
  }
219
- }), [["__scopeId", "data-v-b924204e"]]);
241
+ }), [["__scopeId", "data-v-b4881cc1"]]);
220
242
  //#endregion
221
243
  export { UiPopover_default as t };
@@ -4843,6 +4843,7 @@ legend.sr-only:where([data-hn-ui-radio-group]) {
4843
4843
  margin: 0 var(--ui-editor-toolbar-divider-margin, 4px);
4844
4844
  }
4845
4845
  :scope[data-ui-editor-toolbar-state~="floating"]:where([data-hn-ui-editor-toolbar]) {
4846
+ box-sizing: border-box;
4846
4847
  position: relative;
4847
4848
  top: auto;
4848
4849
  width: max-content;
@@ -5389,6 +5390,8 @@ html[data-ui-input-modality="touch"] :scope:where([data-hn-ui-icon-button])::aft
5389
5390
  syntax: "*";
5390
5391
  inherits: false;
5391
5392
  }
5393
+
5394
+ /* A toolbar or card can supply its own surface, including an unclipped shadow. */
5392
5395
  @scope (.ui-popover) to ([data-hn]) {
5393
5396
  :scope:where([data-hn-ui-popover]) {
5394
5397
  --_ui-popover-surface: var(--ui-popover-surface);
@@ -5425,6 +5428,13 @@ html[data-ui-input-modality="touch"] :scope:where([data-hn-ui-icon-button])::aft
5425
5428
  var(--ui-overlay-shadow, var(--ui-elevation-overlay, var(--ui-shadow-lg)))
5426
5429
  );
5427
5430
  }
5431
+ :scope[data-ui-popover-state~="bare"] .surface:where([data-hn-ui-popover]) {
5432
+ overflow: visible;
5433
+ padding: 0;
5434
+ border: 0;
5435
+ background: transparent;
5436
+ box-shadow: none;
5437
+ }
5428
5438
  :scope:not([data-ui-popover-state~="internalOpen"]):where([data-hn-ui-popover]) {
5429
5439
  display: none;
5430
5440
  }
@@ -10438,6 +10448,30 @@ html[data-ui-input-modality="touch"]
10438
10448
  :is([data-component~="ui-editor-surface"], .ui-editor-surface) .ProseMirror[data-link-navigation] a[href] {
10439
10449
  cursor: pointer;
10440
10450
  }
10451
+
10452
+ /* Inline table affordances share the document's scroll chain; popups use the top layer. */
10453
+ .looma-editor__table-overlay-shell {
10454
+ position: absolute;
10455
+ z-index: var(--ui-editor-overlay-z-index, 299);
10456
+ overflow: visible;
10457
+ pointer-events: none;
10458
+ }
10459
+
10460
+ /* The toolbar follows its table, lifting inside the document scroller only when needed. */
10461
+ [data-looma-table-toolbar-host] { display: contents; }
10462
+ .looma-editor__table-toolbar-shell {
10463
+ position: sticky;
10464
+ bottom: var(--ui-space-3);
10465
+ z-index: var(--ui-editor-popover-z-index, 300);
10466
+ width: max-content;
10467
+ max-width: 100%;
10468
+ margin: var(--ui-space-3) auto 0;
10469
+ overflow-x: auto;
10470
+ }
10471
+
10472
+ .looma-editor .ProseMirror [data-looma-table-block] + p {
10473
+ margin-top: var(--ui-editor-table-following-gap, var(--ui-space-4));
10474
+ }
10441
10475
  /**
10442
10476
  * Looma editor styles (tokens-based).
10443
10477
  * Tables, context menu, insert-table grid, slash menu. Use with ProseMirror/Tiptap content.
@@ -10738,7 +10772,6 @@ html[data-ui-input-modality="touch"]
10738
10772
 
10739
10773
  .looma-editor__table-picker-popover,
10740
10774
  .looma-editor__chip-popover,
10741
- .looma-editor__table-toolbar-shell,
10742
10775
  .looma-editor__table-menu-shell {
10743
10776
  z-index: var(--ui-editor-popover-z-index, 300);
10744
10777
  }
@@ -10750,15 +10783,6 @@ html[data-ui-input-modality="touch"]
10750
10783
  background: transparent;
10751
10784
  }
10752
10785
 
10753
- /* The floating toolbar supplies the surface; the containing popover only
10754
- positions it beside the app-owned trigger. */
10755
- .looma-editor__formatting-popover {
10756
- --ui-popover-padding: 0;
10757
- --ui-popover-surface: transparent;
10758
- --ui-popover-border: transparent;
10759
- --ui-popover-shadow: none;
10760
- }
10761
-
10762
10786
  .looma-editor__chip-popover {
10763
10787
  inline-size: min(15rem, calc(100vw - 2rem));
10764
10788
  }
@@ -10768,9 +10792,7 @@ html[data-ui-input-modality="touch"]
10768
10792
  outline-offset: var(--ui-focus-width);
10769
10793
  }
10770
10794
 
10771
- .looma-editor__table-toolbar-shell,
10772
- .looma-editor__table-menu-shell,
10773
- .looma-editor__table-overlay-shell {
10795
+ .looma-editor__table-menu-shell {
10774
10796
  position: fixed;
10775
10797
  }
10776
10798
 
@@ -10836,12 +10858,6 @@ html[data-ui-input-modality="touch"]
10836
10858
  color: var(--ui-text-primary, #111827);
10837
10859
  }
10838
10860
 
10839
- .looma-editor__table-overlay-shell {
10840
- z-index: var(--ui-editor-overlay-z-index, 299);
10841
- overflow: visible;
10842
- pointer-events: none;
10843
- }
10844
-
10845
10861
  .looma-editor .ProseMirror .looma-mention {
10846
10862
  padding: 0.08em 0.28em;
10847
10863
  color: var(--ui-editor-mention-color, var(--ui-accent-active, var(--ui-accent-solid)));
@@ -12862,6 +12878,15 @@ html[data-ui-input-modality="touch"]
12862
12878
  box-shadow: var(--_ui-popover-shadow, var(--ui-overlay-shadow, var(--ui-elevation-overlay, var(--ui-shadow-lg))));
12863
12879
  }
12864
12880
 
12881
+ /* A toolbar or card can supply its own surface, including an unclipped shadow. */
12882
+ :scope:where([data-component~="ui-popover"])[data-ui-popover-state~="bare"] .surface {
12883
+ overflow: visible;
12884
+ padding: 0;
12885
+ border: 0;
12886
+ background: transparent;
12887
+ box-shadow: none;
12888
+ }
12889
+
12865
12890
  :scope:where([data-component~="ui-popover"]):not([data-ui-popover-state~="internalOpen"]) {
12866
12891
  display: none;
12867
12892
  }
@@ -12917,6 +12942,8 @@ html[data-ui-input-modality="touch"]
12917
12942
  @scope ([data-component~="ui-popover"]) to ([data-component]) {
12918
12943
  /* This component's hooks style the element they are set on, and never a nested one. */
12919
12944
 
12945
+ /* A toolbar or card can supply its own surface, including an unclipped shadow. */
12946
+
12920
12947
  @starting-style {
12921
12948
  }
12922
12949