@estiva-app/ui 0.54.0 → 0.56.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.
Files changed (69) hide show
  1. package/dist/BlockHandle.d.ts +47 -0
  2. package/dist/BlockHandle.d.ts.map +1 -0
  3. package/dist/CollapsibleSection.d.ts +7 -1
  4. package/dist/CollapsibleSection.d.ts.map +1 -1
  5. package/dist/ContainerHeader.d.ts +7 -1
  6. package/dist/ContainerHeader.d.ts.map +1 -1
  7. package/dist/EditorBlockHandle.d.ts +57 -0
  8. package/dist/EditorBlockHandle.d.ts.map +1 -0
  9. package/dist/IconButton.d.ts.map +1 -1
  10. package/dist/ListColumn.d.ts +2 -0
  11. package/dist/ListColumn.d.ts.map +1 -1
  12. package/dist/Menu.d.ts.map +1 -1
  13. package/dist/NavItem.d.ts.map +1 -1
  14. package/dist/NavTree.d.ts +9 -1
  15. package/dist/NavTree.d.ts.map +1 -1
  16. package/dist/PersonTrigger.d.ts.map +1 -1
  17. package/dist/SectionHeader.d.ts +22 -3
  18. package/dist/SectionHeader.d.ts.map +1 -1
  19. package/dist/SectionLabel.d.ts.map +1 -1
  20. package/dist/editor.d.ts +1 -0
  21. package/dist/editor.d.ts.map +1 -1
  22. package/dist/editor.js +127 -11
  23. package/dist/editor.js.map +4 -4
  24. package/dist/gates/index.js +1 -1
  25. package/dist/gates/index.js.map +2 -2
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +831 -624
  29. package/dist/index.js.map +4 -4
  30. package/dist/looks.d.ts +4 -0
  31. package/dist/looks.d.ts.map +1 -1
  32. package/package.json +1 -1
  33. package/registry.json +302 -23
  34. package/src/BlockHandle.mdx +58 -0
  35. package/src/BlockHandle.stories.tsx +44 -0
  36. package/src/BlockHandle.test.tsx +216 -0
  37. package/src/BlockHandle.tsx +220 -0
  38. package/src/CollapsibleSection.stories.tsx +14 -1
  39. package/src/CollapsibleSection.tsx +10 -4
  40. package/src/ContainerHeader.mdx +4 -0
  41. package/src/ContainerHeader.stories.tsx +14 -0
  42. package/src/ContainerHeader.test.tsx +8 -0
  43. package/src/ContainerHeader.tsx +34 -6
  44. package/src/EditorBlockHandle.mdx +65 -0
  45. package/src/EditorBlockHandle.stories.tsx +81 -0
  46. package/src/EditorBlockHandle.tsx +181 -0
  47. package/src/IconButton.tsx +2 -1
  48. package/src/ListColumn.tsx +4 -2
  49. package/src/Menu.tsx +5 -4
  50. package/src/NavItem.test.tsx +2 -2
  51. package/src/NavItem.tsx +21 -12
  52. package/src/NavTree.mdx +16 -1
  53. package/src/NavTree.stories.tsx +49 -2
  54. package/src/NavTree.test.tsx +7 -0
  55. package/src/NavTree.tsx +21 -4
  56. package/src/PersonTrigger.tsx +6 -5
  57. package/src/SectionHeader.mdx +6 -0
  58. package/src/SectionHeader.stories.tsx +18 -0
  59. package/src/SectionHeader.test.tsx +26 -0
  60. package/src/SectionHeader.tsx +99 -23
  61. package/src/SectionLabel.tsx +6 -3
  62. package/src/Sidebar.tsx +1 -1
  63. package/src/editor.ts +1 -0
  64. package/src/eslint/no-copied-look.test.ts +2 -2
  65. package/src/gates/app-checks.ts +1 -1
  66. package/src/index.ts +1 -0
  67. package/src/looks.ts +6 -0
  68. package/src/registry/registry.test.ts +5 -5
  69. package/stories/Choosing.mdx +2 -0
