@yewdiel21/motion-kit 0.14.0 → 0.16.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.
- package/README.md +178 -155
- package/bin/motion-kit.mjs +3 -3
- package/dist/auto/index.d.ts +6 -3
- package/dist/auto/index.js +8 -2
- package/dist/auto/index.js.map +1 -1
- package/dist/{chunk-EY4BJKC4.js → chunk-NFZIX3IA.js} +2 -2
- package/dist/chunk-RBEKR5UZ.js +3660 -0
- package/dist/chunk-RBEKR5UZ.js.map +1 -0
- package/dist/{chunk-CU6SXW3E.js → chunk-SDQ776LG.js} +1596 -485
- package/dist/chunk-SDQ776LG.js.map +1 -0
- package/dist/css/components/add-to-cart.css +72 -0
- package/dist/css/components/bidirectional-slider.css +168 -0
- package/dist/css/components/chart.css +7 -0
- package/dist/css/components/data-table.css +121 -26
- package/dist/css/components/filter-builder.css +10 -15
- package/dist/css/components/heatmap-chart.css +398 -0
- package/dist/css/components/hover-card.css +8 -2
- package/dist/css/components/image-preview.css +90 -2
- package/dist/css/components/json-viewer.css +33 -1
- package/dist/css/components/nav.css +102 -102
- package/dist/css/components/orbit-menu.css +130 -0
- package/dist/css/components/pagination.css +10 -5
- package/dist/css/components/pie-chart.css +397 -0
- package/dist/css/components/pill-button.css +135 -0
- package/dist/css/components/radar-chart.css +377 -0
- package/dist/css/components/radial-chart.css +412 -0
- package/dist/css/components/sankey-chart.css +365 -0
- package/dist/css/components/scroll-island.css +114 -0
- package/dist/css/components/select.css +136 -0
- package/dist/css/components/toolbar.css +122 -0
- package/dist/css/components/tree-view.css +79 -0
- package/dist/css/components/voice-dots.css +70 -0
- package/dist/elements/index.d.ts +180 -7
- package/dist/elements/index.js +293 -7
- package/dist/elements/index.js.map +1 -1
- package/dist/{engine-Cn6IhE3z.d.ts → engine-5zRIssTO.d.ts} +11 -1
- package/dist/index.css +1849 -168
- package/dist/index.d.ts +4 -4
- package/dist/index.js +33 -3
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.ts +117 -11
- package/dist/react/index.js +320 -13
- package/dist/react/index.js.map +1 -1
- package/dist/{reduced-BBUusld6.d.ts → reduced-DQwVXQYb.d.ts} +1 -1
- package/dist/{reveal-DEvp6U6T.d.ts → reveal-DRpNO4KP.d.ts} +109 -4
- package/dist/store-Da3zSh8Z.d.ts +457 -0
- package/package.json +9 -16
- package/registry.json +1096 -948
- package/src/auto/auto-init.ts +6 -0
- package/src/components/add-to-cart/README.md +49 -0
- package/src/components/add-to-cart/add-to-cart.css +71 -0
- package/src/components/add-to-cart/element.ts +20 -0
- package/src/components/add-to-cart/engine.ts +206 -0
- package/src/components/add-to-cart/react.tsx +33 -0
- package/src/components/bidirectional-slider/README.md +46 -0
- package/src/components/bidirectional-slider/bidirectional-slider.css +167 -0
- package/src/components/bidirectional-slider/element.ts +42 -0
- package/src/components/bidirectional-slider/engine.ts +289 -0
- package/src/components/bidirectional-slider/react.tsx +45 -0
- package/src/components/chart/README.md +49 -24
- package/src/components/chart/chart.css +7 -0
- package/src/components/chart/element.ts +4 -1
- package/src/components/chart/engine.ts +153 -34
- package/src/components/chart/parts.ts +237 -0
- package/src/components/chart/react.tsx +2 -2
- package/src/components/chart/use-chart.ts +55 -0
- package/src/components/data-table/README.md +3 -3
- package/src/components/data-table/data-table.css +121 -26
- package/src/components/data-table/engine.ts +81 -29
- package/src/components/editable-table/engine.ts +67 -11
- package/src/components/filter-builder/engine.ts +33 -2
- package/src/components/filter-builder/filter-builder.css +10 -15
- package/src/components/heatmap-chart/README.md +48 -0
- package/src/components/heatmap-chart/element.ts +21 -0
- package/src/components/heatmap-chart/engine.ts +281 -0
- package/src/components/heatmap-chart/heatmap-chart.css +93 -0
- package/src/components/heatmap-chart/react.tsx +14 -0
- package/src/components/hover-card/engine.ts +30 -1
- package/src/components/hover-card/hover-card.css +8 -2
- package/src/components/image-preview/element.ts +1 -1
- package/src/components/image-preview/engine.ts +95 -15
- package/src/components/image-preview/image-preview.css +90 -2
- package/src/components/image-preview/react.tsx +3 -3
- package/src/components/json-viewer/engine.ts +11 -2
- package/src/components/json-viewer/json-viewer.css +33 -1
- package/src/components/nav/README.md +12 -10
- package/src/components/nav/element.ts +5 -5
- package/src/components/nav/engine.ts +21 -14
- package/src/components/nav/nav.css +102 -102
- package/src/components/nav/react.tsx +4 -4
- package/src/components/orbit-menu/README.md +46 -0
- package/src/components/orbit-menu/element.ts +27 -0
- package/src/components/orbit-menu/engine.ts +179 -0
- package/src/components/orbit-menu/orbit-menu.css +129 -0
- package/src/components/orbit-menu/react.tsx +44 -0
- package/src/components/pagination/README.md +1 -1
- package/src/components/pagination/engine.ts +50 -4
- package/src/components/pagination/pagination.css +10 -5
- package/src/components/pie-chart/README.md +48 -0
- package/src/components/pie-chart/element.ts +30 -0
- package/src/components/pie-chart/engine.ts +283 -0
- package/src/components/pie-chart/pie-chart.css +92 -0
- package/src/components/pie-chart/react.tsx +14 -0
- package/src/components/pill-button/README.md +46 -0
- package/src/components/pill-button/element.ts +16 -0
- package/src/components/pill-button/engine.ts +77 -0
- package/src/components/pill-button/pill-button.css +134 -0
- package/src/components/pill-button/react.tsx +32 -0
- package/src/components/radar-chart/README.md +48 -0
- package/src/components/radar-chart/element.ts +30 -0
- package/src/components/radar-chart/engine.ts +327 -0
- package/src/components/radar-chart/radar-chart.css +72 -0
- package/src/components/radar-chart/react.tsx +14 -0
- package/src/components/radial-chart/README.md +58 -0
- package/src/components/radial-chart/element.ts +30 -0
- package/src/components/radial-chart/engine.ts +317 -0
- package/src/components/radial-chart/radial-chart.css +107 -0
- package/src/components/radial-chart/react.tsx +14 -0
- package/src/components/sankey-chart/README.md +48 -0
- package/src/components/sankey-chart/element.ts +31 -0
- package/src/components/sankey-chart/engine.ts +401 -0
- package/src/components/sankey-chart/react.tsx +14 -0
- package/src/components/sankey-chart/sankey-chart.css +60 -0
- package/src/components/scroll-island/README.md +44 -0
- package/src/components/scroll-island/element.ts +16 -0
- package/src/components/scroll-island/engine.ts +274 -0
- package/src/components/scroll-island/react.tsx +26 -0
- package/src/components/scroll-island/scroll-island.css +113 -0
- package/src/components/select/README.md +34 -0
- package/src/components/select/element.ts +26 -0
- package/src/components/select/engine.ts +267 -0
- package/src/components/select/react.tsx +33 -0
- package/src/components/select/select.css +135 -0
- package/src/components/toolbar/README.md +46 -0
- package/src/components/toolbar/element.ts +30 -0
- package/src/components/toolbar/engine.ts +230 -0
- package/src/components/toolbar/react.tsx +34 -0
- package/src/components/toolbar/toolbar.css +121 -0
- package/src/components/tree-view/element.ts +1 -1
- package/src/components/tree-view/engine.ts +174 -0
- package/src/components/tree-view/react.tsx +7 -3
- package/src/components/tree-view/tree-view.css +79 -0
- package/src/components/voice-dots/README.md +48 -0
- package/src/components/voice-dots/element.ts +45 -0
- package/src/components/voice-dots/engine.ts +243 -0
- package/src/components/voice-dots/react.tsx +52 -0
- package/src/components/voice-dots/voice-dots.css +69 -0
- package/src/elements/index.ts +39 -0
- package/src/index.ts +16 -0
- package/src/motion/liquid.ts +14 -4
- package/src/react/index.ts +14 -1
- package/dist/chunk-6P4P7NU6.js +0 -1401
- package/dist/chunk-6P4P7NU6.js.map +0 -1
- package/dist/chunk-CU6SXW3E.js.map +0 -1
- package/dist/store-DjkTBX7f.d.ts +0 -195
- /package/dist/{chunk-EY4BJKC4.js.map → chunk-NFZIX3IA.js.map} +0 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useRef, type RefObject } from 'react';
|
|
3
|
+
import { useIsomorphicLayoutEffect, useLatest } from '../../react/use-recipe';
|
|
4
|
+
import type { Instance } from '../../core/types';
|
|
5
|
+
|
|
6
|
+
interface Updatable<O> extends Instance {
|
|
7
|
+
update(options: O): void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The React side of the round and flow charts: the engine is created once and
|
|
12
|
+
* data changes are pushed with update(), so the chart glides instead of being
|
|
13
|
+
* drawn again. `data` is compared as JSON; callbacks always call the latest.
|
|
14
|
+
*/
|
|
15
|
+
export function useChart<O extends object, I extends Updatable<O>>(
|
|
16
|
+
create: (root: HTMLElement | null, options: O) => I,
|
|
17
|
+
data: O,
|
|
18
|
+
callbacks: { formatValue?: (value: number) => string; onSelect?: (...args: never[]) => void },
|
|
19
|
+
instanceRef?: { current: I | null },
|
|
20
|
+
): RefObject<HTMLDivElement | null> {
|
|
21
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
22
|
+
const instance = useRef<I | null>(null);
|
|
23
|
+
const format = useLatest(callbacks.formatValue);
|
|
24
|
+
const select = useLatest(callbacks.onSelect);
|
|
25
|
+
const formatted = callbacks.formatValue !== undefined;
|
|
26
|
+
const key = JSON.stringify(data);
|
|
27
|
+
const latest = useLatest(data);
|
|
28
|
+
const options = (): O => ({
|
|
29
|
+
...latest.current,
|
|
30
|
+
formatValue: formatted ? (value: number) => format.current?.(value) ?? String(value) : undefined,
|
|
31
|
+
onSelect: (...args: never[]) => (select.current as ((...a: never[]) => void) | undefined)?.(...args),
|
|
32
|
+
});
|
|
33
|
+
useIsomorphicLayoutEffect(() => {
|
|
34
|
+
const created = create(ref.current, options());
|
|
35
|
+
instance.current = created;
|
|
36
|
+
if (instanceRef) instanceRef.current = created;
|
|
37
|
+
return () => {
|
|
38
|
+
created.destroy();
|
|
39
|
+
instance.current = null;
|
|
40
|
+
if (instanceRef) instanceRef.current = null;
|
|
41
|
+
};
|
|
42
|
+
// Created once; data is pushed below.
|
|
43
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
44
|
+
}, []);
|
|
45
|
+
const first = useRef(true);
|
|
46
|
+
useIsomorphicLayoutEffect(() => {
|
|
47
|
+
if (first.current) {
|
|
48
|
+
first.current = false;
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
instance.current?.update(options());
|
|
52
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
53
|
+
}, [key, formatted]);
|
|
54
|
+
return ref;
|
|
55
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Tabla avanzada con ordenamiento, búsqueda, filtros, selección, paginación, acciones por fila y columnas configurables. Al ordenar, las filas se deslizan a su nuevo lugar (el resorte de layout del kit, .62 s) y la flecha del encabezado gira. La búsqueda resalta lo que coincide (sin importar tildes ni mayúsculas) y las filas que quedan se reacomodan juntas. Los filtros usan `filter-builder` dentro de un panel que crece desde su botón: la tabla responde mientras editas y una frase dice qué está filtrado («Estado es Enviado y Monto mayor que 500»).
|
|
4
4
|
|
|
5
|
-
Las casillas se dibujan solas y hacen pop; la del encabezado queda a medias cuando solo hay algunas filas elegidas.
|
|
5
|
+
Las casillas se dibujan solas y hacen pop; la del encabezado queda a medias cuando solo hay algunas filas elegidas. Una fila elegida se tiñe apenas y lleva un borde de acento a la izquierda, sin tapar sus datos. Al elegir filas, la barra con el conteo (que rueda) y las acciones masivas aparece con un blur en el lugar de la barra de herramientas, así nunca cubre filas, y ofrece elegir todos los resultados cuando la página entera está marcada. Al cambiar de página, las filas entran por el lado hacia el que vas; si la tabla cabe en su panel, el desborde se oculta mientras viajan para que no parpadee una barra de desplazamiento (si ya es más ancha, aparecen con un fundido). Las filas eliminadas salen deslizándose y las demás cierran el hueco. Mientras carga hay *skeletons* que brillan, y sin nada que mostrar aparece un estado vacío que explica por qué y ofrece limpiar los filtros.
|
|
6
6
|
|
|
7
7
|
## HTML / Astro
|
|
8
8
|
|
|
@@ -56,7 +56,7 @@ Unas `rows` nuevas entran en su sitio y se conservan el orden, los filtros y la
|
|
|
56
56
|
| `rowKey` | `row-key` | `'id'` | Clave (o función) del id de cada fila. |
|
|
57
57
|
| `caption` | `caption` | `'Tabla'` | Nombre accesible. |
|
|
58
58
|
| `pageSize` | `page-size` | `10` | Filas por página (`0`: todas). |
|
|
59
|
-
| `pageSizes` | `page-sizes` (JSON) | `[10, 25, 50]` | Opciones
|
|
59
|
+
| `pageSizes` | `page-sizes` (JSON) | `[10, 25, 50]` | Opciones del menú «Filas» (un botón que abre un menú como Filtros y Columnas, con la opción actual marcada). |
|
|
60
60
|
| `selectable` | `selectable` | `false` | Casillas para elegir filas. |
|
|
61
61
|
| `search` | `search` | `true` | Campo de búsqueda (un texto es su marcador). |
|
|
62
62
|
| `filters` | `filters` | `true` | Botón «Filtros» con `filter-builder`. |
|
|
@@ -80,4 +80,4 @@ Unas `rows` nuevas entran en su sitio y se conservan el orden, los filtros y la
|
|
|
80
80
|
|
|
81
81
|
## Accesibilidad
|
|
82
82
|
|
|
83
|
-
Una tabla real con `caption`, encabezados `th scope="col"` con `aria-sort` y botones para ordenar; casillas con etiqueta («Seleccionar Ana Gómez»); el botón de acciones dice de qué fila es y abre un menú de WAI-ARIA; la barra de selección es un `toolbar` con nombre. Si la tabla es más ancha que su contenedor, se desplaza de lado dentro de una región enfocable. Un estado discreto anuncia el orden, los resultados, la página y la selección. Con movimiento reducido los cambios son inmediatos.
|
|
83
|
+
Una tabla real con `caption`, encabezados `th scope="col"` con `aria-sort` y botones para ordenar; casillas con etiqueta («Seleccionar Ana Gómez»); el botón de acciones dice de qué fila es y abre un menú de WAI-ARIA; «Filas» es un botón con `aria-haspopup="menu"` y sus opciones son `menuitemradio` con `aria-checked`; la barra de selección es un `toolbar` con nombre. Si la tabla es más ancha que su contenedor, se desplaza de lado dentro de una región enfocable. Un estado discreto anuncia el orden, los resultados, la página y la selección. Con movimiento reducido los cambios son inmediatos.
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/* data-table — a toolbar (a 12 px search on surface-3, 12 px tool buttons with an accent count), the filter sentence
|
|
2
2
|
on accent-soft, then the table on a 20 px panel: a sticky muted header with sort buttons whose arrow turns, 3rem
|
|
3
|
-
rows on hairlines, chosen rows tinted
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
rows on hairlines, chosen rows softly tinted with an accent edge (their data stays readable), 18 px checks that
|
|
4
|
+
draw themselves, an 8 px row menu button and shimmering bones while loading; the foot with the range, the rows
|
|
5
|
+
per page menu and the pager; the bar of the chosen rows takes the toolbar's place, so it never covers a row.
|
|
6
|
+
Panels and menus (columns, filters, rows per page, row actions) are anchored .mk-dialog (dialog.css) surfaces with
|
|
7
|
+
20 px corners and 8 px items. The table scrolls sideways inside its panel when it is wider; --mk-dt-max-height
|
|
8
|
+
lets it scroll down too. */
|
|
7
9
|
.mk-data-table,
|
|
8
10
|
mk-data-table {
|
|
9
11
|
--mk-dt-max-height: none;
|
|
@@ -17,6 +19,8 @@ mk-data-table {
|
|
|
17
19
|
|
|
18
20
|
/* ── The toolbar ─────────────────────────────────────────────────────────── */
|
|
19
21
|
[data-mk-dt-bar] {
|
|
22
|
+
grid-row: 1;
|
|
23
|
+
grid-column: 1;
|
|
20
24
|
display: flex;
|
|
21
25
|
flex-wrap: wrap;
|
|
22
26
|
align-items: center;
|
|
@@ -86,6 +90,7 @@ mk-data-table {
|
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
[data-mk-dt-tool]:focus-visible,
|
|
93
|
+
[data-mk-dt-size-button]:focus-visible,
|
|
89
94
|
[data-mk-dt-sort]:focus-visible,
|
|
90
95
|
[data-mk-dt-row-menu]:focus-visible,
|
|
91
96
|
[data-mk-dt-pager] button:focus-visible,
|
|
@@ -223,8 +228,17 @@ mk-data-table {
|
|
|
223
228
|
background: color-mix(in srgb, var(--mk-text) 3%, transparent);
|
|
224
229
|
}
|
|
225
230
|
|
|
231
|
+
/* A chosen row: a light tint and an accent edge, never a fill that hides the data (on OLED the accent is white). */
|
|
226
232
|
[data-mk-dt-table] tbody tr[data-selected] {
|
|
227
|
-
background: color-mix(in srgb, var(--mk-accent
|
|
233
|
+
background: color-mix(in srgb, var(--mk-accent) 7%, transparent);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
[data-mk-dt-table] tbody tr[data-selected]:hover {
|
|
237
|
+
background: color-mix(in srgb, var(--mk-accent) 11%, transparent);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
[data-mk-dt-table] tbody tr[data-selected] > :first-child {
|
|
241
|
+
box-shadow: inset 3px 0 0 var(--mk-accent);
|
|
228
242
|
}
|
|
229
243
|
|
|
230
244
|
[data-mk-dt-table] td mark {
|
|
@@ -296,7 +310,7 @@ th[data-direction='desc'] [data-mk-dt-sort] svg {
|
|
|
296
310
|
width: 1.125rem;
|
|
297
311
|
height: 1.125rem;
|
|
298
312
|
box-sizing: border-box;
|
|
299
|
-
border:
|
|
313
|
+
border: 2px solid var(--mk-outline);
|
|
300
314
|
border-radius: 5px;
|
|
301
315
|
color: transparent;
|
|
302
316
|
vertical-align: middle;
|
|
@@ -317,6 +331,9 @@ th[data-direction='desc'] [data-mk-dt-sort] svg {
|
|
|
317
331
|
}
|
|
318
332
|
|
|
319
333
|
[data-mk-dt-check] svg {
|
|
334
|
+
display: block;
|
|
335
|
+
width: 100%;
|
|
336
|
+
height: 100%;
|
|
320
337
|
pointer-events: none;
|
|
321
338
|
}
|
|
322
339
|
|
|
@@ -422,14 +439,35 @@ th[data-direction='desc'] [data-mk-dt-sort] svg {
|
|
|
422
439
|
margin-left: auto;
|
|
423
440
|
}
|
|
424
441
|
|
|
425
|
-
[data-mk-dt-size]
|
|
442
|
+
[data-mk-dt-size-button] {
|
|
443
|
+
display: inline-flex;
|
|
444
|
+
align-items: center;
|
|
445
|
+
gap: 0.25rem;
|
|
426
446
|
height: 2rem;
|
|
427
|
-
padding: 0 0.
|
|
447
|
+
padding: 0 0.375rem 0 0.75rem;
|
|
428
448
|
border: 1px solid var(--mk-outline-soft);
|
|
429
449
|
border-radius: 8px;
|
|
430
450
|
background: var(--mk-surface-1);
|
|
431
451
|
color: var(--mk-text);
|
|
432
452
|
font: inherit;
|
|
453
|
+
font-variant-numeric: tabular-nums;
|
|
454
|
+
cursor: pointer;
|
|
455
|
+
transition: background-color 0.15s;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
[data-mk-dt-size-button]:hover,
|
|
459
|
+
[data-mk-dt-size-button][aria-expanded='true'] {
|
|
460
|
+
background: var(--mk-surface-2);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
[data-mk-dt-size-button] svg {
|
|
464
|
+
flex: none;
|
|
465
|
+
color: var(--mk-muted);
|
|
466
|
+
transition: transform 0.3s cubic-bezier(0.38, 0.49, 0, 1.5);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
[data-mk-dt-size-button][aria-expanded='true'] svg {
|
|
470
|
+
transform: rotate(180deg);
|
|
433
471
|
}
|
|
434
472
|
|
|
435
473
|
[data-mk-dt-pager] {
|
|
@@ -463,24 +501,28 @@ th[data-direction='desc'] [data-mk-dt-sort] svg {
|
|
|
463
501
|
}
|
|
464
502
|
|
|
465
503
|
/* ── The bar of the chosen rows ──────────────────────────────────────────── */
|
|
504
|
+
/* It shares the toolbar's grid cell and covers it while rows are chosen: the rows stay in view. */
|
|
466
505
|
[data-mk-dt-bulk] {
|
|
467
|
-
position:
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
z-index: 3;
|
|
506
|
+
position: relative;
|
|
507
|
+
z-index: 2;
|
|
508
|
+
grid-row: 1;
|
|
509
|
+
grid-column: 1;
|
|
472
510
|
display: flex;
|
|
473
511
|
flex-wrap: wrap;
|
|
474
512
|
align-items: center;
|
|
513
|
+
align-self: stretch;
|
|
475
514
|
gap: 0.25rem 0.5rem;
|
|
476
|
-
width:
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
515
|
+
min-width: 0;
|
|
516
|
+
min-height: 2.5rem;
|
|
517
|
+
padding: 0.25rem 0.25rem 0.25rem 1rem;
|
|
518
|
+
border-radius: 12px;
|
|
519
|
+
background: var(--mk-surface-3);
|
|
520
|
+
color: var(--mk-text);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.mk-data-table:has(> [data-mk-dt-bulk]:not([hidden])) > [data-mk-dt-bar],
|
|
524
|
+
mk-data-table:has(> [data-mk-dt-bulk]:not([hidden])) > [data-mk-dt-bar] {
|
|
525
|
+
visibility: hidden;
|
|
484
526
|
}
|
|
485
527
|
|
|
486
528
|
[data-mk-dt-bulk-label] {
|
|
@@ -504,12 +546,17 @@ th[data-direction='desc'] [data-mk-dt-sort] svg {
|
|
|
504
546
|
height: 2rem;
|
|
505
547
|
padding: 0 0.75rem;
|
|
506
548
|
border: 0;
|
|
507
|
-
border-radius:
|
|
508
|
-
background:
|
|
549
|
+
border-radius: 8px;
|
|
550
|
+
background: var(--mk-surface-1);
|
|
509
551
|
color: inherit;
|
|
510
552
|
font: inherit;
|
|
511
553
|
font-weight: 500;
|
|
512
554
|
cursor: pointer;
|
|
555
|
+
transition: background-color 0.15s;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
[data-mk-dt-bulk] button:hover {
|
|
559
|
+
background: var(--mk-surface-2);
|
|
513
560
|
}
|
|
514
561
|
|
|
515
562
|
[data-mk-dt-bulk] button[data-danger] {
|
|
@@ -517,23 +564,31 @@ th[data-direction='desc'] [data-mk-dt-sort] svg {
|
|
|
517
564
|
color: var(--mk-on-error, #fff);
|
|
518
565
|
}
|
|
519
566
|
|
|
520
|
-
[data-mk-dt-select-all] {
|
|
567
|
+
[data-mk-dt-bulk] [data-mk-dt-select-all] {
|
|
568
|
+
background: none;
|
|
521
569
|
text-decoration: underline;
|
|
522
570
|
text-underline-offset: 3px;
|
|
523
571
|
}
|
|
524
572
|
|
|
525
|
-
|
|
573
|
+
/* The close button is a square with its cross in the middle (the bar's button padding must not reach it). */
|
|
574
|
+
[data-mk-dt-bulk] [data-mk-dt-bulk-close] {
|
|
526
575
|
display: inline-grid;
|
|
527
576
|
place-items: center;
|
|
528
577
|
width: 2rem;
|
|
578
|
+
margin-left: auto;
|
|
529
579
|
padding: 0;
|
|
530
580
|
}
|
|
531
581
|
|
|
582
|
+
[data-mk-dt-bulk-close] svg {
|
|
583
|
+
display: block;
|
|
584
|
+
}
|
|
585
|
+
|
|
532
586
|
/* ── Panels and the row menu ─────────────────────────────────────────────── */
|
|
533
587
|
.mk-dialog.mk-dt-panel {
|
|
534
588
|
--mk-dialog-radius: 20px;
|
|
535
589
|
--mk-dialog-bg: var(--mk-surface-1);
|
|
536
|
-
|
|
590
|
+
/* A hairline keeps the panel apart from the table under it (on OLED both are near black). */
|
|
591
|
+
--mk-dialog-shadow: 0 0 0 1px var(--mk-outline-soft), 0 16px 48px -12px color-mix(in srgb, var(--mk-shadow-color, #000) 35%, transparent);
|
|
537
592
|
gap: 0.5rem;
|
|
538
593
|
width: max-content;
|
|
539
594
|
min-width: min(14rem, calc(100vw - 32px));
|
|
@@ -574,6 +629,45 @@ th[data-direction='desc'] [data-mk-dt-sort] svg {
|
|
|
574
629
|
background: var(--mk-surface-2);
|
|
575
630
|
}
|
|
576
631
|
|
|
632
|
+
/* The row menu and the rows per page menu look like the panels: 20 px surface, 8 px items, the same hover. */
|
|
633
|
+
.mk-dialog.mk-dt-panel.mk-dt-menu {
|
|
634
|
+
gap: 0.125rem;
|
|
635
|
+
min-width: 9rem;
|
|
636
|
+
padding: 0.375rem;
|
|
637
|
+
-webkit-backdrop-filter: none;
|
|
638
|
+
backdrop-filter: none;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
.mk-dialog.mk-dt-menu [data-mk-item] {
|
|
642
|
+
gap: 0.625rem;
|
|
643
|
+
min-height: 2.25rem;
|
|
644
|
+
padding: 0 0.625rem;
|
|
645
|
+
border-radius: 8px;
|
|
646
|
+
font-family: inherit;
|
|
647
|
+
font-size: 0.875rem;
|
|
648
|
+
font-weight: 500;
|
|
649
|
+
letter-spacing: normal;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
.mk-dialog.mk-dt-menu [data-mk-item]:hover,
|
|
653
|
+
.mk-dialog.mk-dt-menu [data-mk-item]:focus-visible {
|
|
654
|
+
background: var(--mk-surface-2);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
.mk-dialog.mk-dt-menu [data-mk-item] svg {
|
|
658
|
+
flex: none;
|
|
659
|
+
margin-left: auto;
|
|
660
|
+
opacity: 0;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
.mk-dialog.mk-dt-menu [data-mk-item][aria-checked='true'] {
|
|
664
|
+
font-weight: 600;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
.mk-dialog.mk-dt-menu [data-mk-item][aria-checked='true'] svg {
|
|
668
|
+
opacity: 1;
|
|
669
|
+
}
|
|
670
|
+
|
|
577
671
|
.mk-dialog.mk-dt-menu [data-mk-item][data-danger] {
|
|
578
672
|
color: var(--mk-error);
|
|
579
673
|
}
|
|
@@ -584,6 +678,7 @@ th[data-direction='desc'] [data-mk-dt-sort] svg {
|
|
|
584
678
|
}
|
|
585
679
|
|
|
586
680
|
[data-mk-dt-sort] svg,
|
|
681
|
+
[data-mk-dt-size-button] svg,
|
|
587
682
|
[data-mk-dt-check] {
|
|
588
683
|
transition: none;
|
|
589
684
|
}
|
|
@@ -147,9 +147,11 @@ const ICONS = {
|
|
|
147
147
|
next: SVG('<path d="M9 6l6 6-6 6"/>'),
|
|
148
148
|
close: SVG('<path d="M7 7l10 10M17 7L7 17"/>', 14),
|
|
149
149
|
arrow: SVG('<path d="M12 19V5M6 11l6-6 6 6"/>', 12),
|
|
150
|
+
chevron: SVG('<path d="M7 10l5 5 5-5"/>', 14),
|
|
151
|
+
check: SVG('<path d="M5 12.5l4.5 4.5L19 7.5"/>', 16),
|
|
150
152
|
};
|
|
151
153
|
const CHECK =
|
|
152
|
-
'<svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path data-mk-dt-tick d="M5
|
|
154
|
+
'<svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.2" stroke-linecap="round" stroke-linejoin="round"><path data-mk-dt-tick d="M5.5 12l4 4L18.5 7.5" pathLength="1"/><path data-mk-dt-dash d="M6.5 12h11"/></svg>';
|
|
153
155
|
const plural = (n: number, one: string, many: string) => `${n} ${n === 1 ? one : many}`;
|
|
154
156
|
let sequence = 0;
|
|
155
157
|
|
|
@@ -343,21 +345,18 @@ export function createDataTable<T extends DataRow = DataRow>(root: HTMLElement |
|
|
|
343
345
|
|
|
344
346
|
const foot = el('div', 'data-mk-dt-foot');
|
|
345
347
|
const range = el('span', 'data-mk-dt-range');
|
|
346
|
-
const sizeField = el('
|
|
348
|
+
const sizeField = el('div', 'data-mk-dt-size');
|
|
347
349
|
sizeField.append(el('span', undefined, 'Filas'));
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
}
|
|
359
|
-
sizeSelect.value = String(pageSize);
|
|
360
|
-
sizeField.append(sizeSelect);
|
|
350
|
+
const sizeButton = el('button', 'data-mk-dt-size-button');
|
|
351
|
+
sizeButton.type = 'button';
|
|
352
|
+
sizeButton.setAttribute('aria-haspopup', 'menu');
|
|
353
|
+
sizeButton.setAttribute('aria-expanded', 'false');
|
|
354
|
+
const sizeValue = el('span', 'data-mk-dt-size-value', String(pageSize));
|
|
355
|
+
sizeButton.append(sizeValue);
|
|
356
|
+
sizeButton.insertAdjacentHTML('beforeend', ICONS.chevron);
|
|
357
|
+
sizeButton.setAttribute('aria-label', `Filas por página: ${pageSize}`);
|
|
358
|
+
sizeField.append(sizeButton);
|
|
359
|
+
const sizes = pageSize && !pageSizes.includes(pageSize) ? [pageSize, ...pageSizes] : [...pageSizes];
|
|
361
360
|
const pager = el('nav', 'data-mk-dt-pager');
|
|
362
361
|
pager.setAttribute('aria-label', 'Páginas');
|
|
363
362
|
const previous = el('button', 'data-mk-dt-previous');
|
|
@@ -400,7 +399,7 @@ export function createDataTable<T extends DataRow = DataRow>(root: HTMLElement |
|
|
|
400
399
|
const status = el('span');
|
|
401
400
|
status.className = 'mk-sr-only';
|
|
402
401
|
status.setAttribute('role', 'status');
|
|
403
|
-
root.append(bar, active, scroll, emptyHost, foot,
|
|
402
|
+
root.append(bar, bulk, active, scroll, emptyHost, foot, status);
|
|
404
403
|
scope.attr(root, 'data-mk-ready', '');
|
|
405
404
|
scope.add(() => {
|
|
406
405
|
for (const effect of effects) effect.kill();
|
|
@@ -610,15 +609,17 @@ export function createDataTable<T extends DataRow = DataRow>(root: HTMLElement |
|
|
|
610
609
|
gsap.killTweensOf(bulk);
|
|
611
610
|
if (show) {
|
|
612
611
|
bulk.hidden = false;
|
|
613
|
-
if (animate && !reduced()) keep(gsap.fromTo(bulk, { opacity: 0, y:
|
|
612
|
+
if (animate && !reduced()) keep(gsap.fromTo(bulk, { opacity: 0, y: -8, filter: 'blur(8px)' }, { opacity: 1, y: 0, filter: 'blur(0px)', duration: 0.35, ease: EASE.pop, clearProps: 'opacity,transform,filter' }));
|
|
614
613
|
} else if (animate && !reduced()) {
|
|
615
|
-
keep(gsap.to(bulk, { opacity: 0, y:
|
|
614
|
+
keep(gsap.to(bulk, { opacity: 0, y: -8, filter: 'blur(6px)', duration: 0.22, ease: 'power2.in', onComplete: () => {
|
|
616
615
|
bulk.hidden = true;
|
|
617
616
|
gsap.set(bulk, { clearProps: 'opacity,transform,filter' });
|
|
618
617
|
} }));
|
|
619
618
|
} else bulk.hidden = true;
|
|
620
619
|
};
|
|
621
620
|
|
|
621
|
+
let slides = 0;
|
|
622
|
+
scope.add(() => scroll.style.removeProperty('overflow-x'));
|
|
622
623
|
/** Draw the page; `motion`: how the rows change (glide, slide from a side, or nothing). */
|
|
623
624
|
const render = (motion: 'reflow' | 'forward' | 'back' | 'none' = 'none') => {
|
|
624
625
|
const all = loading ? [] : results();
|
|
@@ -645,7 +646,19 @@ export function createDataTable<T extends DataRow = DataRow>(root: HTMLElement |
|
|
|
645
646
|
}),
|
|
646
647
|
);
|
|
647
648
|
} else if (motion === 'forward' || motion === 'back') {
|
|
648
|
-
|
|
649
|
+
// Sliding rows stick out past the side: a table that fits its panel hides the overflow while they travel
|
|
650
|
+
// (no scrollbar blinks); a wider one, which already scrolls, fades them in place instead.
|
|
651
|
+
const fits = scroll.scrollWidth <= scroll.clientWidth + 1;
|
|
652
|
+
const token = ++slides;
|
|
653
|
+
if (fits) scroll.style.overflowX = 'hidden';
|
|
654
|
+
const release = () => {
|
|
655
|
+
if (token === slides) scroll.style.removeProperty('overflow-x');
|
|
656
|
+
};
|
|
657
|
+
keep(
|
|
658
|
+
gsap.fromTo(fresh, fits ? { opacity: 0, x: motion === 'forward' ? 24 : -24 } : { opacity: 0, y: 6 }, {
|
|
659
|
+
opacity: 1, x: 0, y: 0, duration: 0.32, stagger: 0.015, ease: 'power2.out', clearProps: 'opacity,transform', onComplete: release, onInterrupt: release,
|
|
660
|
+
}),
|
|
661
|
+
);
|
|
649
662
|
} else if (wasLoading) keep(gsap.fromTo(fresh, { opacity: 0, y: 6 }, { opacity: 1, y: 0, duration: 0.35, stagger: 0.025, ease: 'power2.out', clearProps: 'opacity,transform' }));
|
|
650
663
|
}
|
|
651
664
|
options.onRender?.(body);
|
|
@@ -793,11 +806,7 @@ export function createDataTable<T extends DataRow = DataRow>(root: HTMLElement |
|
|
|
793
806
|
keep(press(next));
|
|
794
807
|
setPage(page + 1);
|
|
795
808
|
});
|
|
796
|
-
|
|
797
|
-
pageSize = Number(sizeSelect.value) || 0;
|
|
798
|
-
page = 0;
|
|
799
|
-
render('reflow');
|
|
800
|
-
});
|
|
809
|
+
|
|
801
810
|
scope.on(clearButton, 'click', clearAll);
|
|
802
811
|
scope.on(bulkClose, 'click', () => setSelection([]));
|
|
803
812
|
scope.on(bulkAll, 'click', () => setSelection(results().map(idOf)));
|
|
@@ -821,11 +830,57 @@ export function createDataTable<T extends DataRow = DataRow>(root: HTMLElement |
|
|
|
821
830
|
return dialog;
|
|
822
831
|
};
|
|
823
832
|
|
|
833
|
+
const sizeDialog = panelDialog('size', 'Filas por página');
|
|
834
|
+
sizeDialog.className = 'mk-dialog mk-menu mk-dt-panel mk-dt-menu';
|
|
835
|
+
const sizeItems = sizes.map((size) => {
|
|
836
|
+
const item = el('button', 'data-mk-item');
|
|
837
|
+
item.type = 'button';
|
|
838
|
+
item.dataset.mkValue = String(size);
|
|
839
|
+
item.append(el('span', undefined, String(size)));
|
|
840
|
+
item.insertAdjacentHTML('beforeend', ICONS.check);
|
|
841
|
+
sizeDialog.append(item);
|
|
842
|
+
return item;
|
|
843
|
+
});
|
|
844
|
+
const paintSize = () => {
|
|
845
|
+
for (const item of sizeItems) {
|
|
846
|
+
const on = Number(item.dataset.mkValue) === pageSize;
|
|
847
|
+
item.setAttribute('role', 'menuitemradio');
|
|
848
|
+
item.setAttribute('aria-checked', String(on));
|
|
849
|
+
}
|
|
850
|
+
};
|
|
851
|
+
/** A new page size from the menu (animated) or the API. */
|
|
852
|
+
const changePageSize = (size: number, animate: boolean) => {
|
|
853
|
+
const next = Math.max(0, Math.floor(size));
|
|
854
|
+
const text = String(next);
|
|
855
|
+
if (animate && next !== pageSize && !reduced()) keep(rollDigits(sizeValue, text, next > pageSize ? 1 : -1, 0.3));
|
|
856
|
+
else sizeValue.textContent = text;
|
|
857
|
+
sizeButton.setAttribute('aria-label', `Filas por página: ${next}`);
|
|
858
|
+
pageSize = next;
|
|
859
|
+
page = 0;
|
|
860
|
+
paintSize();
|
|
861
|
+
render('reflow');
|
|
862
|
+
};
|
|
863
|
+
const sizeMenu = createMenu(sizeDialog, {
|
|
864
|
+
triggers: false,
|
|
865
|
+
// The menu marks every item a plain menuitem when it opens: these are a choice.
|
|
866
|
+
onOpen: paintSize,
|
|
867
|
+
onClose: () => sizeButton.setAttribute('aria-expanded', 'false'),
|
|
868
|
+
onSelect: (value) => changePageSize(Number(value) || 0, true),
|
|
869
|
+
});
|
|
870
|
+
paintSize();
|
|
871
|
+
scope.on(sizeDialog, 'mk:select', (event) => event.stopPropagation());
|
|
872
|
+
scope.add(() => sizeMenu.destroy());
|
|
873
|
+
scope.on(sizeButton, 'click', () => {
|
|
874
|
+
keep(press(sizeButton));
|
|
875
|
+
sizeButton.setAttribute('aria-expanded', 'true');
|
|
876
|
+
sizeMenu.open(sizeButton);
|
|
877
|
+
});
|
|
878
|
+
|
|
824
879
|
let rowMenu: ReturnType<typeof createMenu> | null = null;
|
|
825
880
|
let menuRow: T | null = null;
|
|
826
881
|
if (options.actions?.length) {
|
|
827
882
|
const dialog = panelDialog('menu', 'Acciones de la fila');
|
|
828
|
-
dialog.className = 'mk-dialog mk-menu mk-dt-menu';
|
|
883
|
+
dialog.className = 'mk-dialog mk-menu mk-dt-panel mk-dt-menu';
|
|
829
884
|
for (const action of options.actions) {
|
|
830
885
|
const item = el('button', 'data-mk-item', action.label);
|
|
831
886
|
item.type = 'button';
|
|
@@ -1005,10 +1060,7 @@ export function createDataTable<T extends DataRow = DataRow>(root: HTMLElement |
|
|
|
1005
1060
|
sortBy,
|
|
1006
1061
|
setPage,
|
|
1007
1062
|
setPageSize(size) {
|
|
1008
|
-
|
|
1009
|
-
sizeSelect.value = String(pageSize);
|
|
1010
|
-
page = 0;
|
|
1011
|
-
render('reflow');
|
|
1063
|
+
changePageSize(size, false);
|
|
1012
1064
|
},
|
|
1013
1065
|
setQuery(next) {
|
|
1014
1066
|
query = next;
|
|
@@ -2,6 +2,7 @@ import { gsap, EASE, registerMotion } from '../../core/gsap';
|
|
|
2
2
|
import { reduced } from '../../core/reduced';
|
|
3
3
|
import { createScope } from '../../core/scope';
|
|
4
4
|
import { inert, type Instance } from '../../core/types';
|
|
5
|
+
import { createMenu, type MenuInstance } from '../menu/engine';
|
|
5
6
|
import { fromJson } from '../../core/dates';
|
|
6
7
|
import { toTime } from '../../core/relative-time';
|
|
7
8
|
import { press, rollDigits, shake } from '../../motion/micro';
|
|
@@ -395,18 +396,13 @@ export function createEditableTable<T extends DataRow = DataRow>(root: HTMLEleme
|
|
|
395
396
|
setValue(id, column!.key, !value);
|
|
396
397
|
return;
|
|
397
398
|
}
|
|
398
|
-
|
|
399
|
+
// A choice opens the kit's menu on the cell (like every other select of the kit), not a system list.
|
|
399
400
|
if (kind === 'select') {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
select.append(element);
|
|
406
|
-
}
|
|
407
|
-
select.value = String(value ?? '');
|
|
408
|
-
editor = select;
|
|
409
|
-
} else {
|
|
401
|
+
openChoices(cell, id, column!, String(value ?? ''));
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
let editor: HTMLInputElement | HTMLSelectElement;
|
|
405
|
+
{
|
|
410
406
|
const input = document.createElement('input');
|
|
411
407
|
input.type = kind === 'number' ? 'text' : kind === 'date' ? 'date' : 'text';
|
|
412
408
|
if (kind === 'number') input.inputMode = 'decimal';
|
|
@@ -479,6 +475,66 @@ export function createEditableTable<T extends DataRow = DataRow>(root: HTMLEleme
|
|
|
479
475
|
};
|
|
480
476
|
const finishEdit = (save: boolean) => editing?.done(save);
|
|
481
477
|
|
|
478
|
+
// ── Choices: one menu, filled with the column's options when a choice cell is edited ──────────────
|
|
479
|
+
const CHECK = '<svg aria-hidden="true" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>';
|
|
480
|
+
let choices: { menu: MenuInstance; dialog: HTMLDialogElement } | null = null;
|
|
481
|
+
let choosing: { id: RowId; key: string; value: string } | null = null;
|
|
482
|
+
const markChoices = (dialog: HTMLElement) => {
|
|
483
|
+
for (const item of dialog.querySelectorAll<HTMLElement>('[data-mk-item]')) {
|
|
484
|
+
item.setAttribute('role', 'menuitemradio');
|
|
485
|
+
item.setAttribute('aria-checked', String(item.dataset.mkValue === choosing?.value));
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
const choiceMenu = () => {
|
|
489
|
+
if (choices) return choices;
|
|
490
|
+
const dialog = document.createElement('dialog');
|
|
491
|
+
dialog.className = 'mk-dialog mk-menu mk-select-menu';
|
|
492
|
+
dialog.dataset.mkEtChoices = '';
|
|
493
|
+
root.append(dialog);
|
|
494
|
+
scope.add(() => dialog.remove());
|
|
495
|
+
const menu = createMenu(dialog, {
|
|
496
|
+
triggers: false,
|
|
497
|
+
onOpen: () => markChoices(dialog),
|
|
498
|
+
onSelect: (value) => {
|
|
499
|
+
const target = choosing;
|
|
500
|
+
if (target && value !== target.value) setValue(target.id, target.key, value);
|
|
501
|
+
},
|
|
502
|
+
onClose: () => {
|
|
503
|
+
const target = choosing;
|
|
504
|
+
choosing = null;
|
|
505
|
+
// Focus comes back to the cell (drawn again after a change).
|
|
506
|
+
const fresh = target ? cellOf(target.id, target.key) : null;
|
|
507
|
+
if (fresh) focusCell(fresh, false);
|
|
508
|
+
},
|
|
509
|
+
});
|
|
510
|
+
// The menu's selection stays inside: listeners on the table hear the table's events.
|
|
511
|
+
scope.on(dialog, 'mk:select', (event) => event.stopPropagation());
|
|
512
|
+
scope.add(() => menu.destroy());
|
|
513
|
+
choices = { menu, dialog };
|
|
514
|
+
return choices;
|
|
515
|
+
};
|
|
516
|
+
const openChoices = (cell: HTMLTableCellElement, id: RowId, column: EditableColumn<T>, value: string) => {
|
|
517
|
+
const { menu, dialog } = choiceMenu();
|
|
518
|
+
dialog.setAttribute('aria-label', column.label);
|
|
519
|
+
dialog.replaceChildren(
|
|
520
|
+
...(column.options ?? []).map((option) => {
|
|
521
|
+
const item = document.createElement('button');
|
|
522
|
+
item.type = 'button';
|
|
523
|
+
item.dataset.mkItem = '';
|
|
524
|
+
item.dataset.mkValue = option.value;
|
|
525
|
+
const text = document.createElement('span');
|
|
526
|
+
text.textContent = option.label;
|
|
527
|
+
item.append(text);
|
|
528
|
+
item.insertAdjacentHTML('beforeend', CHECK);
|
|
529
|
+
return item;
|
|
530
|
+
}),
|
|
531
|
+
);
|
|
532
|
+
choosing = { id, key: column.key, value };
|
|
533
|
+
markChoices(dialog);
|
|
534
|
+
dialog.style.setProperty('--mk-select-width', `${cell.offsetWidth}px`);
|
|
535
|
+
menu.open(cell);
|
|
536
|
+
};
|
|
537
|
+
|
|
482
538
|
const moveFrom = (cell: HTMLTableCellElement, move: 'down' | 'right' | 'left' | 'up') => {
|
|
483
539
|
const { row, column, rows, cells } = positionOf(cell);
|
|
484
540
|
if (move === 'right' || move === 'left') {
|