@estiva-app/ui 0.51.0 → 0.52.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/dist/ComposerTriggers.d.ts +169 -0
- package/dist/ComposerTriggers.d.ts.map +1 -0
- package/dist/editor.d.ts +1 -0
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +313 -12
- package/dist/editor.js.map +4 -4
- package/package.json +6 -1
- package/registry.json +314 -3
- package/src/ComposerTriggers.mdx +108 -0
- package/src/ComposerTriggers.stories.tsx +79 -0
- package/src/ComposerTriggers.test.tsx +207 -0
- package/src/ComposerTriggers.tsx +507 -0
- package/src/editor.ts +29 -0
- package/src/registry/registry.test.ts +8 -7
- package/stories/Choosing.mdx +1 -0
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.
|
|
8
|
-
"exports":
|
|
9
|
-
"typeExports":
|
|
7
|
+
"packageVersion": "0.52.0",
|
|
8
|
+
"exports": 120,
|
|
9
|
+
"typeExports": 128,
|
|
10
10
|
"files": null
|
|
11
11
|
},
|
|
12
12
|
"storybook": {
|
|
@@ -15,6 +15,25 @@
|
|
|
15
15
|
"devUrl": "http://localhost:6008"
|
|
16
16
|
},
|
|
17
17
|
"entries": [
|
|
18
|
+
{
|
|
19
|
+
"name": "applyFormat",
|
|
20
|
+
"repo": "estiva-ui",
|
|
21
|
+
"kind": "helper",
|
|
22
|
+
"importPath": "@estiva-app/ui/editor",
|
|
23
|
+
"sourceFile": "src/ComposerTriggers.tsx",
|
|
24
|
+
"purpose": "Apply one format to the current block. `set*` wherever Tiptap has one: choosing Heading should make a heading, not turn one off.",
|
|
25
|
+
"purposeFrom": "comment",
|
|
26
|
+
"props": [],
|
|
27
|
+
"variants": [],
|
|
28
|
+
"ownsBehaviours": [],
|
|
29
|
+
"status": "stable",
|
|
30
|
+
"migrationStage": null,
|
|
31
|
+
"docsId": "primitives-composertriggers--docs",
|
|
32
|
+
"storyId": "primitives-composertriggers--default",
|
|
33
|
+
"docPage": null,
|
|
34
|
+
"app": null,
|
|
35
|
+
"looks": []
|
|
36
|
+
},
|
|
18
37
|
{
|
|
19
38
|
"name": "AppShell",
|
|
20
39
|
"repo": "estiva-ui",
|
|
@@ -583,6 +602,25 @@
|
|
|
583
602
|
"signal:bg-transparent signal:border signal:border-border-default signal:text-text-primary signal:shadow-none signal:hover:bg-success-muted signal:hover:border-success-outline signal:hover:text-success-default signal:transition-colors"
|
|
584
603
|
]
|
|
585
604
|
},
|
|
605
|
+
{
|
|
606
|
+
"name": "CaptionedReference",
|
|
607
|
+
"repo": "estiva-ui",
|
|
608
|
+
"kind": "component",
|
|
609
|
+
"importPath": "@estiva-app/ui/editor",
|
|
610
|
+
"sourceFile": "src/ComposerTriggers.tsx",
|
|
611
|
+
"purpose": "A reference mid-sentence, drawn as `label: snippet` and written as its `uri` verbatim (SPEC §13.1).",
|
|
612
|
+
"purposeFrom": "comment",
|
|
613
|
+
"props": [],
|
|
614
|
+
"variants": [],
|
|
615
|
+
"ownsBehaviours": [],
|
|
616
|
+
"status": "stable",
|
|
617
|
+
"migrationStage": null,
|
|
618
|
+
"docsId": "primitives-composertriggers--docs",
|
|
619
|
+
"storyId": "primitives-composertriggers--default",
|
|
620
|
+
"docPage": null,
|
|
621
|
+
"app": null,
|
|
622
|
+
"looks": []
|
|
623
|
+
},
|
|
586
624
|
{
|
|
587
625
|
"name": "Card",
|
|
588
626
|
"repo": "estiva-ui",
|
|
@@ -2176,6 +2214,63 @@
|
|
|
2176
2214
|
"app": null,
|
|
2177
2215
|
"looks": []
|
|
2178
2216
|
},
|
|
2217
|
+
{
|
|
2218
|
+
"name": "filterPeople",
|
|
2219
|
+
"repo": "estiva-ui",
|
|
2220
|
+
"kind": "helper",
|
|
2221
|
+
"importPath": "@estiva-app/ui/editor",
|
|
2222
|
+
"sourceFile": "src/ComposerTriggers.tsx",
|
|
2223
|
+
"purpose": "The people whose name holds the query, the first {@link PEOPLE_LIMIT} of them.",
|
|
2224
|
+
"purposeFrom": "comment",
|
|
2225
|
+
"props": [],
|
|
2226
|
+
"variants": [],
|
|
2227
|
+
"ownsBehaviours": [],
|
|
2228
|
+
"status": "stable",
|
|
2229
|
+
"migrationStage": null,
|
|
2230
|
+
"docsId": "primitives-composertriggers--docs",
|
|
2231
|
+
"storyId": "primitives-composertriggers--default",
|
|
2232
|
+
"docPage": null,
|
|
2233
|
+
"app": null,
|
|
2234
|
+
"looks": []
|
|
2235
|
+
},
|
|
2236
|
+
{
|
|
2237
|
+
"name": "filterReferences",
|
|
2238
|
+
"repo": "estiva-ui",
|
|
2239
|
+
"kind": "helper",
|
|
2240
|
+
"importPath": "@estiva-app/ui/editor",
|
|
2241
|
+
"sourceFile": "src/ComposerTriggers.tsx",
|
|
2242
|
+
"purpose": "The references whose label or text holds the query — the last {@link REFERENCE_LIMIT}, in the order given: a thread is unbounded, and a list that grew with it would be a hundred rows of prose.",
|
|
2243
|
+
"purposeFrom": "comment",
|
|
2244
|
+
"props": [],
|
|
2245
|
+
"variants": [],
|
|
2246
|
+
"ownsBehaviours": [],
|
|
2247
|
+
"status": "stable",
|
|
2248
|
+
"migrationStage": null,
|
|
2249
|
+
"docsId": "primitives-composertriggers--docs",
|
|
2250
|
+
"storyId": "primitives-composertriggers--default",
|
|
2251
|
+
"docPage": null,
|
|
2252
|
+
"app": null,
|
|
2253
|
+
"looks": []
|
|
2254
|
+
},
|
|
2255
|
+
{
|
|
2256
|
+
"name": "filterSlashSections",
|
|
2257
|
+
"repo": "estiva-ui",
|
|
2258
|
+
"kind": "helper",
|
|
2259
|
+
"importPath": "@estiva-app/ui/editor",
|
|
2260
|
+
"sourceFile": "src/ComposerTriggers.tsx",
|
|
2261
|
+
"purpose": "The sections narrowed to a query — the text typed after the `/`.",
|
|
2262
|
+
"purposeFrom": "comment",
|
|
2263
|
+
"props": [],
|
|
2264
|
+
"variants": [],
|
|
2265
|
+
"ownsBehaviours": [],
|
|
2266
|
+
"status": "stable",
|
|
2267
|
+
"migrationStage": null,
|
|
2268
|
+
"docsId": "primitives-composertriggers--docs",
|
|
2269
|
+
"storyId": "primitives-composertriggers--default",
|
|
2270
|
+
"docPage": null,
|
|
2271
|
+
"app": null,
|
|
2272
|
+
"looks": []
|
|
2273
|
+
},
|
|
2179
2274
|
{
|
|
2180
2275
|
"name": "Form",
|
|
2181
2276
|
"repo": "estiva-ui",
|
|
@@ -2239,6 +2334,44 @@
|
|
|
2239
2334
|
"app": null,
|
|
2240
2335
|
"looks": []
|
|
2241
2336
|
},
|
|
2337
|
+
{
|
|
2338
|
+
"name": "FORMATS",
|
|
2339
|
+
"repo": "estiva-ui",
|
|
2340
|
+
"kind": "helper",
|
|
2341
|
+
"importPath": "@estiva-app/ui/editor",
|
|
2342
|
+
"sourceFile": "src/ComposerTriggers.tsx",
|
|
2343
|
+
"purpose": "The formats `/` offers, in order. The hint is what you would type, not a key chord.",
|
|
2344
|
+
"purposeFrom": "comment",
|
|
2345
|
+
"props": [],
|
|
2346
|
+
"variants": [],
|
|
2347
|
+
"ownsBehaviours": [],
|
|
2348
|
+
"status": "stable",
|
|
2349
|
+
"migrationStage": null,
|
|
2350
|
+
"docsId": "primitives-composertriggers--docs",
|
|
2351
|
+
"storyId": "primitives-composertriggers--default",
|
|
2352
|
+
"docPage": null,
|
|
2353
|
+
"app": null,
|
|
2354
|
+
"looks": []
|
|
2355
|
+
},
|
|
2356
|
+
{
|
|
2357
|
+
"name": "formatSection",
|
|
2358
|
+
"repo": "estiva-ui",
|
|
2359
|
+
"kind": "helper",
|
|
2360
|
+
"importPath": "@estiva-app/ui/editor",
|
|
2361
|
+
"sourceFile": "src/ComposerTriggers.tsx",
|
|
2362
|
+
"purpose": "The Format section every app's `/` opens with.",
|
|
2363
|
+
"purposeFrom": "comment",
|
|
2364
|
+
"props": [],
|
|
2365
|
+
"variants": [],
|
|
2366
|
+
"ownsBehaviours": [],
|
|
2367
|
+
"status": "stable",
|
|
2368
|
+
"migrationStage": null,
|
|
2369
|
+
"docsId": "primitives-composertriggers--docs",
|
|
2370
|
+
"storyId": "primitives-composertriggers--default",
|
|
2371
|
+
"docPage": null,
|
|
2372
|
+
"app": null,
|
|
2373
|
+
"looks": []
|
|
2374
|
+
},
|
|
2242
2375
|
{
|
|
2243
2376
|
"name": "hueFor",
|
|
2244
2377
|
"repo": "estiva-ui",
|
|
@@ -3725,6 +3858,25 @@
|
|
|
3725
3858
|
"app": null,
|
|
3726
3859
|
"looks": []
|
|
3727
3860
|
},
|
|
3861
|
+
{
|
|
3862
|
+
"name": "peopleMenu",
|
|
3863
|
+
"repo": "estiva-ui",
|
|
3864
|
+
"kind": "helper",
|
|
3865
|
+
"importPath": "@estiva-app/ui/editor",
|
|
3866
|
+
"sourceFile": "src/ComposerTriggers.tsx",
|
|
3867
|
+
"purpose": "The people list `@` and `!@` open: the face, the name, the second line, and the Enter hint while highlighted — `MenuItem` at its tall size.",
|
|
3868
|
+
"purposeFrom": "comment",
|
|
3869
|
+
"props": [],
|
|
3870
|
+
"variants": [],
|
|
3871
|
+
"ownsBehaviours": [],
|
|
3872
|
+
"status": "stable",
|
|
3873
|
+
"migrationStage": null,
|
|
3874
|
+
"docsId": "primitives-composertriggers--docs",
|
|
3875
|
+
"storyId": "primitives-composertriggers--default",
|
|
3876
|
+
"docPage": null,
|
|
3877
|
+
"app": null,
|
|
3878
|
+
"looks": []
|
|
3879
|
+
},
|
|
3728
3880
|
{
|
|
3729
3881
|
"name": "Person",
|
|
3730
3882
|
"repo": "estiva-ui",
|
|
@@ -3775,6 +3927,25 @@
|
|
|
3775
3927
|
"app": null,
|
|
3776
3928
|
"looks": []
|
|
3777
3929
|
},
|
|
3930
|
+
{
|
|
3931
|
+
"name": "PersonMention",
|
|
3932
|
+
"repo": "estiva-ui",
|
|
3933
|
+
"kind": "component",
|
|
3934
|
+
"importPath": "@estiva-app/ui/editor",
|
|
3935
|
+
"sourceFile": "src/ComposerTriggers.tsx",
|
|
3936
|
+
"purpose": "`@` — a person, as a chip that writes their key (SPEC §13.1).",
|
|
3937
|
+
"purposeFrom": "comment",
|
|
3938
|
+
"props": [],
|
|
3939
|
+
"variants": [],
|
|
3940
|
+
"ownsBehaviours": [],
|
|
3941
|
+
"status": "stable",
|
|
3942
|
+
"migrationStage": null,
|
|
3943
|
+
"docsId": "primitives-composertriggers--docs",
|
|
3944
|
+
"storyId": "primitives-composertriggers--default",
|
|
3945
|
+
"docPage": null,
|
|
3946
|
+
"app": null,
|
|
3947
|
+
"looks": []
|
|
3948
|
+
},
|
|
3778
3949
|
{
|
|
3779
3950
|
"name": "PersonTrigger",
|
|
3780
3951
|
"repo": "estiva-ui",
|
|
@@ -4410,6 +4581,44 @@
|
|
|
4410
4581
|
"app": null,
|
|
4411
4582
|
"looks": []
|
|
4412
4583
|
},
|
|
4584
|
+
{
|
|
4585
|
+
"name": "referenceMenu",
|
|
4586
|
+
"repo": "estiva-ui",
|
|
4587
|
+
"kind": "helper",
|
|
4588
|
+
"importPath": "@estiva-app/ui/editor",
|
|
4589
|
+
"sourceFile": "src/ComposerTriggers.tsx",
|
|
4590
|
+
"purpose": "The list `[` opens: one section of `label: snippet` rows, the same box as the people list.",
|
|
4591
|
+
"purposeFrom": "comment",
|
|
4592
|
+
"props": [
|
|
4593
|
+
{
|
|
4594
|
+
"name": "ariaLabel",
|
|
4595
|
+
"takes": "text",
|
|
4596
|
+
"required": true,
|
|
4597
|
+
"note": "Names the list, e.g. what can be referenced."
|
|
4598
|
+
},
|
|
4599
|
+
{
|
|
4600
|
+
"name": "sectionLabel",
|
|
4601
|
+
"takes": "text",
|
|
4602
|
+
"required": true,
|
|
4603
|
+
"note": "The section's heading."
|
|
4604
|
+
},
|
|
4605
|
+
{
|
|
4606
|
+
"name": "icon",
|
|
4607
|
+
"takes": "anything",
|
|
4608
|
+
"required": true,
|
|
4609
|
+
"note": "At each row's start: a 16px icon."
|
|
4610
|
+
}
|
|
4611
|
+
],
|
|
4612
|
+
"variants": [],
|
|
4613
|
+
"ownsBehaviours": [],
|
|
4614
|
+
"status": "stable",
|
|
4615
|
+
"migrationStage": null,
|
|
4616
|
+
"docsId": "primitives-composertriggers--docs",
|
|
4617
|
+
"storyId": "primitives-composertriggers--default",
|
|
4618
|
+
"docPage": null,
|
|
4619
|
+
"app": null,
|
|
4620
|
+
"looks": []
|
|
4621
|
+
},
|
|
4413
4622
|
{
|
|
4414
4623
|
"name": "ReferenceNode",
|
|
4415
4624
|
"repo": "estiva-ui",
|
|
@@ -4429,6 +4638,25 @@
|
|
|
4429
4638
|
"app": null,
|
|
4430
4639
|
"looks": []
|
|
4431
4640
|
},
|
|
4641
|
+
{
|
|
4642
|
+
"name": "ReferenceTrigger",
|
|
4643
|
+
"repo": "estiva-ui",
|
|
4644
|
+
"kind": "component",
|
|
4645
|
+
"importPath": "@estiva-app/ui/editor",
|
|
4646
|
+
"sourceFile": "src/ComposerTriggers.tsx",
|
|
4647
|
+
"purpose": "`[` — reference something, inserted as a {@link CaptionedReference} chip.",
|
|
4648
|
+
"purposeFrom": "comment",
|
|
4649
|
+
"props": [],
|
|
4650
|
+
"variants": [],
|
|
4651
|
+
"ownsBehaviours": [],
|
|
4652
|
+
"status": "stable",
|
|
4653
|
+
"migrationStage": null,
|
|
4654
|
+
"docsId": "primitives-composertriggers--docs",
|
|
4655
|
+
"storyId": "primitives-composertriggers--default",
|
|
4656
|
+
"docPage": null,
|
|
4657
|
+
"app": null,
|
|
4658
|
+
"looks": []
|
|
4659
|
+
},
|
|
4432
4660
|
{
|
|
4433
4661
|
"name": "RichText",
|
|
4434
4662
|
"repo": "estiva-ui",
|
|
@@ -5168,6 +5396,25 @@
|
|
|
5168
5396
|
"app": null,
|
|
5169
5397
|
"looks": []
|
|
5170
5398
|
},
|
|
5399
|
+
{
|
|
5400
|
+
"name": "SlashCommands",
|
|
5401
|
+
"repo": "estiva-ui",
|
|
5402
|
+
"kind": "component",
|
|
5403
|
+
"importPath": "@estiva-app/ui/editor",
|
|
5404
|
+
"sourceFile": "src/ComposerTriggers.tsx",
|
|
5405
|
+
"purpose": "`/` — format the line or insert something, at the caret, mid-sentence too.",
|
|
5406
|
+
"purposeFrom": "comment",
|
|
5407
|
+
"props": [],
|
|
5408
|
+
"variants": [],
|
|
5409
|
+
"ownsBehaviours": [],
|
|
5410
|
+
"status": "stable",
|
|
5411
|
+
"migrationStage": null,
|
|
5412
|
+
"docsId": "primitives-composertriggers--docs",
|
|
5413
|
+
"storyId": "primitives-composertriggers--default",
|
|
5414
|
+
"docPage": null,
|
|
5415
|
+
"app": null,
|
|
5416
|
+
"looks": []
|
|
5417
|
+
},
|
|
5171
5418
|
{
|
|
5172
5419
|
"name": "SuggestionMenu",
|
|
5173
5420
|
"repo": "estiva-ui",
|
|
@@ -6175,6 +6422,25 @@
|
|
|
6175
6422
|
"app": null,
|
|
6176
6423
|
"looks": []
|
|
6177
6424
|
},
|
|
6425
|
+
{
|
|
6426
|
+
"name": "typeTrigger",
|
|
6427
|
+
"repo": "estiva-ui",
|
|
6428
|
+
"kind": "helper",
|
|
6429
|
+
"importPath": "@estiva-app/ui/editor",
|
|
6430
|
+
"sourceFile": "src/ComposerTriggers.tsx",
|
|
6431
|
+
"purpose": "A row that types another trigger, so its own list opens — `@` from `/`.",
|
|
6432
|
+
"purposeFrom": "comment",
|
|
6433
|
+
"props": [],
|
|
6434
|
+
"variants": [],
|
|
6435
|
+
"ownsBehaviours": [],
|
|
6436
|
+
"status": "stable",
|
|
6437
|
+
"migrationStage": null,
|
|
6438
|
+
"docsId": "primitives-composertriggers--docs",
|
|
6439
|
+
"storyId": "primitives-composertriggers--default",
|
|
6440
|
+
"docPage": null,
|
|
6441
|
+
"app": null,
|
|
6442
|
+
"looks": []
|
|
6443
|
+
},
|
|
6178
6444
|
{
|
|
6179
6445
|
"name": "UnknownBlock",
|
|
6180
6446
|
"repo": "estiva-ui",
|
|
@@ -6194,6 +6460,51 @@
|
|
|
6194
6460
|
"app": null,
|
|
6195
6461
|
"looks": []
|
|
6196
6462
|
},
|
|
6463
|
+
{
|
|
6464
|
+
"name": "UrgentPersonMention",
|
|
6465
|
+
"repo": "estiva-ui",
|
|
6466
|
+
"kind": "component",
|
|
6467
|
+
"importPath": "@estiva-app/ui/editor",
|
|
6468
|
+
"sourceFile": "src/ComposerTriggers.tsx",
|
|
6469
|
+
"purpose": "`!@` — a person, urgently: the same chip in the urgent tone.",
|
|
6470
|
+
"purposeFrom": "comment",
|
|
6471
|
+
"props": [],
|
|
6472
|
+
"variants": [],
|
|
6473
|
+
"ownsBehaviours": [],
|
|
6474
|
+
"status": "stable",
|
|
6475
|
+
"migrationStage": null,
|
|
6476
|
+
"docsId": "primitives-composertriggers--docs",
|
|
6477
|
+
"storyId": "primitives-composertriggers--default",
|
|
6478
|
+
"docPage": null,
|
|
6479
|
+
"app": null,
|
|
6480
|
+
"looks": []
|
|
6481
|
+
},
|
|
6482
|
+
{
|
|
6483
|
+
"name": "urgentPubkeys",
|
|
6484
|
+
"repo": "estiva-ui",
|
|
6485
|
+
"kind": "helper",
|
|
6486
|
+
"importPath": "@estiva-app/ui/editor",
|
|
6487
|
+
"sourceFile": "src/ComposerTriggers.tsx",
|
|
6488
|
+
"purpose": "The keys the document's urgent mentions name — what the write turns into `[\"urgent\", <pubkey>]` tags.",
|
|
6489
|
+
"purposeFrom": "comment",
|
|
6490
|
+
"props": [
|
|
6491
|
+
{
|
|
6492
|
+
"name": "state",
|
|
6493
|
+
"takes": "{ doc: Doc }",
|
|
6494
|
+
"required": true,
|
|
6495
|
+
"note": null
|
|
6496
|
+
}
|
|
6497
|
+
],
|
|
6498
|
+
"variants": [],
|
|
6499
|
+
"ownsBehaviours": [],
|
|
6500
|
+
"status": "stable",
|
|
6501
|
+
"migrationStage": null,
|
|
6502
|
+
"docsId": "primitives-composertriggers--docs",
|
|
6503
|
+
"storyId": "primitives-composertriggers--default",
|
|
6504
|
+
"docPage": null,
|
|
6505
|
+
"app": null,
|
|
6506
|
+
"looks": []
|
|
6507
|
+
},
|
|
6197
6508
|
{
|
|
6198
6509
|
"name": "useToast",
|
|
6199
6510
|
"repo": "estiva-ui",
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Meta, Canvas } from '@storybook/addon-docs/blocks'
|
|
2
|
+
import * as ComposerTriggersStories from './ComposerTriggers.stories'
|
|
3
|
+
|
|
4
|
+
<Meta of={ComposerTriggersStories} />
|
|
5
|
+
|
|
6
|
+
# ComposerTriggers
|
|
7
|
+
|
|
8
|
+
The four keys a composer shares across apps: `@` and `!@` to mention a person,
|
|
9
|
+
`[` to reference something, `/` to format the line or insert something. Each
|
|
10
|
+
opens a `SuggestionMenu` at the caret; a pick inserts a chip.
|
|
11
|
+
|
|
12
|
+
<Canvas of={ComposerTriggersStories.Default} />
|
|
13
|
+
|
|
14
|
+
## When
|
|
15
|
+
|
|
16
|
+
- **`PersonMention` and `UrgentPersonMention`** — any editor whose text names
|
|
17
|
+
people. The urgent one is the same chip in the urgent tone.
|
|
18
|
+
- **`CaptionedReference` and `ReferenceTrigger`** — referencing something the
|
|
19
|
+
app can list, drawn as who wrote it and its opening words.
|
|
20
|
+
- **`SlashCommands`** — `formatSection()` first, then only the rows the app can
|
|
21
|
+
perform. `formatSection('block')` makes Code a block, for an editor of a
|
|
22
|
+
block document.
|
|
23
|
+
|
|
24
|
+
## When not
|
|
25
|
+
|
|
26
|
+
- Formatting text already written → **SelectionToolbar**. Bold, italic and
|
|
27
|
+
underline are not in `/` for that reason.
|
|
28
|
+
- A list that is not opened by a key typed in an editor → **CommandPalette** or
|
|
29
|
+
**Menu**.
|
|
30
|
+
- Drawing a sent body → **RichText**. These are for editing only.
|
|
31
|
+
|
|
32
|
+
## How
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import { EditorContent, useEditor } from '@tiptap/react'
|
|
36
|
+
import StarterKit from '@tiptap/starter-kit'
|
|
37
|
+
import { IconSquareRounded } from '@tabler/icons-react'
|
|
38
|
+
import {
|
|
39
|
+
CaptionedReference,
|
|
40
|
+
PersonMention,
|
|
41
|
+
ReferenceTrigger,
|
|
42
|
+
SlashCommands,
|
|
43
|
+
UrgentPersonMention,
|
|
44
|
+
formatSection,
|
|
45
|
+
typeTrigger,
|
|
46
|
+
urgentPubkeys,
|
|
47
|
+
type CaptionedItem,
|
|
48
|
+
type MentionPerson,
|
|
49
|
+
} from '@estiva-app/ui/editor'
|
|
50
|
+
|
|
51
|
+
const people = (): MentionPerson[] => [{ id: 'one', name: 'Person one', pubkey: null }]
|
|
52
|
+
const items = (): CaptionedItem[] => [{ id: 'one', label: 'Person one', snippet: 'Item one', uri: 'nostr:item' }]
|
|
53
|
+
|
|
54
|
+
const editor = useEditor({
|
|
55
|
+
extensions: [
|
|
56
|
+
StarterKit,
|
|
57
|
+
PersonMention.configure({ people }),
|
|
58
|
+
UrgentPersonMention.configure({ people }),
|
|
59
|
+
CaptionedReference.configure({ icon: <IconSquareRounded size={14} stroke={1.5} /> }),
|
|
60
|
+
ReferenceTrigger.configure({ items, ariaLabel: 'Items', sectionLabel: 'Items', icon: <IconSquareRounded size={16} stroke={1.5} /> }),
|
|
61
|
+
SlashCommands.configure({
|
|
62
|
+
sections: [formatSection(), { label: 'Insert', commands: [{ key: 'insert-@', row: { label: 'Mention', shortcut: '@' }, run: typeTrigger('@') }] }],
|
|
63
|
+
}),
|
|
64
|
+
],
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
const urgent = editor ? urgentPubkeys(editor) : undefined
|
|
68
|
+
|
|
69
|
+
<EditorContent editor={editor} />
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
- **The body is written by `@estiva-app/conversation`, not here.** A mention
|
|
73
|
+
chip carries `pubkey` and `label` for its `mentionText`; a reference chip
|
|
74
|
+
carries the `uri` the app built with that package, written back verbatim. The
|
|
75
|
+
urgency is not in the text: hand `urgentPubkeys(editor)` to `urgentTagsFor`.
|
|
76
|
+
- **The lists ask each time the query changes.** `people` and `items` are
|
|
77
|
+
functions, so they read the app's current data. `@` shows the first six
|
|
78
|
+
names holding the query; `[` the last six whose label or `search` (default:
|
|
79
|
+
`snippet`) holds it.
|
|
80
|
+
- **An app's own node name:** `CaptionedReference.extend({ name: 'yourName' })`
|
|
81
|
+
and `ReferenceTrigger.configure({ nodeName: 'yourName' })`. The chip's HTML
|
|
82
|
+
attribute follows the name (`data-your-name`).
|
|
83
|
+
- **A row that opens another list** runs `typeTrigger('@')`: it types the key
|
|
84
|
+
once the `/query` is gone.
|
|
85
|
+
- An open list is reported by `isSuggestionActive()`, so an editor that sends
|
|
86
|
+
on Enter does not send on the Enter that picked a row.
|
|
87
|
+
|
|
88
|
+
<Canvas of={ComposerTriggersStories.CodeAsABlock} />
|
|
89
|
+
|
|
90
|
+
## What it owns
|
|
91
|
+
|
|
92
|
+
Nothing. It only draws. Clicks and keys are yours.
|
|
93
|
+
|
|
94
|
+
## Keys
|
|
95
|
+
|
|
96
|
+
The lists are `SuggestionMenu`s, and their keys are its own. What the body
|
|
97
|
+
says, the tags it earns and sending stay the app's.
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
| Key | Does |
|
|
101
|
+
| --- | --- |
|
|
102
|
+
| `@` after a space or at the line's start | Opens the people list |
|
|
103
|
+
| `!@` | Opens the urgent list |
|
|
104
|
+
| `[` | Opens the reference list |
|
|
105
|
+
| `/` after a space or at the line's start | Opens Format and the app's sections |
|
|
106
|
+
| ↑ ↓ | Move the highlight, wrapping at the ends |
|
|
107
|
+
| Enter | Inserts the highlighted row, or runs it |
|
|
108
|
+
| Escape | Closes the list |
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { IconSquareRounded } from '@tabler/icons-react'
|
|
3
|
+
import { EditorContent, useEditor } from '@tiptap/react'
|
|
4
|
+
import StarterKit from '@tiptap/starter-kit'
|
|
5
|
+
import { richTextClassName } from './RichText'
|
|
6
|
+
import {
|
|
7
|
+
CaptionedReference,
|
|
8
|
+
PersonMention,
|
|
9
|
+
ReferenceTrigger,
|
|
10
|
+
SlashCommands,
|
|
11
|
+
UrgentPersonMention,
|
|
12
|
+
formatSection,
|
|
13
|
+
typeTrigger,
|
|
14
|
+
type CaptionedItem,
|
|
15
|
+
type MentionPerson,
|
|
16
|
+
type SlashSection,
|
|
17
|
+
} from './ComposerTriggers'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The four composer triggers: `@` and `!@` offer a person, `[` offers
|
|
21
|
+
* something to reference, and `/` formats the line or inserts something.
|
|
22
|
+
* Type one of them in the box.
|
|
23
|
+
*/
|
|
24
|
+
const meta = {
|
|
25
|
+
title: 'Primitives/ComposerTriggers',
|
|
26
|
+
parameters: { layout: 'padded' },
|
|
27
|
+
} satisfies Meta
|
|
28
|
+
|
|
29
|
+
export default meta
|
|
30
|
+
type Story = StoryObj
|
|
31
|
+
|
|
32
|
+
const PEOPLE: MentionPerson[] = ['one', 'two', 'three', 'four', 'five', 'six', 'seven'].map((n, i) => ({
|
|
33
|
+
id: `person-${n}`,
|
|
34
|
+
name: `Person ${n}`,
|
|
35
|
+
description: i % 2 === 0 ? 'Label' : undefined,
|
|
36
|
+
pubkey: null,
|
|
37
|
+
}))
|
|
38
|
+
|
|
39
|
+
const ITEMS: CaptionedItem[] = ['one', 'two', 'three'].map((n) => ({
|
|
40
|
+
id: `item-${n}`,
|
|
41
|
+
label: 'Person one',
|
|
42
|
+
snippet: `Item ${n}`,
|
|
43
|
+
uri: `nostr:item-${n}`,
|
|
44
|
+
}))
|
|
45
|
+
|
|
46
|
+
const INSERT: SlashSection = {
|
|
47
|
+
label: 'Insert',
|
|
48
|
+
commands: [
|
|
49
|
+
{ key: 'insert-@', row: { label: 'Mention', shortcut: '@' }, keywords: ['@'], run: typeTrigger('@') },
|
|
50
|
+
{ key: 'insert-[', row: { label: 'Reference', shortcut: '[' }, keywords: ['['], run: typeTrigger('[') },
|
|
51
|
+
],
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function Composer({ code }: { code: 'inline' | 'block' }) {
|
|
55
|
+
const editor = useEditor({
|
|
56
|
+
extensions: [
|
|
57
|
+
StarterKit,
|
|
58
|
+
PersonMention.configure({ people: () => PEOPLE }),
|
|
59
|
+
UrgentPersonMention.configure({ people: () => PEOPLE }),
|
|
60
|
+
CaptionedReference.configure({ icon: <IconSquareRounded size={14} stroke={1.5} /> }),
|
|
61
|
+
ReferenceTrigger.configure({
|
|
62
|
+
items: () => ITEMS,
|
|
63
|
+
ariaLabel: 'Items',
|
|
64
|
+
sectionLabel: 'Items',
|
|
65
|
+
icon: <IconSquareRounded size={16} stroke={1.5} className="text-text-secondary" />,
|
|
66
|
+
}),
|
|
67
|
+
SlashCommands.configure({ sections: [formatSection(code), INSERT] }),
|
|
68
|
+
],
|
|
69
|
+
content: '<p>Type @, !@, [ or / after a space: </p>',
|
|
70
|
+
editorProps: { attributes: { class: richTextClassName('default', 'min-h-[80px] w-[480px] rounded-md border border-border-default p-3 outline-none') } },
|
|
71
|
+
})
|
|
72
|
+
return <EditorContent editor={editor} />
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Code as a mark inside the line. */
|
|
76
|
+
export const Default: Story = { render: () => <Composer code="inline" /> }
|
|
77
|
+
|
|
78
|
+
/** Code as a block of its own, for an editor of a block document. */
|
|
79
|
+
export const CodeAsABlock: Story = { render: () => <Composer code="block" /> }
|