package/dist/looks.d.ts CHANGED
@@ -34,6 +34,10 @@ export declare const CHIP_TEXT_CLASSES = "text-chip signal:font-mono signal:text
34
34
  /** Words typed straight into an input with no box of its own: SearchInput and ChipInput. */
35
35
  export declare const BARE_INPUT_CLASSES = "bg-transparent text-text-primary placeholder:text-text-muted outline-none";
36
36
  /** A sidebar row's shape and its words at rest, brightening under the pointer: NavItem, and SectionHeader's `row` (a folder among rows). */
37
+ /** A quiet icon control: secondary, brightening on a hover fill — IconButton's `muted`. */
38
+ export declare const MUTED_CONTROL_CLASSES = "text-text-secondary hover:bg-bg-hover hover:text-text-primary";
39
+ /** A control that opens a menu: a hover fill, held while its menu is up — PersonTrigger's row, and a ContainerHeader title with a menu. */
40
+ export declare const MENU_TRIGGER_CLASSES = "cursor-pointer rounded-md transition-colors hover:bg-bg-hover data-[popup-open]:bg-bg-hover";
37
41
  export declare const SIDEBAR_ROW_CLASSES = "rounded-lg transition-colors";
38
42
  export declare const SIDEBAR_ROW_TEXT_CLASSES = "text-text-secondary hover:text-text-primary";
