@panphora/clayjs 1.13.1 → 1.15.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/THIRD-PARTY-NOTICES.md +19 -0
- package/dist/clay.standalone.js +69385 -40992
- package/dist/server/checkpoint.cjs +1 -0
- package/dist/server/checkpoint.mjs +8 -0
- package/dist/server/core.cjs +72210 -0
- package/dist/server/rooms.cjs +1 -0
- package/dist/server/rooms.mjs +66 -0
- package/docs/guide.md +127 -99
- package/entries/clay.js +22 -0
- package/entries/sap.js +5 -5
- package/package.json +16 -2
- package/packed-contract.json +18 -3
- package/src/core/boot-phase.js +6 -0
- package/src/core/conflict-download.js +12 -3
- package/src/core/host-meta.js +75 -0
- package/src/core/persist.js +11 -0
- package/src/core/save-core.js +266 -31
- package/src/core/save.js +154 -36
- package/src/core/snapshot.js +51 -13
- package/src/core/style-comparisons.js +37 -0
- package/src/dom/insert-style-tag.js +11 -12
- package/src/lib/derived.js +116 -0
- package/src/lib/effect.js +130 -0
- package/src/lib/mutation.js +3 -1
- package/src/lib/style-adoption.js +9 -0
- package/src/lib/style.js +47 -0
- package/src/live/fast-lane.js +347 -60
- package/src/live/local-lane.js +32 -6
- package/src/live/ops.js +77 -0
- package/src/live/provenance.js +129 -0
- package/src/live/save-history.js +188 -0
- package/src/live/server-lane.js +9 -2
- package/src/live/store.js +194 -16
- package/src/loader-logic.js +16 -4
- package/src/loader.js +37 -1
- package/src/page/address.js +20 -0
- package/src/page/adopt-boot.js +189 -0
- package/src/page/adopt.js +240 -0
- package/src/page/ai-tool.js +188 -0
- package/src/page/announce.js +21 -0
- package/src/page/blocks.js +1170 -0
- package/src/page/blocksel.js +269 -0
- package/src/page/board-drag.js +226 -0
- package/src/page/board-panel.js +353 -0
- package/src/page/board-style.js +146 -0
- package/src/page/board-view.js +447 -0
- package/src/page/board.js +41 -0
- package/src/page/chart.js +220 -0
- package/src/page/checklist.js +143 -0
- package/src/page/chrome.js +587 -0
- package/src/page/csv-import.js +169 -0
- package/src/page/drag.js +202 -0
- package/src/page/drop.js +112 -0
- package/src/page/editor.js +386 -0
- package/src/page/highlight.js +268 -0
- package/src/page/inline.js +60 -1
- package/src/page/items.js +159 -0
- package/src/page/link.js +247 -0
- package/src/page/markdown.js +71 -0
- package/src/page/media.js +570 -0
- package/src/page/merge.js +130 -0
- package/src/page/model.js +71 -9
- package/src/page/placeholder.js +23 -0
- package/src/page/projection.js +647 -0
- package/src/page/recover.js +193 -0
- package/src/page/rich/cell.js +16 -0
- package/src/page/rich/chart.js +104 -0
- package/src/page/rich/csv.js +118 -0
- package/src/page/rich/page-tool.js +86 -0
- package/src/page/rich/tool-runtime.js +195 -0
- package/src/page/save-projection.js +53 -0
- package/src/page/save-states.js +85 -0
- package/src/page/schema.js +12 -5
- package/src/page/table-kinds.js +20 -0
- package/src/page/table-paste.js +57 -0
- package/src/page/table-select.js +244 -0
- package/src/page/table-style.js +25 -0
- package/src/page/table.js +650 -0
- package/src/page/themes/content.css +46 -0
- package/src/page/themes/light-1.css +269 -0
- package/src/page/themes/light-2.css +211 -0
- package/src/page/themes/light-3.css +130 -0
- package/src/page/themes/light-4.css +299 -0
- package/src/page/tool-data.js +25 -0
- package/src/page/tool-view.js +68 -0
- package/src/page/tools.js +133 -0
- package/src/page/view.js +136 -0
- package/src/page/ybind/author.js +25 -0
- package/src/page/ybind/binding.js +92 -0
- package/src/page/ybind/board-commands.js +170 -0
- package/src/page/ybind/board.js +89 -0
- package/src/page/ybind/bridge.js +382 -0
- package/src/page/ybind/conversion.js +26 -0
- package/src/page/ybind/core.js +7 -0
- package/src/page/ybind/decode.js +208 -0
- package/src/page/ybind/doc.js +58 -0
- package/src/page/ybind/external.js +190 -0
- package/src/page/ybind/group.js +124 -0
- package/src/page/ybind/index.js +12 -0
- package/src/page/ybind/layout.js +27 -0
- package/src/page/ybind/marks.js +18 -0
- package/src/page/ybind/migrate.js +152 -0
- package/src/page/ybind/moves.js +288 -0
- package/src/page/ybind/origins.js +257 -0
- package/src/page/ybind/parts.js +44 -0
- package/src/page/ybind/projection-map.js +92 -0
- package/src/page/ybind/provider.js +285 -0
- package/src/page/ybind/recovery.js +411 -0
- package/src/page/ybind/seed.js +27 -0
- package/src/page/ybind/table.js +166 -0
- package/src/page/ybind/tokens.js +93 -0
- package/src/page/ybind/walk.js +333 -0
- package/src/page/ybind/writer.js +307 -0
- package/src/plugins/derive.js +12 -0
- package/src/plugins/indicator.js +16 -6
- package/src/plugins/live.js +742 -65
- package/src/plugins/page.js +547 -0
- package/src/plugins/undo.js +62 -4
- package/src/server/checkpoint/index.js +218 -0
- package/src/server/rooms/index.js +59 -0
- package/src/server/rooms/live-data-ops.js +369 -0
- package/src/server/rooms/live-data-rooms.js +965 -0
- package/src/server/rooms/live-data-tags.js +49 -0
- package/src/server/rooms/live-page-history.js +41 -0
- package/src/server/rooms/live-provenance.js +142 -0
- package/src/server/rooms/page-admission.js +79 -0
- package/src/server/rooms/page-conversion.js +62 -0
- package/src/server/rooms/page-rebase.js +108 -0
- package/src/server/rooms/page-state.js +91 -0
- package/src/server/rooms/page-write-admission.js +63 -0
- package/src/server/rooms/page-write-guard.js +23 -0
- package/src/server/rooms/recent.js +18 -0
- package/src/server/rooms/spec-wire.js +17 -0
- package/src/standalone.js +22 -0
- package/src/sync/conflict-revert.js +2 -0
- package/src/sync/conflicts.js +2 -2
- package/src/sync/live-sync.js +36 -8
- package/src/sync/stream.js +62 -16
- package/src/ui/index.js +4 -0
- package/src/ui/toast.js +21 -5
- package/src/vendor/pierre/pierre.vendor.js +3 -0
- package/src/vendor/sap-core.vendor.js +9 -0
- package/src/vendor/sap-ownership.vendor.js +111 -0
- package/src/vendor/yjs.vendor.js +7 -0
- package/src/page/binding.js +0 -647
- package/src/page/replay.js +0 -578
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// Rectangle selection in a table: drag across cells or Shift+click, or pick
|
|
2
|
+
// Select row / Select column from a handle menu. The rectangle lives in plugin
|
|
3
|
+
// state by table, row and column ids, so a peer's edit keeps it while those
|
|
4
|
+
// ids exist. Copy writes tab-separated text plus an HTML table; Delete clears
|
|
5
|
+
// the covered cells; a paste starts at the top-left cell.
|
|
6
|
+
import { state, view as pmView } from "../vendor/prosemirror.vendor.js";
|
|
7
|
+
import { schema } from "./schema.js";
|
|
8
|
+
import { cellValue, orderedIds } from "./model.js";
|
|
9
|
+
import { cellText } from "./rich/cell.js";
|
|
10
|
+
import { clipboardSerializer } from "./blocks.js";
|
|
11
|
+
import { asStep } from "./blocksel.js";
|
|
12
|
+
import { announce } from "./announce.js";
|
|
13
|
+
import { REMOTE_META } from "./ybind/layout.js";
|
|
14
|
+
|
|
15
|
+
const { Plugin, PluginKey, TextSelection } = state;
|
|
16
|
+
const N = schema.nodes;
|
|
17
|
+
export const rectKey = new PluginKey("tableRect");
|
|
18
|
+
|
|
19
|
+
const findTable = (doc, id) => {
|
|
20
|
+
let found = null;
|
|
21
|
+
doc.forEach((node, pos) => { if (!found && node.type === N.table && node.attrs.id === id) found = { node, pos }; });
|
|
22
|
+
return found;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function rectCells(doc, rect) {
|
|
26
|
+
if (!rect) return null;
|
|
27
|
+
const found = findTable(doc, rect.table);
|
|
28
|
+
if (!found) return null;
|
|
29
|
+
const rowIds = [];
|
|
30
|
+
found.node.forEach((row) => rowIds.push(row.attrs.id));
|
|
31
|
+
const colIds = orderedIds(found.node.attrs.cols);
|
|
32
|
+
const r = [rowIds.indexOf(rect.anchor[0]), rowIds.indexOf(rect.head[0])];
|
|
33
|
+
const c = [colIds.indexOf(rect.anchor[1]), colIds.indexOf(rect.head[1])];
|
|
34
|
+
if (r.includes(-1) || c.includes(-1)) return null;
|
|
35
|
+
const top = Math.min(...r), bottom = Math.max(...r), left = Math.min(...c), right = Math.max(...c);
|
|
36
|
+
const cells = [];
|
|
37
|
+
let rowPos = found.pos + 1;
|
|
38
|
+
found.node.forEach((row, _o, i) => {
|
|
39
|
+
if (i >= top && i <= bottom) {
|
|
40
|
+
const line = [];
|
|
41
|
+
let at = rowPos + 1;
|
|
42
|
+
row.forEach((cell, _p, j) => { if (j >= left && j <= right) line.push({ cell, pos: at }); at += cell.nodeSize; });
|
|
43
|
+
cells.push(line);
|
|
44
|
+
}
|
|
45
|
+
rowPos += row.nodeSize;
|
|
46
|
+
});
|
|
47
|
+
return { ...found, top, bottom, left, right, cells };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const cellIds = (doc, pos) => {
|
|
51
|
+
const $pos = doc.resolve(pos);
|
|
52
|
+
for (let d = $pos.depth; d > 0; d--) {
|
|
53
|
+
if ($pos.node(d).type === N.table_cell && d >= 3) return { table: $pos.node(d - 2).attrs.id, row: $pos.node(d - 1).attrs.id, col: $pos.node(d).attrs.col, start: $pos.start(d) };
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export function setRect(view, anchorPos, headPos) {
|
|
59
|
+
const a = cellIds(view.state.doc, anchorPos), h = cellIds(view.state.doc, headPos);
|
|
60
|
+
if (!a || !h || a.table !== h.table) return false;
|
|
61
|
+
const rect = { table: a.table, anchor: [a.row, a.col], head: [h.row, h.col] };
|
|
62
|
+
const tr = view.state.tr.setSelection(TextSelection.create(view.state.doc, h.start)).setMeta(rectKey, rect);
|
|
63
|
+
view.dispatch(tr);
|
|
64
|
+
const found = rectCells(view.state.doc, rect);
|
|
65
|
+
const count = found ? (found.bottom - found.top + 1) * (found.right - found.left + 1) : 0;
|
|
66
|
+
announce(view, `${count} ${count === 1 ? "cell" : "cells"} selected`);
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function selectLine(view, tablePos, what, index) {
|
|
71
|
+
const table = view.state.doc.nodeAt(tablePos);
|
|
72
|
+
if (table?.type !== N.table || !table.childCount) return false;
|
|
73
|
+
const lastRow = table.childCount - 1, lastCol = table.child(0).childCount - 1;
|
|
74
|
+
const at = (row, col) => {
|
|
75
|
+
let pos = tablePos + 1;
|
|
76
|
+
for (let i = 0; i < row; i++) pos += table.child(i).nodeSize;
|
|
77
|
+
pos++;
|
|
78
|
+
for (let j = 0; j < col; j++) pos += table.child(row).child(j).nodeSize;
|
|
79
|
+
return pos + 1;
|
|
80
|
+
};
|
|
81
|
+
return what === "row" ? setRect(view, at(index, 0), at(index, lastCol)) : setRect(view, at(0, index), at(lastRow, index));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const MODIFIERS = new Set(["Shift", "Control", "Alt", "Meta", "CapsLock"]);
|
|
85
|
+
|
|
86
|
+
const quote = (text) => (/[\t\n\r"]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text);
|
|
87
|
+
|
|
88
|
+
export function rectClipboard(found, doc) {
|
|
89
|
+
const text = found.cells.map((line) => line.map(({ cell }) => quote(cellText(cellValue(cell)))).join("\t")).join("\n");
|
|
90
|
+
const table = doc.createElement("table");
|
|
91
|
+
const body = table.appendChild(doc.createElement("tbody"));
|
|
92
|
+
for (const line of found.cells) {
|
|
93
|
+
const tr = body.appendChild(doc.createElement("tr"));
|
|
94
|
+
for (const { cell } of line) {
|
|
95
|
+
const td = tr.appendChild(doc.createElement("td"));
|
|
96
|
+
td.dataset.col = cell.attrs.col;
|
|
97
|
+
td.append(clipboardSerializer.serializeFragment(cell.content, { document: doc }));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return { text, html: table.outerHTML };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function clearRect(st, dispatch) {
|
|
104
|
+
const found = rectCells(st.doc, rectKey.getState(st));
|
|
105
|
+
if (!found) return false;
|
|
106
|
+
if (dispatch) {
|
|
107
|
+
const tr = st.tr;
|
|
108
|
+
for (const line of [...found.cells].reverse()) for (const { cell, pos } of [...line].reverse()) {
|
|
109
|
+
if (cell.content.size) tr.delete(pos + 1, pos + cell.nodeSize - 1);
|
|
110
|
+
}
|
|
111
|
+
tr.setMeta(rectKey, rectKey.getState(st));
|
|
112
|
+
dispatch(asStep(tr));
|
|
113
|
+
}
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const write = (event, found, view) => {
|
|
118
|
+
const { text, html } = rectClipboard(found, view.dom.ownerDocument);
|
|
119
|
+
event.clipboardData.setData("text/plain", text);
|
|
120
|
+
event.clipboardData.setData("text/html", html);
|
|
121
|
+
event.preventDefault();
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const extend = (dr, dc) => (st, dispatch, view) => {
|
|
125
|
+
const rect = rectKey.getState(st);
|
|
126
|
+
const found = rectCells(st.doc, rect);
|
|
127
|
+
if (!found || !view) return false;
|
|
128
|
+
const rowIds = [], colIds = orderedIds(found.node.attrs.cols);
|
|
129
|
+
found.node.forEach((row) => rowIds.push(row.attrs.id));
|
|
130
|
+
const r = Math.max(0, Math.min(rowIds.length - 1, rowIds.indexOf(rect.head[0]) + dr));
|
|
131
|
+
const c = Math.max(0, Math.min(colIds.length - 1, colIds.indexOf(rect.head[1]) + dc));
|
|
132
|
+
if (dispatch) dispatch(st.tr.setMeta(rectKey, { ...rect, head: [rowIds[r], colIds[c]] }));
|
|
133
|
+
return true;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const cellOfEvent = (view, event) => {
|
|
137
|
+
const td = event.target?.closest?.("td");
|
|
138
|
+
if (!td || !view.dom.contains(td)) return null;
|
|
139
|
+
try { return view.posAtDOM(td, 0); } catch { return null; }
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export function tableSelectPlugin() {
|
|
143
|
+
let drag = null;
|
|
144
|
+
return new Plugin({
|
|
145
|
+
key: rectKey,
|
|
146
|
+
state: {
|
|
147
|
+
init: () => null,
|
|
148
|
+
apply(tr, value, _old, next) {
|
|
149
|
+
const meta = tr.getMeta(rectKey);
|
|
150
|
+
if (meta !== undefined) return meta;
|
|
151
|
+
if (!value) return null;
|
|
152
|
+
if (tr.selectionSet) return null;
|
|
153
|
+
if (tr.docChanged && !tr.getMeta(REMOTE_META)) return null;
|
|
154
|
+
return rectCells(next.doc, value) ? value : null;
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
props: {
|
|
158
|
+
attributes: (st) => (rectKey.getState(st) ? { class: "has-cell-rect" } : {}),
|
|
159
|
+
createSelectionBetween: (view) => (rectKey.getState(view.state) ? view.state.selection : null),
|
|
160
|
+
decorations(st) {
|
|
161
|
+
const found = rectCells(st.doc, rectKey.getState(st));
|
|
162
|
+
if (!found) return null;
|
|
163
|
+
return pmView.DecorationSet.create(st.doc, found.cells.flat().map(({ cell, pos }) => pmView.Decoration.node(pos, pos + cell.nodeSize, { class: "is-cell-selected" })));
|
|
164
|
+
},
|
|
165
|
+
handleKeyDown(view, event) {
|
|
166
|
+
const st = view.state;
|
|
167
|
+
if (!rectKey.getState(st)) return false;
|
|
168
|
+
if (event.key === "Escape") { view.dispatch(st.tr.setMeta(rectKey, null)); return true; }
|
|
169
|
+
if (event.key === "Backspace" || event.key === "Delete") return clearRect(st, view.dispatch);
|
|
170
|
+
if (event.shiftKey) {
|
|
171
|
+
const step = { ArrowUp: [-1, 0], ArrowDown: [1, 0], ArrowLeft: [0, -1], ArrowRight: [0, 1] }[event.key];
|
|
172
|
+
if (step) return extend(...step)(st, view.dispatch, view);
|
|
173
|
+
}
|
|
174
|
+
if (MODIFIERS.has(event.key) || ((event.metaKey || event.ctrlKey) && (event.key === "c" || event.key === "x" || event.key === "v"))) return false;
|
|
175
|
+
view.dispatch(st.tr.setMeta(rectKey, null));
|
|
176
|
+
return false;
|
|
177
|
+
},
|
|
178
|
+
handleDOMEvents: {
|
|
179
|
+
mousedown(view, event) {
|
|
180
|
+
if (event.button !== 0) return false;
|
|
181
|
+
if (!event.shiftKey && rectKey.getState(view.state)) view.dispatch(view.state.tr.setMeta(rectKey, null));
|
|
182
|
+
const pos = cellOfEvent(view, event);
|
|
183
|
+
if (pos === null) return false;
|
|
184
|
+
if (event.shiftKey) {
|
|
185
|
+
const rect = rectKey.getState(view.state);
|
|
186
|
+
const anchor = (rect && anchorPos(view.state.doc, rect)) ?? view.state.selection.$anchor.pos;
|
|
187
|
+
if (!cellIds(view.state.doc, anchor)) return false;
|
|
188
|
+
event.preventDefault();
|
|
189
|
+
return setRect(view, anchor, pos);
|
|
190
|
+
}
|
|
191
|
+
const doc = view.dom.ownerDocument;
|
|
192
|
+
const start = pos;
|
|
193
|
+
const move = (e) => {
|
|
194
|
+
const at = cellOfEvent(view, e);
|
|
195
|
+
if (at === null) return;
|
|
196
|
+
const a = cellIds(view.state.doc, start), h = cellIds(view.state.doc, at);
|
|
197
|
+
if (!a || !h || a.table !== h.table) return;
|
|
198
|
+
const rect = rectKey.getState(view.state);
|
|
199
|
+
if (a.row === h.row && a.col === h.col && !rect) return;
|
|
200
|
+
if (rect && rect.head[0] === h.row && rect.head[1] === h.col) return;
|
|
201
|
+
e.preventDefault();
|
|
202
|
+
setRect(view, start, at);
|
|
203
|
+
};
|
|
204
|
+
const up = () => { doc.removeEventListener("mouseover", move); doc.removeEventListener("mouseup", up); drag = null; };
|
|
205
|
+
drag?.();
|
|
206
|
+
drag = up;
|
|
207
|
+
doc.addEventListener("mouseover", move);
|
|
208
|
+
doc.addEventListener("mouseup", up);
|
|
209
|
+
return false;
|
|
210
|
+
},
|
|
211
|
+
copy(view, event) {
|
|
212
|
+
const found = rectCells(view.state.doc, rectKey.getState(view.state));
|
|
213
|
+
if (!found || !event.clipboardData) return false;
|
|
214
|
+
write(event, found, view);
|
|
215
|
+
return true;
|
|
216
|
+
},
|
|
217
|
+
cut(view, event) {
|
|
218
|
+
const found = rectCells(view.state.doc, rectKey.getState(view.state));
|
|
219
|
+
if (!found || !event.clipboardData) return false;
|
|
220
|
+
write(event, found, view);
|
|
221
|
+
clearRect(view.state, view.dispatch);
|
|
222
|
+
return true;
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
view: () => ({ destroy: () => drag?.() }),
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function anchorPos(doc, rect) {
|
|
231
|
+
const found = findTable(doc, rect.table);
|
|
232
|
+
let out = null;
|
|
233
|
+
found?.node.forEach((row, offset) => {
|
|
234
|
+
if (row.attrs.id !== rect.anchor[0]) return;
|
|
235
|
+
let at = found.pos + 1 + offset + 1;
|
|
236
|
+
row.forEach((cell) => { if (cell.attrs.col === rect.anchor[1]) out = at + 1; at += cell.nodeSize; });
|
|
237
|
+
});
|
|
238
|
+
return out;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export const rectTopLeft = (st) => {
|
|
242
|
+
const found = rectCells(st.doc, rectKey.getState(st));
|
|
243
|
+
return found ? { row: found.top, cell: found.left, rows: found.bottom - found.top + 1, cols: found.right - found.left + 1 } : null;
|
|
244
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export const TABLE_CSS = `
|
|
2
|
+
.page-editor .page-table { position: relative; }
|
|
3
|
+
.page-table td { position: relative; }
|
|
4
|
+
.page-table th { position: relative; }
|
|
5
|
+
.page-table td.is-cell-selected { background: var(--page-block-selected); }
|
|
6
|
+
.page-editor.has-cell-rect .page-table td ::selection, .page-editor.has-cell-rect .page-table td::selection { background: transparent; }
|
|
7
|
+
.page-editor.has-cell-rect .page-table td { caret-color: transparent; }
|
|
8
|
+
.page-table table.is-sized { table-layout: fixed; min-width: 0; }
|
|
9
|
+
.page-table table.is-sized th, .page-table table.is-sized td { min-width: 0; overflow-wrap: anywhere; }
|
|
10
|
+
.page-table-resize { position: absolute; top: 0; bottom: 0; inset-inline-end: -4px; width: 8px; z-index: 2; cursor: col-resize; touch-action: none; }
|
|
11
|
+
.page-table-resize:hover, .page-table-resize:active { background: var(--page-accent); opacity: 0.4; }
|
|
12
|
+
.page-table-add-row, .page-table-add-col { position: absolute; z-index: 1; border: 0; padding: 0; background: var(--page-chrome-bg-rest); color: var(--page-chrome-ink-rest); font: inherit; line-height: 1; cursor: pointer; opacity: 0; transition: opacity 120ms; }
|
|
13
|
+
.page-table-add-row { left: 0; right: 0; top: 100%; height: 14px; border-radius: 0 0 6px 6px; }
|
|
14
|
+
.page-table-add-col { top: 0; bottom: 0; left: 100%; width: 14px; border-radius: 0 6px 6px 0; }
|
|
15
|
+
.page-table:hover > .page-table-add-row, .page-table:hover > .page-table-add-col, .page-table-add-row:focus-visible, .page-table-add-col:focus-visible { opacity: 1; }
|
|
16
|
+
.page-table-add-row:hover, .page-table-add-col:hover { background: var(--page-chrome-bg-moved); color: var(--page-ink-soft); }
|
|
17
|
+
.page-table-date { position: absolute; inset-inline-start: 0; top: 100%; z-index: 3; font: inherit; color: var(--page-pop-ink); background: var(--page-pop-bg); border: 1px solid var(--page-pop-line); border-radius: 6px; padding: 4px 6px; box-shadow: var(--page-pop-shadow); }
|
|
18
|
+
@media (max-width: 560px) {
|
|
19
|
+
.page-table-date { min-height: 44px; }
|
|
20
|
+
.page-table-resize { display: none; }
|
|
21
|
+
.page-table-add-row, .page-table-add-col { position: static; display: inline-block; width: 50%; height: 44px; border-radius: 0; opacity: 1; border-block-start: 1px solid var(--page-rule); }
|
|
22
|
+
.page-table-add-row::after { content: " Row"; }
|
|
23
|
+
.page-table-add-col::after { content: " Column"; }
|
|
24
|
+
}
|
|
25
|
+
`;
|