@inshapardaz/likhari-react 0.1.8 → 0.1.9-dev.96
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/dist/EditorRoot.d.ts +60 -0
- package/dist/EditorRoot.d.ts.map +1 -1
- package/dist/EditorRoot.js +183 -10
- package/dist/components/DraftRestore.d.ts +24 -0
- package/dist/components/DraftRestore.d.ts.map +1 -0
- package/dist/components/DraftRestore.js +61 -0
- package/dist/components/DraftsDialog.d.ts +25 -0
- package/dist/components/DraftsDialog.d.ts.map +1 -0
- package/dist/components/DraftsDialog.js +66 -0
- package/dist/components/LeaveDialog.d.ts +22 -0
- package/dist/components/LeaveDialog.d.ts.map +1 -0
- package/dist/components/LeaveDialog.js +15 -0
- package/dist/components/TableDialog.d.ts +17 -0
- package/dist/components/TableDialog.d.ts.map +1 -0
- package/dist/components/TableDialog.js +33 -0
- package/dist/components/Toolbar.d.ts +4 -1
- package/dist/components/Toolbar.d.ts.map +1 -1
- package/dist/components/Toolbar.js +110 -6
- package/dist/i18n/strings.d.ts +65 -2
- package/dist/i18n/strings.d.ts.map +1 -1
- package/dist/i18n/strings.js +168 -3
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/nodes.d.ts.map +1 -1
- package/dist/nodes.js +4 -0
- package/dist/persistence/draftStorage.d.ts +55 -0
- package/dist/persistence/draftStorage.d.ts.map +1 -0
- package/dist/persistence/draftStorage.js +198 -0
- package/dist/persistence/draftUi.d.ts +9 -0
- package/dist/persistence/draftUi.d.ts.map +1 -0
- package/dist/persistence/draftUi.js +23 -0
- package/dist/table/tableActions.d.ts +21 -0
- package/dist/table/tableActions.d.ts.map +1 -0
- package/dist/table/tableActions.js +71 -0
- package/dist/theme/editor.css +111 -0
- package/dist/theme/editorTheme.d.ts.map +1 -1
- package/dist/theme/editorTheme.js +5 -0
- package/package.json +2 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { $deleteTableColumn__EXPERIMENTAL, $deleteTableRow__EXPERIMENTAL, $getTableCellNodeFromLexicalNode, $getTableColumnIndexFromTableCellNode, $getTableNodeFromLexicalNodeOrThrow, $getTableRowIndexFromTableCellNode, $insertTableColumn__EXPERIMENTAL, $insertTableRow__EXPERIMENTAL, $isTableCellNode, $isTableSelection, } from '@lexical/table';
|
|
2
|
+
import { $getSelection, $isRangeSelection } from 'lexical';
|
|
3
|
+
/** Table cells the selection covers: the caret's own cell, or every cell of a
|
|
4
|
+
* multi-cell selection. Empty when the selection isn't in a table. */
|
|
5
|
+
function $selectedCells() {
|
|
6
|
+
const selection = $getSelection();
|
|
7
|
+
if ($isTableSelection(selection))
|
|
8
|
+
return selection.getNodes().filter($isTableCellNode);
|
|
9
|
+
if ($isRangeSelection(selection)) {
|
|
10
|
+
const cell = $getTableCellNodeFromLexicalNode(selection.anchor.getNode());
|
|
11
|
+
return cell ? [cell] : [];
|
|
12
|
+
}
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
/** How many rows and columns the selection spans (1 × 1 for a caret in a cell, 0 × 0 outside a table). */
|
|
16
|
+
export function $getTableSelectionSize() {
|
|
17
|
+
const cells = $selectedCells();
|
|
18
|
+
if (cells.length === 0)
|
|
19
|
+
return { rows: 0, columns: 0 };
|
|
20
|
+
const rows = cells.map($getTableRowIndexFromTableCellNode);
|
|
21
|
+
const columns = cells.map($getTableColumnIndexFromTableCellNode);
|
|
22
|
+
return {
|
|
23
|
+
rows: Math.max(...rows) - Math.min(...rows) + 1,
|
|
24
|
+
columns: Math.max(...columns) - Math.min(...columns) + 1,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Inserts as many rows as the selection spans, before its first row or after
|
|
29
|
+
* its last. Lexical's own insert works from the selection's focus cell and adds
|
|
30
|
+
* one row, so with several rows selected it would land mid-selection.
|
|
31
|
+
*/
|
|
32
|
+
export function $insertTableRows(insertAfter) {
|
|
33
|
+
const cells = $selectedCells();
|
|
34
|
+
if (cells.length === 0)
|
|
35
|
+
return;
|
|
36
|
+
const rows = cells.map($getTableRowIndexFromTableCellNode);
|
|
37
|
+
const first = Math.min(...rows);
|
|
38
|
+
const last = Math.max(...rows);
|
|
39
|
+
// Park the caret in a cell on the edge row; every insert then lands right next to it.
|
|
40
|
+
cells[rows.indexOf(insertAfter ? last : first)].selectEnd();
|
|
41
|
+
for (let i = 0; i <= last - first; i++)
|
|
42
|
+
$insertTableRow__EXPERIMENTAL(insertAfter);
|
|
43
|
+
}
|
|
44
|
+
/** Like $insertTableRows, for columns. "Before" is the column index below the
|
|
45
|
+
* first selected one (the right-hand side in a right-to-left table). */
|
|
46
|
+
export function $insertTableColumns(insertAfter) {
|
|
47
|
+
const cells = $selectedCells();
|
|
48
|
+
if (cells.length === 0)
|
|
49
|
+
return;
|
|
50
|
+
const columns = cells.map($getTableColumnIndexFromTableCellNode);
|
|
51
|
+
const first = Math.min(...columns);
|
|
52
|
+
const last = Math.max(...columns);
|
|
53
|
+
cells[columns.indexOf(insertAfter ? last : first)].selectEnd();
|
|
54
|
+
for (let i = 0; i <= last - first; i++)
|
|
55
|
+
$insertTableColumn__EXPERIMENTAL(insertAfter);
|
|
56
|
+
}
|
|
57
|
+
/** Deletes every row the selection touches (Lexical handles a multi-cell selection). */
|
|
58
|
+
export function $deleteTableRows() {
|
|
59
|
+
$deleteTableRow__EXPERIMENTAL();
|
|
60
|
+
}
|
|
61
|
+
/** Deletes every column the selection touches. */
|
|
62
|
+
export function $deleteTableColumns() {
|
|
63
|
+
$deleteTableColumn__EXPERIMENTAL();
|
|
64
|
+
}
|
|
65
|
+
/** Removes the whole table the selection is in. */
|
|
66
|
+
export function $deleteTable() {
|
|
67
|
+
const selection = $getSelection();
|
|
68
|
+
const anchor = $isRangeSelection(selection) || $isTableSelection(selection) ? selection.anchor.getNode() : null;
|
|
69
|
+
if (anchor)
|
|
70
|
+
$getTableNodeFromLexicalNodeOrThrow(anchor).remove();
|
|
71
|
+
}
|
package/dist/theme/editor.css
CHANGED
|
@@ -51,6 +51,12 @@
|
|
|
51
51
|
pointer-events: none;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/* pointer-events is inherited, so without this every menu, dropdown and dialog
|
|
55
|
+
portaled into the (pointer-transparent) anchor would be unclickable. */
|
|
56
|
+
.likhari-portal-target > * {
|
|
57
|
+
pointer-events: auto;
|
|
58
|
+
}
|
|
59
|
+
|
|
54
60
|
.likhari-toolbar-group {
|
|
55
61
|
display: flex;
|
|
56
62
|
align-items: center;
|
|
@@ -253,6 +259,37 @@
|
|
|
253
259
|
margin: 1.5em 0;
|
|
254
260
|
}
|
|
255
261
|
|
|
262
|
+
/* Tables — cell merge/split is deliberately out of scope for now (a flagged
|
|
263
|
+
open risk, lexical-editor-spec.md §12 item 3, needing its own scope
|
|
264
|
+
decision); TablePlugin is configured with hasCellMerge={false} to keep
|
|
265
|
+
every table a plain grid until that's resolved. */
|
|
266
|
+
.likhari-table {
|
|
267
|
+
border-collapse: collapse;
|
|
268
|
+
margin: 1em 0;
|
|
269
|
+
width: 100%;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.likhari-table-row {
|
|
273
|
+
border-bottom: 1px solid var(--editor-line);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.likhari-table-cell {
|
|
277
|
+
border: 1px solid var(--editor-line);
|
|
278
|
+
padding: 6px 10px;
|
|
279
|
+
min-width: 72px;
|
|
280
|
+
vertical-align: top;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.likhari-table-cell-header {
|
|
284
|
+
background: var(--editor-paper-dim);
|
|
285
|
+
font-weight: 600;
|
|
286
|
+
text-align: start;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.likhari-table-cell-selected {
|
|
290
|
+
background: var(--editor-accent-soft);
|
|
291
|
+
}
|
|
292
|
+
|
|
256
293
|
/* Images — block-level figure; the caption is static text (edited in the
|
|
257
294
|
image's dialog), and a selected image gets a corner resize handle. */
|
|
258
295
|
.likhari-image-block {
|
|
@@ -370,3 +407,77 @@
|
|
|
370
407
|
text-overflow: ellipsis;
|
|
371
408
|
white-space: nowrap;
|
|
372
409
|
}
|
|
410
|
+
|
|
411
|
+
/* Restore-draft banner: a strip between the toolbar and the canvas. Uses the
|
|
412
|
+
editor's own tokens, so it follows the accent color and dark mode. */
|
|
413
|
+
.likhari-draft-banner {
|
|
414
|
+
display: flex;
|
|
415
|
+
flex-shrink: 0;
|
|
416
|
+
flex-wrap: wrap;
|
|
417
|
+
align-items: center;
|
|
418
|
+
gap: 8px 12px;
|
|
419
|
+
padding: 8px 12px;
|
|
420
|
+
background: var(--editor-accent-soft);
|
|
421
|
+
border-bottom: 1px solid var(--editor-line);
|
|
422
|
+
color: var(--editor-ink);
|
|
423
|
+
font-size: 13px;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
.likhari-draft-banner-message {
|
|
427
|
+
display: flex;
|
|
428
|
+
flex: 1 1 240px;
|
|
429
|
+
align-items: center;
|
|
430
|
+
gap: 8px;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.likhari-draft-banner-message svg {
|
|
434
|
+
flex-shrink: 0;
|
|
435
|
+
color: var(--editor-accent);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.likhari-draft-banner-actions {
|
|
439
|
+
display: flex;
|
|
440
|
+
flex-wrap: wrap;
|
|
441
|
+
gap: 6px;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.likhari-draft-banner-button {
|
|
445
|
+
height: 28px;
|
|
446
|
+
padding: 0 12px;
|
|
447
|
+
border: 1px solid var(--editor-line-strong);
|
|
448
|
+
border-radius: 6px;
|
|
449
|
+
background: var(--editor-paper);
|
|
450
|
+
color: var(--editor-ink);
|
|
451
|
+
font: inherit;
|
|
452
|
+
font-weight: 500;
|
|
453
|
+
cursor: pointer;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.likhari-draft-banner-button:hover {
|
|
457
|
+
background: var(--editor-paper-dim);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.likhari-draft-banner-button[data-variant='primary'] {
|
|
461
|
+
border-color: var(--editor-accent);
|
|
462
|
+
background: var(--editor-accent);
|
|
463
|
+
color: var(--editor-paper);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.likhari-draft-banner-button[data-variant='primary']:hover {
|
|
467
|
+
filter: brightness(1.1);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.likhari-draft-banner-button[data-variant='danger'] {
|
|
471
|
+
border-color: transparent;
|
|
472
|
+
background: transparent;
|
|
473
|
+
color: var(--editor-danger);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.likhari-draft-banner-button[data-variant='danger']:hover {
|
|
477
|
+
background: var(--editor-paper-dim);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
.likhari-draft-banner-button:focus-visible {
|
|
481
|
+
outline: 2px solid var(--editor-accent);
|
|
482
|
+
outline-offset: 1px;
|
|
483
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editorTheme.d.ts","sourceRoot":"","sources":["../../src/theme/editorTheme.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"editorTheme.d.ts","sourceRoot":"","sources":["../../src/theme/editorTheme.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,kBAmCzB,CAAC"}
|
|
@@ -33,4 +33,9 @@ export const editorTheme = {
|
|
|
33
33
|
},
|
|
34
34
|
hr: 'likhari-hr',
|
|
35
35
|
image: 'likhari-image-block',
|
|
36
|
+
table: 'likhari-table',
|
|
37
|
+
tableRow: 'likhari-table-row',
|
|
38
|
+
tableCell: 'likhari-table-cell',
|
|
39
|
+
tableCellHeader: 'likhari-table-cell-header',
|
|
40
|
+
tableCellSelected: 'likhari-table-cell-selected',
|
|
36
41
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inshapardaz/likhari-react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9-dev.96",
|
|
4
4
|
"description": "React implementation of the Likhari editor, built on @lexical/react.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"@lexical/react": "^0.20.0",
|
|
39
39
|
"@lexical/rich-text": "^0.20.0",
|
|
40
40
|
"@lexical/selection": "^0.20.0",
|
|
41
|
+
"@lexical/table": "^0.20.0",
|
|
41
42
|
"@lexical/utils": "^0.20.0",
|
|
42
43
|
"@inshapardaz/likhari-converters": "^0.1.0",
|
|
43
44
|
"@inshapardaz/likhari-core": "^0.1.0",
|