39
43
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"looks.d.ts","sourceRoot":"","sources":["../src/looks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB,2MAA2M,CAAA;AAEzO,wFAAwF;AACxF,eAAO,MAAM,kBAAkB,kDAAkD,CAAA;AAEjF,mEAAmE;AACnE,eAAO,MAAM,sBAAsB,iKAAiK,CAAA;AAEpM,wEAAwE;AACxE,eAAO,MAAM,wBAAwB,4DAA4D,CAAA;AAEjG,kJAAkJ;AAClJ,eAAO,MAAM,kBAAkB;;;CAGrB,CAAA;AAEV,qGAAqG;AACrG,eAAO,MAAM,mBAAmB,uJAAuJ,CAAA;AAEvL,mEAAmE;AACnE,eAAO,MAAM,wBAAwB,qEAAqE,CAAA;AAE1G,yDAAyD;AACzD,eAAO,MAAM,iBAAiB,0FAA0F,CAAA;AAExH,4FAA4F;AAC5F,eAAO,MAAM,kBAAkB,8EAA8E,CAAA;AAE7G,4IAA4I;AAE5I,eAAO,MAAM,mBAAmB,iCAAiC,CAAA;AACjE,eAAO,MAAM,wBAAwB,gDAAgD,CAAA;AAErF;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,kLAAkL,CAAA"}
1
+ {"version":3,"file":"looks.d.ts","sourceRoot":"","sources":["../src/looks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB,2MAA2M,CAAA;AAEzO,wFAAwF;AACxF,eAAO,MAAM,kBAAkB,kDAAkD,CAAA;AAEjF,mEAAmE;AACnE,eAAO,MAAM,sBAAsB,iKAAiK,CAAA;AAEpM,wEAAwE;AACxE,eAAO,MAAM,wBAAwB,4DAA4D,CAAA;AAEjG,kJAAkJ;AAClJ,eAAO,MAAM,kBAAkB;;;CAGrB,CAAA;AAEV,qGAAqG;AACrG,eAAO,MAAM,mBAAmB,uJAAuJ,CAAA;AAEvL,mEAAmE;AACnE,eAAO,MAAM,wBAAwB,qEAAqE,CAAA;AAE1G,yDAAyD;AACzD,eAAO,MAAM,iBAAiB,0FAA0F,CAAA;AAExH,4FAA4F;AAC5F,eAAO,MAAM,kBAAkB,8EAA8E,CAAA;AAE7G,4IAA4I;AAE5I,2FAA2F;AAC3F,eAAO,MAAM,qBAAqB,kEAAkE,CAAA;AAEpG,2IAA2I;AAC3I,eAAO,MAAM,oBAAoB,gGAAgG,CAAA;AAEjI,eAAO,MAAM,mBAAmB,iCAAiC,CAAA;AACjE,eAAO,MAAM,wBAAwB,gDAAgD,CAAA;AAErF;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,kLAAkL,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@estiva-app/ui",
3
- "version": "0.54.0",
3
+ "version": "0.56.0",
4
4
  "description": "Estiva's design tokens (the contract) and a small set of primitives (a convenience) for every Estiva app.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/registry.json CHANGED
@@ -4,9 +4,9 @@
4
4
  "kind": "package",
5
5
  "repo": "estiva-ui",
6
6
  "package": "@estiva-app/ui",
7
- "packageVersion": "0.54.0",
8
- "exports": 122,
9
- "typeExports": 129,
7
+ "packageVersion": "0.56.0",
8
+ "exports": 130,
9
+ "typeExports": 134,
10
10
  "files": null
11
11
  },
12
12
  "storybook": {
@@ -458,6 +458,88 @@
458
458
  "app": null,
459
459
  "looks": []
460
460
  },
461
+ {
462
+ "name": "blockAtY",
463
+ "repo": "estiva-ui",
464
+ "kind": "helper",
465
+ "importPath": "@estiva-app/ui",
466
+ "sourceFile": "src/BlockHandle.tsx",
467
+ "purpose": "The last of `blocks` whose top is at or above `y` — the gap between two blocks belongs to the one before.",
468
+ "purposeFrom": "comment",
469
+ "props": [],
470
+ "variants": [],
471
+ "ownsBehaviours": [],
472
+ "status": "stable",
473
+ "migrationStage": null,
474
+ "docsId": "primitives-blockhandle--docs",
475
+ "storyId": "primitives-blockhandle--default",
476
+ "docPage": null,
477
+ "app": null,
478
+ "looks": []
479
+ },
480
+ {
481
+ "name": "blockDropCursor",
482
+ "repo": "estiva-ui",
483
+ "kind": "helper",
484
+ "importPath": "@estiva-app/ui/editor",
485
+ "sourceFile": "src/EditorBlockHandle.tsx",
486
+ "purpose": "The drop line a dragged block shows, in the package's accent instead of the editor's default (the text's colour).",
487
+ "purposeFrom": "comment",
488
+ "props": [],
489
+ "variants": [],
490
+ "ownsBehaviours": [],
491
+ "status": "stable",
492
+ "migrationStage": null,
493
+ "docsId": "primitives-editorblockhandle--docs",
494
+ "storyId": "primitives-editorblockhandle--default",
495
+ "docPage": null,
496
+ "app": null,
497
+ "looks": []
498
+ },
499
+ {
500
+ "name": "BlockHandle",
501
+ "repo": "estiva-ui",
502
+ "kind": "component",
503
+ "importPath": "@estiva-app/ui",
504
+ "sourceFile": "src/BlockHandle.tsx",
505
+ "purpose": "The handle left of the block under the pointer, as a block-document page has it, and the block's menu behind it.",
506
+ "purposeFrom": "page",
507
+ "props": [
508
+ {
509
+ "name": "actions",
510
+ "takes": "a handler",
511
+ "required": false,
512
+ "note": "The caller's own rows — a link to the block, a comment on it."
513
+ },
514
+ {
515
+ "name": "editing",
516
+ "takes": "BlockHandleEditing",
517
+ "required": false,
518
+ "note": "Given by `EditorBlockHandle` (from `@estiva-app/ui/editor`)."
519
+ },
520
+ {
521
+ "name": "children",
522
+ "takes": "anything",
523
+ "required": true,
524
+ "note": "What the blocks are drawn in. Reading: a surface whose top-level elements carry `data-block-id`, as `RichText` draws."
525
+ },
526
+ {
527
+ "name": "className",
528
+ "takes": "text",
529
+ "required": false,
530
+ "note": null
531
+ }
532
+ ],
533
+ "variants": [],
534
+ "ownsBehaviours": [],
535
+ "status": "stable",
536
+ "migrationStage": null,
537
+ "docsId": "primitives-blockhandle--docs",
538
+ "storyId": "primitives-blockhandle--default",
539
+ "docPage": "src/BlockHandle.mdx",
540
+ "app": null,
541
+ "looks": []
542
+ },
461
543
  {
462
544
  "name": "BlockId",
463
545
  "repo": "estiva-ui",
@@ -1103,6 +1185,12 @@
1103
1185
  "required": false,
1104
1186
  "note": "Remember open or closed in this browser, under this key."
1105
1187
  },
1188
+ {
1189
+ "name": "icon",
1190
+ "takes": "anything",
1191
+ "required": false,
1192
+ "note": "16px, before the title; the arrow takes its place on hover — `SectionHeader`'s."
1193
+ },
1106
1194
  {
1107
1195
  "name": "trailing",
1108
1196
  "takes": "anything",
@@ -1115,6 +1203,18 @@
1115
1203
  "required": false,
1116
1204
  "note": "Beside the title, revealed on hover or focus — `SectionHeader`'s."
1117
1205
  },
1206
+ {
1207
+ "name": "menu",
1208
+ "takes": "anything",
1209
+ "required": false,
1210
+ "note": "Beside the actions, revealed with them: the section's \"More options\" menu, `MenuItem`s — `SectionHeader`'s."
1211
+ },
1212
+ {
1213
+ "name": "menuLabel",
1214
+ "takes": "text",
1215
+ "required": false,
1216
+ "note": "The ⋮'s accessible name — `SectionHeader`'s."
1217
+ },
1118
1218
  {
1119
1219
  "name": "showActions",
1120
1220
  "takes": "hover | always",
@@ -1668,6 +1768,12 @@
1668
1768
  "required": false,
1669
1769
  "note": "A chevron after the title, for a title that opens something."
1670
1770
  },
1771
+ {
1772
+ "name": "titleMenu",
1773
+ "takes": "anything",
1774
+ "required": false,
1775
+ "note": "The menu the title opens: `MenuItem`s — the column's views (Katerina, 5 October: Peek's Topics column, Topics or Folders)."
1776
+ },
1671
1777
  {
1672
1778
  "name": "actions",
1673
1779
  "takes": "anything",
@@ -1682,13 +1788,47 @@
1682
1788
  }
1683
1789
  ],
1684
1790
  "variants": [],
1685
- "ownsBehaviours": [],
1791
+ "ownsBehaviours": [
1792
+ {
1793
+ "id": "base-ui",
1794
+ "behaviour": "Is built on Base UI"
1795
+ },
1796
+ {
1797
+ "id": "role",
1798
+ "behaviour": "Says what it is to assistive technology"
1799
+ },
1800
+ {
1801
+ "id": "tab-stop",
1802
+ "behaviour": "Is reachable with Tab"
1803
+ }
1804
+ ],
1686
1805
  "status": "stable",
1687
1806
  "migrationStage": null,
1688
1807
  "docsId": "navigation-containerheader--docs",
1689
- "storyId": "navigation-containerheader--default",
1808
+ "storyId": "navigation-containerheader--with-title-menu",
1690
1809
  "docPage": "src/ContainerHeader.mdx",
1691
1810
  "app": null,
1811
+ "looks": [
1812
+ "cursor-pointer rounded-md transition-colors hover:bg-bg-hover"
1813
+ ]
1814
+ },
1815
+ {
1816
+ "name": "deleteBlock",
1817
+ "repo": "estiva-ui",
1818
+ "kind": "helper",
1819
+ "importPath": "@estiva-app/ui/editor",
1820
+ "sourceFile": "src/EditorBlockHandle.tsx",
1821
+ "purpose": "Removes the block, the caret where it was.",
1822
+ "purposeFrom": "comment",
1823
+ "props": [],
1824
+ "variants": [],
1825
+ "ownsBehaviours": [],
1826
+ "status": "stable",
1827
+ "migrationStage": null,
1828
+ "docsId": "primitives-editorblockhandle--docs",
1829
+ "storyId": "primitives-editorblockhandle--default",
1830
+ "docPage": null,
1831
+ "app": null,
1692
1832
  "looks": []
1693
1833
  },
1694
1834
  {
@@ -1864,6 +2004,25 @@
1864
2004
  "app": null,
1865
2005
  "looks": []
1866
2006
  },
2007
+ {
2008
+ "name": "duplicateBlock",
2009
+ "repo": "estiva-ui",
2010
+ "kind": "helper",
2011
+ "importPath": "@estiva-app/ui/editor",
2012
+ "sourceFile": "src/EditorBlockHandle.tsx",
2013
+ "purpose": "A copy of the block straight after it, the caret in the copy.",
2014
+ "purposeFrom": "comment",
2015
+ "props": [],
2016
+ "variants": [],
2017
+ "ownsBehaviours": [],
2018
+ "status": "stable",
2019
+ "migrationStage": null,
2020
+ "docsId": "primitives-editorblockhandle--docs",
2021
+ "storyId": "primitives-editorblockhandle--default",
2022
+ "docPage": null,
2023
+ "app": null,
2024
+ "looks": []
2025
+ },
1867
2026
  {
1868
2027
  "name": "EditableText",
1869
2028
  "repo": "estiva-ui",
@@ -1951,6 +2110,56 @@
1951
2110
  "rounded-md border border-transparent text-left transition-colors hover:border-border-default"
1952
2111
  ]
1953
2112
  },
2113
+ {
2114
+ "name": "EditorBlockHandle",
2115
+ "repo": "estiva-ui",
2116
+ "kind": "component",
2117
+ "importPath": "@estiva-app/ui/editor",
2118
+ "sourceFile": "src/EditorBlockHandle.tsx",
2119
+ "purpose": "`BlockHandle` in a Tiptap editor: drag the handle to move the block, press it for Turn into, Duplicate and Delete, then the app's rows.",
2120
+ "purposeFrom": "page",
2121
+ "props": [
2122
+ {
2123
+ "name": "editor",
2124
+ "takes": "Editor | null",
2125
+ "required": true,
2126
+ "note": "The editor. Not editable, or not there yet, and the handle reads: the caller's rows only."
2127
+ },
2128
+ {
2129
+ "name": "turnInto",
2130
+ "takes": "readonly BlockTurnInto[]",
2131
+ "required": false,
2132
+ "note": "Turn into's rows. Offered for a block of text — a paragraph, a heading, code."
2133
+ },
2134
+ {
2135
+ "name": "actions",
2136
+ "takes": "a handler",
2137
+ "required": false,
2138
+ "note": "The caller's own rows, after the editing ones."
2139
+ },
2140
+ {
2141
+ "name": "children",
2142
+ "takes": "anything",
2143
+ "required": true,
2144
+ "note": "The `EditorContent`."
2145
+ },
2146
+ {
2147
+ "name": "className",
2148
+ "takes": "text",
2149
+ "required": false,
2150
+ "note": null
2151
+ }
2152
+ ],
2153
+ "variants": [],
2154
+ "ownsBehaviours": [],
2155
+ "status": "stable",
2156
+ "migrationStage": null,
2157
+ "docsId": "primitives-editorblockhandle--docs",
2158
+ "storyId": "primitives-editorblockhandle--default",
2159
+ "docPage": "src/EditorBlockHandle.mdx",
2160
+ "app": null,
2161
+ "looks": []
2162
+ },
1954
2163
  {
1955
2164
  "name": "EmptyState",
1956
2165
  "repo": "estiva-ui",
@@ -3150,6 +3359,12 @@
3150
3359
  "required": false,
3151
3360
  "note": "A chevron after the title, as `ContainerHeader` draws it."
3152
3361
  },
3362
+ {
3363
+ "name": "titleMenu",
3364
+ "takes": "anything",
3365
+ "required": false,
3366
+ "note": "The menu the title opens — `ContainerHeader`'s: the column's views."
3367
+ },
3153
3368
  {
3154
3369
  "name": "actions",
3155
3370
  "takes": "anything",
@@ -3594,6 +3809,25 @@
3594
3809
  "app": null,
3595
3810
  "looks": []
3596
3811
  },
3812
+ {
3813
+ "name": "moveBlock",
3814
+ "repo": "estiva-ui",
3815
+ "kind": "helper",
3816
+ "importPath": "@estiva-app/ui/editor",
3817
+ "sourceFile": "src/EditorBlockHandle.tsx",
3818
+ "purpose": "Moves the block at `from` to `to` (a position between top-level blocks), the caret in it: what a drop in the gutter does, where the editor has no drop of its own.",
3819
+ "purposeFrom": "comment",
3820
+ "props": [],
3821
+ "variants": [],
3822
+ "ownsBehaviours": [],
3823
+ "status": "stable",
3824
+ "migrationStage": null,
3825
+ "docsId": "primitives-editorblockhandle--docs",
3826
+ "storyId": "primitives-editorblockhandle--default",
3827
+ "docPage": null,
3828
+ "app": null,
3829
+ "looks": []
3830
+ },
3597
3831
  {
3598
3832
  "name": "NavItem",
3599
3833
  "repo": "estiva-ui",
@@ -3829,6 +4063,18 @@
3829
4063
  "required": false,
3830
4064
  "note": "Beside the title, shown on hover or focus: \"Open\", typically."
3831
4065
  },
4066
+ {
4067
+ "name": "menu",
4068
+ "takes": "anything",
4069
+ "required": false,
4070
+ "note": "The group's \"More options\" menu, after its actions: `MenuItem`s."
4071
+ },
4072
+ {
4073
+ "name": "icon",
4074
+ "takes": "anything",
4075
+ "required": false,
4076
+ "note": "16px, before the title; the arrow takes its place on hover."
4077
+ },
3832
4078
  {
3833
4079
  "name": "defaultOpen",
3834
4080
  "takes": "true/false",
@@ -4131,8 +4377,8 @@
4131
4377
  "docPage": "src/PersonTrigger.mdx",
4132
4378
  "app": null,
4133
4379
  "looks": [
4134
- "cursor-pointer rounded-md text-body-2 text-text-primary transition-colors hover:bg-bg-hover",
4135
- "cursor-pointer rounded-md text-body-2 text-text-primary transition-colors hover:bg-bg-hover bg-bg-hover"
4380
+ "text-body-2 text-text-primary cursor-pointer rounded-md transition-colors hover:bg-bg-hover",
4381
+ "text-body-2 text-text-primary cursor-pointer rounded-md transition-colors hover:bg-bg-hover bg-bg-hover"
4136
4382
  ]
4137
4383
  },
4138
4384
  {
@@ -5008,6 +5254,12 @@
5008
5254
  "required": false,
5009
5255
  "note": "Collapsible: draws the chevron and makes the title a button that toggles."
5010
5256
  },
5257
+ {
5258
+ "name": "icon",
5259
+ "takes": "anything",
5260
+ "required": false,
5261
+ "note": "16px, stroke 1.5, before the title — a folder's (Katerina, 5 October: Peek's Folders, the standard heading with a folder icon)."
5262
+ },
5011
5263
  {
5012
5264
  "name": "isExpanded",
5013
5265
  "takes": "true/false",
@@ -5032,6 +5284,18 @@
5032
5284
  "required": false,
5033
5285
  "note": "Right-aligned, in the order given."
5034
5286
  },
5287
+ {
5288
+ "name": "menu",
5289
+ "takes": "anything",
5290
+ "required": false,
5291
+ "note": "The heading's \"More options\" menu: `MenuItem`s — what acts on the whole section, a folder's Rename or Archive (Katerina, 5 October: Peek's Folders page goes, and its folder actions move onto the folder's heading)."
5292
+ },
5293
+ {
5294
+ "name": "menuLabel",
5295
+ "takes": "text",
5296
+ "required": false,
5297
+ "note": "The ⋮'s accessible name. Default \"More options for\" the title."
5298
+ },
5035
5299
  {
5036
5300
  "name": "showActions",
5037
5301
  "takes": "hover | always",
@@ -5095,11 +5359,11 @@
5095
5359
  "docPage": "src/SectionHeader.mdx",
5096
5360
  "app": null,
5097
5361
  "looks": [
5362
+ "opacity-0 transition-[opacity,transform] duration-150 group-hover:opacity-100",
5363
+ "opacity-0 transition-[opacity,transform] duration-150 group-hover:opacity-100 rotate-90",
5098
5364
  "transition-transform duration-150 text-text-secondary rotate-90",
5099
5365
  "rounded-lg transition-colors text-text-secondary hover:text-text-primary",
5100
- "rounded-lg transition-colors text-text-secondary hover:text-text-primary hover:bg-bg-hover",
5101
- "-mr-1 opacity-0 transition-opacity focus-within:opacity-100 group-hover:opacity-100",
5102
- "opacity-0 transition-opacity focus-within:opacity-100 group-hover:opacity-100"
5366
+ "rounded-lg transition-colors text-text-secondary hover:text-text-primary hover:bg-bg-hover"
5103
5367
  ]
5104
5368
  },
5105
5369
  {
@@ -5160,19 +5424,15 @@
5160
5424
  "docPage": "src/SectionLabel.mdx",
5161
5425
  "app": null,
5162
5426
  "looks": [
5163
- "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest text-text-secondary",
5164
- "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest text-text-secondary truncate",
5165
- "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest text-text-muted",
5166
- "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest text-text-muted truncate",
5167
- "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest text-text-primary",
5168
- "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest text-text-primary truncate",
5169
- "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest text-text-secondary",
5170
- "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest text-text-secondary truncate",
5171
- "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest text-text-muted",
5172
- "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest text-text-muted truncate",
5173
- "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest text-text-primary",
5174
- "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest text-text-primary truncate",
5175
- "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest",
5427
+ "text-h5 leading-3 text-text-secondary truncate",
5428
+ "text-h5 leading-3 text-text-muted truncate",
5429
+ "text-h5 leading-3 text-text-primary truncate",
5430
+ "text-h5 leading-3 leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest text-text-secondary",
5431
+ "text-h5 leading-3 leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest text-text-secondary truncate",
5432
+ "text-h5 leading-3 leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest text-text-muted",
5433
+ "text-h5 leading-3 leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest text-text-muted truncate",
5434
+ "text-h5 leading-3 leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest text-text-primary",
5435
+ "text-h5 leading-3 leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest text-text-primary truncate",
5176
5436
  "leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest"
5177
5437
  ]
5178
5438
  },
@@ -5532,6 +5792,25 @@
5532
5792
  "app": null,
5533
5793
  "looks": []
5534
5794
  },
5795
+ {
5796
+ "name": "startBlockDrag",
5797
+ "repo": "estiva-ui",
5798
+ "kind": "helper",
5799
+ "importPath": "@estiva-app/ui/editor",
5800
+ "sourceFile": "src/EditorBlockHandle.tsx",
5801
+ "purpose": "Starts the browser's own drag of the block, as ProseMirror starts one of a selected node: the block selected, its slice on `view.dragging` with `move`, and its HTML on the transfer.",
5802
+ "purposeFrom": "comment",
5803
+ "props": [],
5804
+ "variants": [],
5805
+ "ownsBehaviours": [],
5806
+ "status": "stable",
5807
+ "migrationStage": null,
5808
+ "docsId": "primitives-editorblockhandle--docs",
5809
+ "storyId": "primitives-editorblockhandle--default",
5810
+ "docPage": null,
5811
+ "app": null,
5812
+ "looks": []
5813
+ },
5535
5814
  {
5536
5815
  "name": "SuggestionMenu",
5537
5816
  "repo": "estiva-ui",
@@ -0,0 +1,58 @@
1
+ import { Meta, Canvas } from '@storybook/addon-docs/blocks'
2
+ import * as BlockHandleStories from './BlockHandle.stories'
3
+
4
+ <Meta of={BlockHandleStories} />
5
+
6
+ # BlockHandle
7
+
8
+ The handle left of the block under the pointer, as a block-document page has
9
+ it, and the block's menu behind it. Over a document being read, the menu is
10
+ the app's rows only — a link to the block, a comment on it.
11
+
12
+ <Canvas of={BlockHandleStories.Default} />
13
+
14
+ ## When
15
+
16
+ - **A document made of blocks, read by someone who cannot edit it**, when the
17
+ app has something to offer on one block: a link to it, a comment on it.
18
+
19
+ ## When not
20
+
21
+ - The same document being edited → **EditorBlockHandle**, from
22
+ `@estiva-app/ui/editor`: the same handle, which also drags and has Turn
23
+ into, Duplicate and Delete.
24
+ - Reading a document with nothing to offer on one block → **RichText** alone.
25
+
26
+ ## How
27
+
28
+ ```tsx
29
+ import { BlockHandle, MenuItem, RichText, type HandleBlock, type RichTextBlock } from '@estiva-app/ui'
30
+
31
+ const blocks: RichTextBlock[] = [{ type: 'paragraph', id: 'b1', inline: [{ text: 'Label', marks: [] }] }]
32
+ const rows = (block: HandleBlock) => <MenuItem label="Copy link" onClick={() => navigator.clipboard.writeText(`#${block.id}`)} />
33
+
34
+ <BlockHandle actions={rows}>
35
+ <RichText blocks={blocks} />
36
+ </BlockHandle>
37
+ ```
38
+
39
+ - **Leave room on the left.** The handle sits 28px left of the content, in
40
+ the page's margin, so wrapping the content moves nothing. A surface flush
41
+ with its container's left edge clips it.
42
+ - `children` is a surface whose top-level elements carry `data-block-id`, as
43
+ `RichText` draws them. `HandleBlock.id` is that §13.3 id: what a link or a
44
+ comment addresses.
45
+ - With no `actions`, or `actions` that return nothing for a block, no handle
46
+ is drawn.
47
+ - **In the main entry on purpose**: a reader must not download an editor.
48
+ `editing` is how `EditorBlockHandle` makes this part edit, not a prop to
49
+ fill by hand.
50
+
51
+ ## What it owns
52
+
53
+ It does these by itself. Write none of them around it.
54
+
55
+ | It does | So you never write | Refused when written by hand |
56
+ |---|---|---|
57
+ | Finds the block under the pointer, by height: the gap between two blocks belongs to the one above | a pointer handler on each block | |
58
+ | Centres the handle on the block's first line, a heading's or a paragraph's | a position for the handle | |
@@ -0,0 +1,44 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import { IconLink, IconMessage } from '@tabler/icons-react'
3
+ import { BlockHandle, type HandleBlock } from './BlockHandle'
4
+ import { MenuItem } from './Menu'
5
+ import { RichText, type RichTextBlock } from './RichText'
6
+
7
+ /**
8
+ * The handle left of the block under the pointer, over a document being read:
9
+ * press it for the app's rows. In an editor, `EditorBlockHandle` adds the drag
10
+ * and the editing rows.
11
+ */
12
+ const meta = {
13
+ title: 'Primitives/BlockHandle',
14
+ // The handle sits 28px left of the content, in the page's margin.
15
+ parameters: { layout: 'padded' },
16
+ } satisfies Meta
17
+
18
+ export default meta
19
+ type Story = StoryObj
20
+
21
+ /** The rows an app adds: here they only say which block they were pressed on. */
22
+ function rows(block: HandleBlock) {
23
+ return (
24
+ <>
25
+ <MenuItem label="Copy link" leading={<IconLink size={16} stroke={1.5} className="text-text-secondary" />} onClick={() => console.info('copy link', block.id)} />
26
+ <MenuItem label="Comment" leading={<IconMessage size={16} stroke={1.5} className="text-text-secondary" />} onClick={() => console.info('comment', block.id)} />
27
+ </>
28
+ )
29
+ }
30
+
31
+ const READING: RichTextBlock[] = [
32
+ { type: 'heading', level: 1, id: 'b1', inline: [{ text: 'A heading', marks: [] }] },
33
+ { type: 'paragraph', id: 'b2', inline: [{ text: 'Someone who cannot edit this still gets the handle, with the app’s rows only.', marks: [] }] },
34
+ { type: 'paragraph', id: 'b3', inline: [{ text: 'The second paragraph.', marks: [] }] },
35
+ ]
36
+
37
+ /** Reading: no drag and no editing rows — only what the app adds. */
38
+ export const Default: Story = {
39
+ render: () => (
40
+ <BlockHandle actions={rows} className="ml-8 max-w-[560px]">
41
+ <RichText blocks={READING} />
42
+ </BlockHandle>
43
+ ),
44
+ }