@estiva-app/ui 0.72.0-widgets2.0 → 0.72.0-widgets2.1
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/widgets-2/fixtures.d.ts +2 -0
- package/dist/widgets-2/fixtures.d.ts.map +1 -1
- package/dist/widgets-2/places.d.ts +99 -0
- package/dist/widgets-2/places.d.ts.map +1 -0
- package/dist/widgets-2.d.ts +1 -0
- package/dist/widgets-2.d.ts.map +1 -1
- package/dist/widgets-2.js +166 -0
- package/dist/widgets-2.js.map +4 -4
- package/package.json +1 -1
- package/registry.json +1 -1
- package/src/widgets-2/fixtures.ts +2 -0
- package/src/widgets-2/places.tsx +309 -0
- package/src/widgets-2.ts +1 -0
|
@@ -65,6 +65,8 @@ export interface Thing {
|
|
|
65
65
|
at: string;
|
|
66
66
|
};
|
|
67
67
|
updated?: string;
|
|
68
|
+
/** Where it is placed (WID-3): still loading, or not available to this reader. Ready when absent. */
|
|
69
|
+
state?: 'loading' | 'unavailable';
|
|
68
70
|
}
|
|
69
71
|
export declare const ISSUE: Thing;
|
|
70
72
|
export declare const PROJECT: Thing;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../src/widgets-2/fixtures.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE5C,uEAAuE;AACvE,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAA;AAEjE,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,YAAY,CAAA;CACrB;AAED,4EAA4E;AAC5E,MAAM,WAAW,KAAK;IACpB,0EAA0E;IAC1E,IAAI,EAAE,SAAS,GAAG,MAAM,CAAA;IACxB,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAA;IACZ,2BAA2B;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,kGAAkG;IAClG,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,kEAAkE;IAClE,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC9E,iCAAiC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,qDAAqD;IACrD,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IACtC,yEAAyE;IACzE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,MAAM,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;IACrD,OAAO,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../src/widgets-2/fixtures.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE5C,uEAAuE;AACvE,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAA;AAEjE,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,YAAY,CAAA;CACrB;AAED,4EAA4E;AAC5E,MAAM,WAAW,KAAK;IACpB,0EAA0E;IAC1E,IAAI,EAAE,SAAS,GAAG,MAAM,CAAA;IACxB,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAA;IACZ,2BAA2B;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,kGAAkG;IAClG,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,kEAAkE;IAClE,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC9E,iCAAiC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,qDAAqD;IACrD,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IACtC,yEAAyE;IACzE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,MAAM,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;IACrD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,qGAAqG;IACrG,KAAK,CAAC,EAAE,SAAS,GAAG,aAAa,CAAA;CAClC;AAWD,eAAO,MAAM,KAAK,EAAE,KAanB,CAAA;AAED,eAAO,MAAM,OAAO,EAAE,KAiBrB,CAAA;AAED,eAAO,MAAM,KAAK,EAAE,KAUnB,CAAA;AAED,eAAO,MAAM,OAAO,EAAE,KAQrB,CAAA;AAED,eAAO,MAAM,IAAI,EAAE,KAYlB,CAAA;AAED,+DAA+D;AAC/D,eAAO,MAAM,IAAI;;;;;CAKhB,CAAA;AAED,eAAO,MAAM,UAAU;;;;CAAuE,CAAA;AAC9F,eAAO,MAAM,eAAe;;;;CAAgF,CAAA;AAE5G,wFAAwF;AACxF,eAAO,MAAM,UAAU;;;;;CAAiH,CAAA;AAExI,qEAAqE;AACrE,eAAO,MAAM,OAAO,QAIjB,CAAA"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { Thing } from './fixtures';
|
|
3
|
+
/** A piece of a sentence: words, or a link to a file. */
|
|
4
|
+
export type Piece = string | Thing;
|
|
5
|
+
/** One line of text: a link alone on its line, or a sentence of words and links. */
|
|
6
|
+
export type Line = Thing | Piece[];
|
|
7
|
+
/** Every file the lines link to, each once. */
|
|
8
|
+
export declare function linksIn(lines: Line[]): Thing[];
|
|
9
|
+
/** The file whose card goes under a message: the one it links. None when it links two or more. */
|
|
10
|
+
export declare function cardFileOf(lines: Line[]): Thing | undefined;
|
|
11
|
+
/** A message that is nothing but one link: it shows the card, not the row. */
|
|
12
|
+
export declare function isOnlyOneLink(lines: Line[]): boolean;
|
|
13
|
+
/** A file's card: a linked message draws as a message, every other file as its detailed card; loading and not available as the proposed kit draws them. */
|
|
14
|
+
export declare function CardOf({ thing }: {
|
|
15
|
+
thing: Thing;
|
|
16
|
+
}): import("react").JSX.Element;
|
|
17
|
+
export interface PlacedLinesProps {
|
|
18
|
+
lines: Line[];
|
|
19
|
+
/** In a document: the lone-line links switched to their card, by line index. */
|
|
20
|
+
cards?: number[];
|
|
21
|
+
/** In a document: a click on a lone-line link, which opens its chip menu. */
|
|
22
|
+
onLink?: (index: number) => void;
|
|
23
|
+
/** Drawn under a line, by line index: the chip menu, open. */
|
|
24
|
+
under?: (index: number) => ReactNode;
|
|
25
|
+
className?: string;
|
|
26
|
+
}
|
|
27
|
+
/** Text whose links draw by where they sit: a chip in a sentence, a row alone on its line, or (in a document) the card chosen for that line. */
|
|
28
|
+
export declare function PlacedLines({ lines, cards, onLink, under, className }: PlacedLinesProps): import("react").JSX.Element;
|
|
29
|
+
export interface CardUnderProps {
|
|
30
|
+
thing: Thing;
|
|
31
|
+
/** The ✕ that removes it: under the message box before sending, or the author's own on a sent message. */
|
|
32
|
+
onRemove?: () => void;
|
|
33
|
+
/** On a sent message the ✕ shows on hover; under the box it always shows. */
|
|
34
|
+
removeOnHover?: boolean;
|
|
35
|
+
className?: string;
|
|
36
|
+
}
|
|
37
|
+
/** The one file's card under a message, and the ✕ that removes it. */
|
|
38
|
+
export declare function CardUnder({ thing, onRemove, removeOnHover, className }: CardUnderProps): import("react").JSX.Element;
|
|
39
|
+
export interface PlacedMessageProps {
|
|
40
|
+
lines: Line[];
|
|
41
|
+
/** The card was removed with ✕: before sending, or by the author afterwards. */
|
|
42
|
+
removed?: boolean;
|
|
43
|
+
/** Draws the ✕; without it the card shows with none, as to anyone but the author. */
|
|
44
|
+
onRemove?: () => void;
|
|
45
|
+
removeOnHover?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* An old message, sent before Widgets 2 with a `compact` tag for each of
|
|
48
|
+
* these: no card was added to it, and each still draws as a row under it.
|
|
49
|
+
*/
|
|
50
|
+
oldCompact?: Thing[];
|
|
51
|
+
className?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* A message's text, and under it the one file's card. A file with its card
|
|
55
|
+
* under the message draws no row in it (a chip in a sentence still draws), so
|
|
56
|
+
* a message that is only one link shows its card alone.
|
|
57
|
+
*/
|
|
58
|
+
export declare function PlacedMessage({ lines, removed, onRemove, removeOnHover, oldCompact, className }: PlacedMessageProps): import("react").JSX.Element;
|
|
59
|
+
/** A sent message whose author can remove its card with ✕, live: try it. */
|
|
60
|
+
export declare function SentMessage({ lines, author, className }: {
|
|
61
|
+
lines: Line[];
|
|
62
|
+
author?: boolean;
|
|
63
|
+
className?: string;
|
|
64
|
+
}): import("react").JSX.Element;
|
|
65
|
+
export interface ChipMenuProps {
|
|
66
|
+
thing: Thing;
|
|
67
|
+
/**
|
|
68
|
+
* A lone-line link offers its card; a chip in a sentence offers Open only,
|
|
69
|
+
* and so does a link to what has no card worth choosing (a Folder, a file
|
|
70
|
+
* that is not a picture).
|
|
71
|
+
*/
|
|
72
|
+
alone?: boolean;
|
|
73
|
+
shownAs?: 'row' | 'card';
|
|
74
|
+
onShowAs?: (as: 'row' | 'card') => void;
|
|
75
|
+
onOpen?: () => void;
|
|
76
|
+
}
|
|
77
|
+
/** What a link in a document offers its writer: Open, and on a lone-line link of a kind that has one, its card in that line's place. */
|
|
78
|
+
export declare function ChipMenu({ thing, alone, shownAs, onShowAs, onOpen }: ChipMenuProps): import("react").JSX.Element;
|
|
79
|
+
/** A document's text with its chip menu, live: click a lone-line link, then choose Row or Card. */
|
|
80
|
+
export declare function PlacedDocument({ lines, initialCards, className }: {
|
|
81
|
+
lines: Line[];
|
|
82
|
+
initialCards?: number[];
|
|
83
|
+
className?: string;
|
|
84
|
+
}): import("react").JSX.Element;
|
|
85
|
+
/**
|
|
86
|
+
* A description's and a page's `/` menu with the widget entry (Miky's QA note,
|
|
87
|
+
* 15 Oct: "`/` section where you can use `[` to insert a widget"). Format rows
|
|
88
|
+
* shortened to three; Insert as the app has it, plus the link.
|
|
89
|
+
*/
|
|
90
|
+
export declare function SlashMenuWithLink({ file, table }: {
|
|
91
|
+
file?: boolean;
|
|
92
|
+
table?: boolean;
|
|
93
|
+
}): import("react").JSX.Element;
|
|
94
|
+
/** The `[` list the widget entry opens: the files to link, each led by its kind's icon. */
|
|
95
|
+
export declare function LinkPicker({ files, query }: {
|
|
96
|
+
files: Thing[];
|
|
97
|
+
query?: string;
|
|
98
|
+
}): import("react").JSX.Element;
|
|
99
|
+
//# sourceMappingURL=places.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"places.d.ts","sourceRoot":"","sources":["../../src/widgets-2/places.tsx"],"names":[],"mappings":"AAwBA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAIhD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAGvC,yDAAyD;AACzD,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG,KAAK,CAAA;AAClC,oFAAoF;AACpF,MAAM,MAAM,IAAI,GAAG,KAAK,GAAG,KAAK,EAAE,CAAA;AAIlC,+CAA+C;AAC/C,wBAAgB,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,KAAK,EAAE,CAE9C;AAED,kGAAkG;AAClG,wBAAgB,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,KAAK,GAAG,SAAS,CAG3D;AAED,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAEpD;AAED,2JAA2J;AAC3J,wBAAgB,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,KAAK,CAAA;CAAE,+BAIjD;AAkBD,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,IAAI,EAAE,CAAA;IACb,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,8DAA8D;IAC9D,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,SAAS,CAAA;IACpC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,gJAAgJ;AAChJ,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,KAAU,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,gBAAgB,+BAqC5F;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,KAAK,CAAA;IACZ,0GAA0G;IAC1G,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,sEAAsE;AACtE,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,aAAqB,EAAE,SAAS,EAAE,EAAE,cAAc,+BAgB9F;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,IAAI,EAAE,CAAA;IACb,gFAAgF;IAChF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,qFAAqF;IACrF,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;OAGG;IACH,UAAU,CAAC,EAAE,KAAK,EAAE,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,OAAe,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,kBAAkB,+BAY3H;AAED,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,MAAa,EAAE,SAAS,EAAE,EAAE;IAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BAGvH;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,KAAK,CAAA;IACZ;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,OAAO,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;IACxB,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,KAAK,GAAG,MAAM,KAAK,IAAI,CAAA;IACvC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;CACpB;AAED,wIAAwI;AACxI,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAY,EAAE,OAAe,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,aAAa,+BA2BjG;AAED,mGAAmG;AACnG,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,YAAiB,EAAE,SAAS,EAAE,EAAE;IAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BA4BrI;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,IAAW,EAAE,KAAY,EAAE,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,+BAkBnG;AAED,2FAA2F;AAC3F,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,KAAU,EAAE,EAAE;IAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,+BAYnF"}
|
package/dist/widgets-2.d.ts
CHANGED
package/dist/widgets-2.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"widgets-2.d.ts","sourceRoot":"","sources":["../src/widgets-2.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,sBAAsB,CAAA;AACpC,cAAc,sBAAsB,CAAA"}
|
|
1
|
+
{"version":3,"file":"widgets-2.d.ts","sourceRoot":"","sources":["../src/widgets-2.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,sBAAsB,CAAA;AACpC,cAAc,sBAAsB,CAAA;AACpC,cAAc,oBAAoB,CAAA"}
|
package/dist/widgets-2.js
CHANGED
|
@@ -388,18 +388,176 @@ var UNREADABLE = { kind: "issue", noun: "Issue", app: "Ship", address: "naddr1qq
|
|
|
388
388
|
var PICTURE = "data:image/svg+xml;utf8," + encodeURIComponent(
|
|
389
389
|
'<svg xmlns="http://www.w3.org/2000/svg" width="480" height="300"><rect width="480" height="300" fill="#8a94a6"/><circle cx="360" cy="90" r="40" fill="#d8dde6"/><path d="M0 300 L170 120 L300 260 L380 190 L480 300 Z" fill="#5d6778"/></svg>'
|
|
390
390
|
);
|
|
391
|
+
|
|
392
|
+
// src/widgets-2/places.tsx
|
|
393
|
+
import {
|
|
394
|
+
IconCheck,
|
|
395
|
+
IconExternalLink,
|
|
396
|
+
IconH1,
|
|
397
|
+
IconLetterT,
|
|
398
|
+
IconLink,
|
|
399
|
+
IconListCheck,
|
|
400
|
+
IconPaperclip as IconPaperclip2,
|
|
401
|
+
IconTable,
|
|
402
|
+
IconX
|
|
403
|
+
} from "@tabler/icons-react";
|
|
404
|
+
import { useState } from "react";
|
|
405
|
+
import { cn as cn2 } from "@estiva-app/ui";
|
|
406
|
+
import { IconButton } from "@estiva-app/ui";
|
|
407
|
+
import { MenuItem, MenuPanel as MenuPanel2, MenuSection, MenuSeparator } from "@estiva-app/ui";
|
|
408
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
409
|
+
var isLink = (piece) => typeof piece === "object" && !Array.isArray(piece);
|
|
410
|
+
function linksIn(lines) {
|
|
411
|
+
return [...new Set(lines.flatMap((line) => isLink(line) ? [line] : line.filter(isLink)))];
|
|
412
|
+
}
|
|
413
|
+
function cardFileOf(lines) {
|
|
414
|
+
const files = linksIn(lines);
|
|
415
|
+
return files.length === 1 ? files[0] : void 0;
|
|
416
|
+
}
|
|
417
|
+
function isOnlyOneLink(lines) {
|
|
418
|
+
return lines.length === 1 && isLink(lines[0]);
|
|
419
|
+
}
|
|
420
|
+
function CardOf({ thing }) {
|
|
421
|
+
if (thing.state === "loading") return /* @__PURE__ */ jsx2(CardLoading, { kind: thing.kind, layout: "detailed", shape: loadingShapeOf(thing) });
|
|
422
|
+
if (thing.state === "unavailable") return /* @__PURE__ */ jsx2(CardUnavailable, { kind: thing.kind, noun: thing.noun, app: thing.app });
|
|
423
|
+
return thing.kind === "message" ? /* @__PURE__ */ jsx2(MessageCard, { thing }) : /* @__PURE__ */ jsx2(DetailedCard, { thing });
|
|
424
|
+
}
|
|
425
|
+
function Row({ thing, preview = false }) {
|
|
426
|
+
if (thing.state === "loading") return /* @__PURE__ */ jsx2(CardLoading, { kind: thing.kind, layout: "compact" });
|
|
427
|
+
if (thing.state === "unavailable") return /* @__PURE__ */ jsx2(CardUnavailable, { kind: thing.kind, noun: thing.noun, app: thing.app });
|
|
428
|
+
return /* @__PURE__ */ jsx2(CompactCard, { thing, preview });
|
|
429
|
+
}
|
|
430
|
+
function Sentence({ pieces }) {
|
|
431
|
+
return /* @__PURE__ */ jsx2("p", { className: "text-body-2 text-text-primary", children: pieces.map((piece, i) => typeof piece === "string" ? /* @__PURE__ */ jsx2("span", { children: piece }, i) : /* @__PURE__ */ jsx2(ReferenceChip, { thing: piece, state: piece.state, preview: !piece.state }, i)) });
|
|
432
|
+
}
|
|
433
|
+
function PlacedLines({ lines, cards = [], onLink, under, className }) {
|
|
434
|
+
return /* @__PURE__ */ jsx2("div", { className: cn2("flex flex-col gap-2", className), children: lines.map((line, i) => /* @__PURE__ */ jsxs2("div", { className: "relative flex flex-col", children: [
|
|
435
|
+
isLink(line) ? onLink ? (
|
|
436
|
+
// While writing, a click on the link opens its menu instead of following it (the record clicking-a-chip-while-editing-opens-a-menu).
|
|
437
|
+
/* @__PURE__ */ jsx2(
|
|
438
|
+
"div",
|
|
439
|
+
{
|
|
440
|
+
role: "button",
|
|
441
|
+
tabIndex: 0,
|
|
442
|
+
"aria-haspopup": "menu",
|
|
443
|
+
className: "w-full min-w-0 cursor-pointer",
|
|
444
|
+
onClickCapture: (event) => {
|
|
445
|
+
event.preventDefault();
|
|
446
|
+
event.stopPropagation();
|
|
447
|
+
onLink(i);
|
|
448
|
+
},
|
|
449
|
+
onKeyDown: (event) => {
|
|
450
|
+
if (event.key === "Enter") onLink(i);
|
|
451
|
+
},
|
|
452
|
+
children: cards.includes(i) ? /* @__PURE__ */ jsx2(CardOf, { thing: line }) : /* @__PURE__ */ jsx2(Row, { thing: line })
|
|
453
|
+
}
|
|
454
|
+
)
|
|
455
|
+
) : cards.includes(i) ? /* @__PURE__ */ jsx2(CardOf, { thing: line }) : /* @__PURE__ */ jsx2(Row, { thing: line, preview: true }) : /* @__PURE__ */ jsx2(Sentence, { pieces: line }),
|
|
456
|
+
under?.(i)
|
|
457
|
+
] }, i)) });
|
|
458
|
+
}
|
|
459
|
+
function CardUnder({ thing, onRemove, removeOnHover = false, className }) {
|
|
460
|
+
return /* @__PURE__ */ jsxs2("div", { className: cn2("group/card flex items-start gap-1", className), children: [
|
|
461
|
+
/* @__PURE__ */ jsx2("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx2(CardOf, { thing }) }),
|
|
462
|
+
onRemove && // At the card's top corner, not its middle: a tall card's ✕ halfway down reads as belonging to nothing.
|
|
463
|
+
/* @__PURE__ */ jsx2("div", { className: "flex shrink-0 self-start pt-2", children: /* @__PURE__ */ jsx2(IconButton, { "aria-label": "Remove card", tooltip: "Remove card", onClick: onRemove, className: cn2(removeOnHover && "opacity-0 group-hover/card:opacity-100 focus-visible:opacity-100"), children: /* @__PURE__ */ jsx2(IconX, { size: 16 }) }) })
|
|
464
|
+
] });
|
|
465
|
+
}
|
|
466
|
+
function PlacedMessage({ lines, removed = false, onRemove, removeOnHover, oldCompact, className }) {
|
|
467
|
+
const file = removed || oldCompact ? void 0 : cardFileOf(lines);
|
|
468
|
+
const shown = file ? lines.filter((line) => line !== file) : lines;
|
|
469
|
+
return /* @__PURE__ */ jsxs2("div", { className: cn2("flex flex-col gap-2", className), children: [
|
|
470
|
+
shown.length > 0 && /* @__PURE__ */ jsx2(PlacedLines, { lines: shown }),
|
|
471
|
+
file && /* @__PURE__ */ jsx2(CardUnder, { thing: file, onRemove, removeOnHover, className: "max-w-[480px]" }),
|
|
472
|
+
oldCompact?.map((thing, i) => /* @__PURE__ */ jsx2(CompactCard, { thing, preview: true }, i))
|
|
473
|
+
] });
|
|
474
|
+
}
|
|
475
|
+
function SentMessage({ lines, author = true, className }) {
|
|
476
|
+
const [removed, setRemoved] = useState(false);
|
|
477
|
+
return /* @__PURE__ */ jsx2(PlacedMessage, { lines, removed, onRemove: author ? () => setRemoved(true) : void 0, removeOnHover: true, className });
|
|
478
|
+
}
|
|
479
|
+
function ChipMenu({ thing, alone = true, shownAs = "row", onShowAs, onOpen }) {
|
|
480
|
+
return /* @__PURE__ */ jsx2(MenuPanel2, { className: "w-[200px]", children: /* @__PURE__ */ jsxs2("div", { role: "menu", "aria-label": "Link", className: "flex flex-col gap-0.5", children: [
|
|
481
|
+
/* @__PURE__ */ jsx2(MenuItem, { role: "menuitem", label: "Open", leading: /* @__PURE__ */ jsx2(IconExternalLink, { size: 16 }), onClick: onOpen }),
|
|
482
|
+
alone && /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
483
|
+
/* @__PURE__ */ jsx2(MenuSeparator, {}),
|
|
484
|
+
/* @__PURE__ */ jsx2("div", { role: "group", "aria-label": "Show as", children: /* @__PURE__ */ jsx2(MenuSection, { label: "Show as", children: ["row", "card"].map((as) => /* @__PURE__ */ jsx2(
|
|
485
|
+
MenuItem,
|
|
486
|
+
{
|
|
487
|
+
role: "menuitemradio",
|
|
488
|
+
"aria-checked": shownAs === as,
|
|
489
|
+
label: as === "row" ? "Row" : "Card",
|
|
490
|
+
trailing: shownAs === as ? /* @__PURE__ */ jsx2(IconCheck, { size: 16, "aria-hidden": true }) : void 0,
|
|
491
|
+
onClick: () => onShowAs?.(as)
|
|
492
|
+
},
|
|
493
|
+
as
|
|
494
|
+
)) }) })
|
|
495
|
+
] })
|
|
496
|
+
] }) });
|
|
497
|
+
}
|
|
498
|
+
function PlacedDocument({ lines, initialCards = [], className }) {
|
|
499
|
+
const [cards, setCards] = useState(initialCards);
|
|
500
|
+
const [open, setOpen] = useState(null);
|
|
501
|
+
return /* @__PURE__ */ jsx2(
|
|
502
|
+
PlacedLines,
|
|
503
|
+
{
|
|
504
|
+
lines,
|
|
505
|
+
cards,
|
|
506
|
+
className,
|
|
507
|
+
onLink: (i) => setOpen(open === i ? null : i),
|
|
508
|
+
under: (i) => {
|
|
509
|
+
const line = lines[i];
|
|
510
|
+
if (open !== i || !isLink(line)) return null;
|
|
511
|
+
return /* @__PURE__ */ jsx2("div", { className: "absolute left-0 top-full z-10 mt-1", children: /* @__PURE__ */ jsx2(
|
|
512
|
+
ChipMenu,
|
|
513
|
+
{
|
|
514
|
+
thing: line,
|
|
515
|
+
shownAs: cards.includes(i) ? "card" : "row",
|
|
516
|
+
onOpen: () => setOpen(null),
|
|
517
|
+
onShowAs: (as) => {
|
|
518
|
+
setCards(as === "card" ? [...cards, i] : cards.filter((c) => c !== i));
|
|
519
|
+
setOpen(null);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
) });
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
function SlashMenuWithLink({ file = true, table = true }) {
|
|
528
|
+
return /* @__PURE__ */ jsx2(MenuPanel2, { className: "w-[260px]", children: /* @__PURE__ */ jsxs2("div", { role: "menu", "aria-label": "Insert", className: "flex flex-col gap-0.5", children: [
|
|
529
|
+
/* @__PURE__ */ jsxs2(MenuSection, { label: "Format", children: [
|
|
530
|
+
/* @__PURE__ */ jsx2(MenuItem, { label: "Text", leading: /* @__PURE__ */ jsx2(IconLetterT, { size: 16 }) }),
|
|
531
|
+
/* @__PURE__ */ jsx2(MenuItem, { label: "Heading", leading: /* @__PURE__ */ jsx2(IconH1, { size: 16 }), shortcut: "#" }),
|
|
532
|
+
/* @__PURE__ */ jsx2(MenuItem, { label: "Checklist", leading: /* @__PURE__ */ jsx2(IconListCheck, { size: 16 }), shortcut: "[]" })
|
|
533
|
+
] }),
|
|
534
|
+
/* @__PURE__ */ jsx2(MenuSeparator, {}),
|
|
535
|
+
/* @__PURE__ */ jsxs2(MenuSection, { label: "Insert", children: [
|
|
536
|
+
table && /* @__PURE__ */ jsx2(MenuItem, { label: "Table", leading: /* @__PURE__ */ jsx2(IconTable, { size: 16 }) }),
|
|
537
|
+
file && /* @__PURE__ */ jsx2(MenuItem, { label: "File", leading: /* @__PURE__ */ jsx2(IconPaperclip2, { size: 16 }) }),
|
|
538
|
+
/* @__PURE__ */ jsx2(MenuItem, { label: "Link to a file", leading: /* @__PURE__ */ jsx2(IconLink, { size: 16 }), shortcut: "[", selected: true })
|
|
539
|
+
] })
|
|
540
|
+
] }) });
|
|
541
|
+
}
|
|
542
|
+
function LinkPicker({ files, query = "" }) {
|
|
543
|
+
return /* @__PURE__ */ jsx2(MenuPanel2, { className: "w-[320px]", children: /* @__PURE__ */ jsx2("div", { role: "listbox", "aria-label": "Link to a file", className: "flex flex-col gap-0.5", children: /* @__PURE__ */ jsx2(MenuSection, { label: query ? `Files matching \u201C${query}\u201D` : "Recent files", children: files.map((thing, i) => /* @__PURE__ */ jsx2(MenuItem, { role: "option", label: thing.kind === "message" ? `${thing.author}: ${thing.body}` : thing.title, leading: /* @__PURE__ */ jsx2(ThingIcon, { kind: thing.kind }), hint: thing.key && /* @__PURE__ */ jsx2("span", { className: "text-caption text-text-secondary", children: thing.key }), selected: i === 0 }, i)) }) }) });
|
|
544
|
+
}
|
|
391
545
|
export {
|
|
392
546
|
ATTACHMENT,
|
|
393
547
|
ATTACHMENT_FILE,
|
|
394
548
|
AttachmentChip,
|
|
395
549
|
CardLoading,
|
|
550
|
+
CardOf,
|
|
396
551
|
CardUnavailable,
|
|
552
|
+
CardUnder,
|
|
553
|
+
ChipMenu,
|
|
397
554
|
CommentAnchor,
|
|
398
555
|
CompactCard,
|
|
399
556
|
DetailedCard,
|
|
400
557
|
HoverPreview,
|
|
401
558
|
HoverPreviewOpen,
|
|
402
559
|
ISSUE,
|
|
560
|
+
LinkPicker,
|
|
403
561
|
MESSAGE,
|
|
404
562
|
MessageCard,
|
|
405
563
|
PAGE,
|
|
@@ -407,12 +565,20 @@ export {
|
|
|
407
565
|
PICTURE,
|
|
408
566
|
PROJECT,
|
|
409
567
|
PartCard,
|
|
568
|
+
PlacedDocument,
|
|
569
|
+
PlacedLines,
|
|
570
|
+
PlacedMessage,
|
|
410
571
|
ProgressRing,
|
|
411
572
|
ReferenceChip,
|
|
573
|
+
SentMessage,
|
|
574
|
+
SlashMenuWithLink,
|
|
412
575
|
StatusMark,
|
|
413
576
|
TOPIC,
|
|
414
577
|
ThingIcon,
|
|
415
578
|
UNREADABLE,
|
|
579
|
+
cardFileOf,
|
|
580
|
+
isOnlyOneLink,
|
|
581
|
+
linksIn,
|
|
416
582
|
loadingShapeOf
|
|
417
583
|
};
|
|
418
584
|
//# sourceMappingURL=widgets-2.js.map
|
package/dist/widgets-2.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/widgets-2/proposed.tsx", "../src/widgets-2/fixtures.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Widgets 2 prototype: the proposed shared kit, one drawing of each widget for\n * Peek, Ship and Leaf alike. Written as the package would export it: no app\n * name and no product noun in a name or a prop. The kind's icon, the key and\n * every field arrive as data from the app's manifest.\n *\n * In `src/` on `concept/widgets-2` only, so the apps' own Storybooks can place\n * it (WID-3): `@estiva-app/ui/widgets-2`, an unlisted entry published only as a\n * `concept` prerelease. The build project shapes the real exports from it once\n * Miky and Katerina approve it.\n *\n * Second iteration, from Miky's notes on WID-2 (15 Oct):\n * - no noun in a card's corner and no \"Open in \u2026\": the icon says what it is,\n * and the title is the link;\n * - an issue shows its key; the only status and assignee are the actionable ones;\n * - detailed shows the file's short summary, not the opening of its description;\n * - hovering a chip or a compact card shows the detailed card;\n * - a project shows its progress as a ring, its status and lead as actions,\n * its comments, and each issue's assignee;\n * - a topic shows its conversations, its last update and its members, and\n * detailed adds the latest conversation;\n * - a message is drawn with its author's face; a page with its authors and\n * its last edit.\n */\nimport { IconCircle, IconCircleCheck, IconCircleDashed, IconCircleHalf2, IconCircleX, IconFileText, IconMessage2, IconPaperclip } from '@tabler/icons-react'\nimport type { ReactNode } from 'react'\nimport { Avatar } from '../Avatar'\nimport { AvatarGroup } from '../AvatarGroup'\nimport { Card } from '../Card'\nimport { cn } from '../cn'\nimport { InlineChip } from '../InlineChip'\nimport { KindIcon } from '../KindIcon'\nimport { Link } from '../Link'\nimport { MenuPanel } from '../Menu'\nimport { PreviewCard } from '../PreviewCard'\nimport { Select } from '../Select'\nimport { SkeletonBar } from '../Skeleton'\nimport { UnreadDot } from '../UnreadDot'\nimport type { Status, StatusColour, Thing } from './fixtures'\n\nconst HREF = '#'\nconst noop = () => {}\n\n/* ------------------------------------------------------------ the parts */\n\nconst STATUS_ICON: Record<StatusColour, { Icon: typeof IconCircle; tone: string }> = {\n neutral: { Icon: IconCircleDashed, tone: 'text-text-secondary' },\n blue: { Icon: IconCircleHalf2, tone: 'text-info-default' },\n green: { Icon: IconCircleCheck, tone: 'text-success-default' },\n muted: { Icon: IconCircleX, tone: 'text-text-muted' },\n}\n\n/** A status as its shape: Ship's icons, which read without colour. */\nexport function StatusMark({ status }: { status: Status }) {\n return (\n <span className=\"inline-flex shrink-0 items-center\" title={status.label}>\n <StatusIcon status={status} />\n <span className=\"sr-only\">{status.label}</span>\n </span>\n )\n}\n\n/** The status's shape alone, beside words that already name it. */\nfunction StatusIcon({ status }: { status: Status }) {\n const { Icon, tone } = STATUS_ICON[status.colour]\n return <Icon size={14} stroke={1.5} className={cn('shrink-0', tone)} aria-hidden />\n}\n\n/** The icon for a kind. A page gets its own (proposed): today every app draws it as \"another app's file\". */\nexport function ThingIcon({ kind, size = 16, tone = 'secondary' }: { kind: Thing['kind']; size?: 14 | 16; tone?: 'secondary' | 'inherit' }) {\n if (kind === 'page') return <IconFileText size={size} stroke={1.5} className={cn('shrink-0', tone === 'secondary' && 'text-text-secondary')} aria-hidden />\n return <KindIcon kind={kind} size={size} tone={tone} />\n}\n\n/** A key, the way people say it (\"ABC-2\"): before the title, quieter than it. */\nfunction Key({ children }: { children: ReactNode }) {\n return <span className=\"shrink-0 text-caption tabular-nums text-text-muted\">{children}</span>\n}\n\n/**\n * Progress as a small ring with the count beside it as a label (Miky, 15 Oct,\n * second round: the ring with the count inside was too big).\n */\nexport function ProgressRing({ done, total }: { done: number; total: number }) {\n const size = 16\n const stroke = 2\n const r = (size - stroke) / 2\n const c = 2 * Math.PI * r\n return (\n <span className=\"inline-flex shrink-0 items-center gap-1 text-caption tabular-nums text-text-secondary\" title={`${done} of ${total} done`}>\n <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} className=\"shrink-0 -rotate-90\" aria-hidden>\n <circle cx={size / 2} cy={size / 2} r={r} fill=\"none\" strokeWidth={stroke} className=\"stroke-border-default\" />\n <circle cx={size / 2} cy={size / 2} r={r} fill=\"none\" strokeWidth={stroke} strokeLinecap=\"round\" strokeDasharray={`${(c * done) / total} ${c}`} className=\"stroke-success-default\" />\n </svg>\n {done}/{total}\n </span>\n )\n}\n\n/*\n The unread dot after a count is the package's UnreadDot: the reader has\n something unread in this conversation. Only a member has unread (SPEC \u00A711.8),\n so a reader who is not one sees the count alone.\n*/\n\n/** Where the count leads: the file's conversation, at the first unread when there is one. */\nconst CONVERSATION_HREF = '#conversation'\n\n/**\n * The comment count, the words brighter and a dot after it when the reader\n * has unread there. It is a link to the conversation (Miky, fourth round:\n * \"especially needed when you have unread messages\"), and it sits above a\n * compact card's covering link, so the card opens the file and the count\n * opens its conversation.\n */\nfunction Comments({ count, unread = false, noun = 'comments' }: { count: number; unread?: boolean; noun?: string }) {\n return (\n <Link\n href={CONVERSATION_HREF}\n variant=\"plain\"\n className={cn(\n 'relative z-10 -mx-1 inline-flex shrink-0 items-center gap-1 rounded-sm px-1 text-caption tabular-nums hover:bg-bg-active',\n unread ? 'font-medium text-text-primary' : 'text-text-secondary',\n )}\n >\n <IconMessage2 size={14} stroke={1.5} aria-hidden />\n {count}\n <span className=\"sr-only\">{unread ? `${noun}, unread \u2014 open the conversation` : `${noun} \u2014 open the conversation`}</span>\n {unread && <UnreadDot className=\"h-auto w-auto\" />}\n </Link>\n )\n}\n\n/**\n * A topic's conversations: the same icon and count as every other file's\n * comments (Miky, third round), after when the last one moved.\n */\nfunction Conversations({ thing }: { thing: Thing }) {\n return (\n <span className=\"inline-flex shrink-0 items-center gap-2\">\n <span className=\"text-caption text-text-secondary\">{thing.updated}</span>\n <Comments count={thing.conversations ?? 0} unread={thing.unread} noun=\"conversations\" />\n </span>\n )\n}\n\nconst people = (names: string[] = []) => names.map((name) => ({ name }))\n\n/**\n * The kind's own pickers, the only place its status and owner show in detailed.\n * Read only, for a reader who may not change the file and in the hover preview\n * (Miky, 15 Oct: no actions on hover), they are the same two values as words,\n * in the same place.\n */\nfunction Pickers({ thing, readOnly = false }: { thing: Thing; readOnly?: boolean }) {\n if (!thing.status) return null\n if (readOnly) {\n return (\n <span className=\"flex h-6 min-w-0 items-center gap-3 text-caption text-text-secondary\">\n <span className=\"inline-flex shrink-0 items-center gap-1\">\n <StatusIcon status={thing.status} />\n {thing.status.label}\n </span>\n <span className=\"inline-flex min-w-0 items-center gap-1\">\n <Avatar name={thing.assignee} size={16} />\n <span className=\"truncate\">{thing.assignee ?? 'Unassigned'}</span>\n </span>\n </span>\n )\n }\n return (\n <>\n <Select size=\"small\" className=\"w-auto\" value=\"s\" onChange={noop} ariaLabel=\"Status\" options={[{ value: 's', label: thing.status.label, leading: <StatusIcon status={thing.status} /> }]} />\n <Select\n size=\"small\"\n className=\"w-auto\"\n value=\"a\"\n onChange={noop}\n ariaLabel={thing.kind === 'project' ? 'Lead' : 'Assignee'}\n options={[{ value: 'a', label: thing.assignee ?? 'Unassigned', leading: <Avatar name={thing.assignee} size={16} /> }]}\n />\n </>\n )\n}\n\n/* ----------------------------------------------------------------- chip */\n\nexport interface ReferenceChipProps {\n thing: Thing\n state?: 'ready' | 'loading' | 'unavailable'\n /** Hovering the chip shows the detailed card. */\n preview?: boolean\n}\n\n/** What leads a chip and a compact card: the author's face for a message, the kind's icon otherwise. */\nfunction Lead({ thing, chip = false }: { thing: Thing; chip?: boolean }) {\n if (thing.kind === 'message') return <Avatar name={thing.author} size={16} />\n return <ThingIcon kind={thing.kind} size={chip ? 14 : 16} tone={chip ? 'inherit' : 'secondary'} />\n}\n\n/**\n * A link in running text: the icon (or the author's face), the key, the\n * title cut at 24ch, the status as its shape. Loading keeps the icon (the\n * kind is known from the address); not available says so in words.\n */\nexport function ReferenceChip({ thing, state = 'ready', preview = false }: ReferenceChipProps) {\n if (state === 'loading') {\n return (\n <InlineChip icon={<ThingIcon kind={thing.kind} size={14} tone=\"inherit\" />}>\n <SkeletonBar className=\"h-2.5 w-16\" />\n </InlineChip>\n )\n }\n if (state === 'unavailable') {\n return (\n <InlineChip icon={<ThingIcon kind={thing.kind} size={14} tone=\"inherit\" />} className=\"text-text-muted\">\n {thing.noun}, not available\n </InlineChip>\n )\n }\n const chip = (\n <InlineChip icon={<Lead thing={thing} chip />} href={HREF} className=\"max-w-[28ch]\">\n {thing.key && <Key>{thing.key}</Key>}\n <span className=\"truncate\">{thing.kind === 'message' ? `${thing.author}: ${thing.body}` : thing.title}</span>\n {thing.status && <StatusMark status={thing.status} />}\n </InlineChip>\n )\n return preview ? <HoverPreview thing={thing}>{chip}</HoverPreview> : chip\n}\n\n/* ---------------------------------------------------------------- cards */\n\nexport type CardLayout = 'compact' | 'detailed'\n\n/** Compact: one 36px line. Every kind, every app. Hovering it shows the detailed card. */\nexport function CompactCard({ thing, preview = false }: { thing: Thing; preview?: boolean }) {\n const card = (\n <Card fill=\"inset\" hover=\"hairline\" className=\"relative flex h-9 items-center gap-2 px-3\">\n <Lead thing={thing} />\n {thing.kind === 'message' ? (\n <span className=\"flex min-w-0 flex-1 items-baseline gap-2 text-body-2\">\n <span className=\"shrink-0 text-body-2-strong text-text-primary\">{thing.author}</span>\n <Link href={HREF} variant=\"quiet\" cover truncate className=\"text-text-secondary\">\n {thing.body}\n </Link>\n </span>\n ) : (\n <>\n {thing.key && <Key>{thing.key}</Key>}\n <span className=\"contents text-body-2-strong text-text-primary\">\n <Link href={HREF} variant=\"quiet\" cover truncate className=\"min-w-0 flex-1\">\n {thing.title}\n </Link>\n </span>\n </>\n )}\n {thing.kind === 'topic' && <Conversations thing={thing} />}\n {thing.kind === 'page' && <span className=\"shrink-0 text-caption text-text-secondary\">{thing.updated}</span>}\n {thing.kind === 'message' && <span className=\"shrink-0 text-caption text-text-muted\">{thing.updated}</span>}\n {/* Compact keeps to one line: the comment count shows only when there is something unread in it. */}\n {thing.unread && thing.comments !== undefined && <Comments count={thing.comments} unread />}\n {thing.status && thing.kind === 'issue' && <StatusMark status={thing.status} />}\n {thing.done && <ProgressRing done={thing.done.done} total={thing.done.total} />}\n {thing.assignee && <Avatar name={thing.assignee} size={16} />}\n {thing.members && <AvatarGroup members={people(thing.members)} size={16} />}\n {thing.authors && <AvatarGroup members={people(thing.authors)} size={16} />}\n </Card>\n )\n return preview ? (\n <HoverPreview thing={thing} block>\n {card}\n </HoverPreview>\n ) : (\n card\n )\n}\n\n/**\n * Detailed: what compact cannot hold. The header is compact's line (icon, key,\n * title as the link; a project's ring or a topic's members in the corner),\n * then the summary, the kind's own extras (a project's issues, a topic's\n * latest conversation), and a footer: the pickers and the comments for an\n * issue or a project, who and when for a page and a topic.\n *\n * `bare` drops the card's frame, for the hover preview, which draws the\n * package's one elevated box around it.\n */\nexport function DetailedCard({ thing, bare = false, readOnly = bare }: { thing: Thing; bare?: boolean; readOnly?: boolean }) {\n const body = (\n <>\n <div className=\"flex min-h-[19.6px] items-center gap-2\">\n {/* The icon opens the file too, so the target is larger than the words (Miky, third round). */}\n <Link href={HREF} variant=\"quiet\" className=\"flex min-w-0 flex-1 items-center gap-2 text-body-2-strong text-text-primary\">\n <ThingIcon kind={thing.kind} />\n <span className=\"truncate\">{thing.title}</span>\n </Link>\n {thing.done && <ProgressRing done={thing.done.done} total={thing.done.total} />}\n {thing.members && <AvatarGroup members={people(thing.members)} size={16} />}\n </div>\n {/* The key sits with the project under the title: \"ABC-2 \u00B7 Group one\" (Miky, second round). */}\n {(thing.key || thing.subtitle) && <p className=\"-mt-1 pl-6 text-caption text-text-secondary\">{[thing.key, thing.subtitle].filter(Boolean).join(' \u00B7 ')}</p>}\n {thing.summary && <p className=\"line-clamp-2 text-body-2 text-text-secondary\">{thing.summary}</p>}\n {thing.children && (\n <div className=\"flex flex-col divide-y divide-border-subtle rounded-md border border-border-subtle\">\n {thing.children.map((child) => (\n <div key={child.key} className=\"relative flex h-8 items-center gap-2 px-2 hover:bg-bg-active\">\n {/* The status is the row's icon: a list of issues needs no issue icon on every row (Miky, second round). */}\n <StatusMark status={child.status} />\n <Key>{child.key}</Key>\n {/* Each issue opens on its own; the link covers its row (Miky, third round). */}\n <span className=\"contents text-body-2 text-text-primary\">\n <Link href={HREF} variant=\"quiet\" cover truncate className=\"min-w-0 flex-1\">\n {child.title}\n </Link>\n </span>\n {child.assignee ? <Avatar name={child.assignee} size={16} /> : <span className=\"size-4 shrink-0\" aria-hidden />}\n </div>\n ))}\n </div>\n )}\n {thing.latest && (\n <div className=\"flex flex-col gap-1\">\n {/*\n \"Latest\" in words: the card follows the topic, so it is not the conversation the link was sent about.\n The label names the whole box, so it sits above it, not inside (Miky, fourth round).\n */}\n <span className=\"text-caption text-text-muted\">Latest conversation</span>\n <div className=\"flex gap-2 rounded-md border border-border-subtle p-2\">\n <Avatar name={thing.latest.author} size={16} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-baseline gap-2 text-caption\">\n <span className=\"text-text-primary\">{thing.latest.author}</span>\n <span className=\"text-text-muted\">{thing.latest.at}</span>\n </div>\n <p className=\"line-clamp-2 text-body-2 text-text-secondary\">{thing.latest.body}</p>\n </div>\n </div>\n </div>\n )}\n <div className=\"flex min-h-6 items-center gap-2 border-t border-border-subtle pt-2\">\n {(thing.kind === 'issue' || thing.kind === 'project') && <Pickers thing={thing} readOnly={readOnly} />}\n {thing.kind === 'topic' && (\n <span className=\"flex items-center gap-2\">\n <span className=\"text-caption text-text-secondary\">Updated {thing.updated}</span>\n </span>\n )}\n {/* Faces only, as every other widget: names would not fit once several people wrote it (Miky, third round). */}\n {thing.authors && (\n <span className=\"flex min-w-0 items-center gap-2 text-caption text-text-secondary\">\n <AvatarGroup members={people(thing.authors)} size={16} />\n <span className=\"shrink-0\">{thing.updated}</span>\n </span>\n )}\n {thing.kind === 'topic' && (\n <span className=\"ml-auto flex\">\n <Comments count={thing.conversations ?? 0} unread={thing.unread} noun=\"conversations\" />\n </span>\n )}\n {thing.comments !== undefined && (\n <span className=\"ml-auto flex\">\n <Comments count={thing.comments} unread={thing.unread} />\n </span>\n )}\n </div>\n </>\n )\n if (bare) return <div className=\"flex flex-col gap-2 p-3\">{body}</div>\n return (\n <Card fill=\"inset\" data-card=\"detailed\" className=\"flex flex-col gap-2 p-3\">\n {body}\n </Card>\n )\n}\n\n/** The detailed card, on hover over a chip or a compact card: the package's preview card. */\nexport function HoverPreview({ thing, block = false, children }: { thing: Thing; block?: boolean; children: ReactNode }) {\n // A compact card fills its column; PreviewCard's own trigger is inline and would shrink it to its words.\n return (\n <PreviewCard side=\"bottom\" content={<DetailedCard thing={thing} bare />} wrapperClassName={block ? 'grid w-full min-w-0 grid-cols-[minmax(0,1fr)]' : 'max-w-full'}>\n {children}\n </PreviewCard>\n )\n}\n\n/** The hover preview drawn open, for a still picture of it: the same box PreviewCard draws. */\nexport function HoverPreviewOpen({ thing }: { thing: Thing }) {\n return (\n <MenuPanel className=\"w-[360px] p-0\">\n <DetailedCard thing={thing} bare />\n </MenuPanel>\n )\n}\n\n/**\n * A linked message, detailed: a card, the frame every other detailed card has\n * (Miky, 15 Oct: \"card it is\", over a quote). The time is the link.\n */\nexport function MessageCard({ thing }: { thing: Thing }) {\n return (\n <Card fill=\"inset\" className=\"flex flex-col gap-2 p-3\">\n <div className=\"flex items-center gap-2\">\n <Avatar name={thing.author} size={24} />\n <span className=\"text-body-2-strong text-text-primary\">{thing.author}</span>\n <Link href={HREF} variant=\"quiet\" className=\"text-caption text-text-muted\">\n {thing.updated}\n </Link>\n </div>\n <p className=\"line-clamp-4 text-body-2 text-text-primary\">{thing.body}</p>\n </Card>\n )\n}\n\n/* ------------------------------------------------- loading, not available */\n\n/** What a kind's detailed card will hold, known before it loads (the kind is in the address). */\nexport interface LoadingShape {\n subtitle?: boolean\n summary?: boolean\n rows?: number\n preview?: boolean\n ring?: boolean\n footer?: 'pickers' | 'people' | 'line' | 'none'\n}\n\n/** The shape a thing's detailed card takes, for its placeholder. */\nexport function loadingShapeOf(thing: Thing): LoadingShape {\n return {\n subtitle: !!(thing.subtitle || thing.key),\n summary: !!thing.summary,\n rows: thing.children?.length ?? 0,\n preview: !!thing.latest,\n ring: !!thing.done,\n footer: thing.kind === 'issue' || thing.kind === 'project' ? 'pickers' : thing.authors ? 'people' : 'line',\n }\n}\n\n/** A bar that shows on an inset card: the package's bar is the inset colour, and vanishes on one. */\nfunction Bar({ className }: { className?: string }) {\n return <SkeletonBar className={cn('bg-bg-active', className)} />\n}\n\n/**\n * Loading: the card it will become, drawn in bars, at the card's own height,\n * so the conversation does not jump when it arrives.\n */\nexport function CardLoading({ kind, layout, shape = {} }: { kind: Thing['kind']; layout: CardLayout; shape?: LoadingShape }) {\n if (layout === 'compact') {\n return (\n <Card fill=\"inset\" aria-hidden data-card=\"loading\" className=\"flex h-9 items-center gap-2 px-3 animate-skeleton-in\">\n <ThingIcon kind={kind} />\n <Bar className=\"h-3 w-2/5\" />\n <Bar className=\"ml-auto size-4 rounded-full\" />\n </Card>\n )\n }\n const { subtitle = false, summary = true, rows = 0, preview = false, ring = false, footer = 'line' } = shape\n return (\n <Card fill=\"inset\" aria-hidden data-card=\"loading\" className=\"flex flex-col gap-2 p-3 animate-skeleton-in\">\n <div className=\"flex h-[19.6px] items-center gap-2\">\n <ThingIcon kind={kind} />\n <Bar className=\"h-3.5 w-1/2\" />\n {ring && <Bar className=\"ml-auto size-4 rounded-full\" />}\n </div>\n {subtitle && (\n <div className=\"-mt-1 flex h-[14.4px] items-center pl-6\">\n <Bar className=\"h-2.5 w-20\" />\n </div>\n )}\n {summary && (\n <div className=\"flex h-[19.6px] items-center\">\n <Bar className=\"h-3 w-4/5\" />\n </div>\n )}\n {rows > 0 && (\n <div className=\"flex flex-col divide-y divide-border-subtle rounded-md border border-border-subtle\">\n {Array.from({ length: rows }, (_, i) => (\n <div key={i} className=\"flex h-8 items-center px-2\">\n <Bar className=\"h-3 w-1/3\" />\n </div>\n ))}\n </div>\n )}\n {preview && (\n <div className=\"flex flex-col gap-1\">\n <div className=\"flex h-[14.4px] items-center\">\n <Bar className=\"h-2.5 w-28\" />\n </div>\n <div className=\"flex h-[71.6px] flex-col justify-center gap-2 rounded-md border border-border-subtle p-2\">\n <Bar className=\"h-2.5 w-24\" />\n <Bar className=\"h-3 w-full\" />\n <Bar className=\"h-3 w-3/5\" />\n </div>\n </div>\n )}\n {footer !== 'none' && (\n <div className=\"flex min-h-6 items-center gap-2 border-t border-border-subtle pt-2\">\n {footer === 'pickers' ? (\n <>\n <Bar className=\"h-6 w-24 rounded-md\" />\n <Bar className=\"h-6 w-28 rounded-md\" />\n </>\n ) : footer === 'people' ? (\n <>\n <Bar className=\"size-4 rounded-full\" />\n <Bar className=\"h-3 w-40\" />\n </>\n ) : (\n <Bar className=\"h-3 w-40\" />\n )}\n </div>\n )}\n </Card>\n )\n}\n\n/**\n * Not available: one line, at compact's height, whichever layout was chosen.\n * Deleted and unreadable read the same (Widgets 2, Not in scope). Here the\n * way out stays: there is no title to click, and the app it lives in may\n * still open it for you.\n */\nexport function CardUnavailable({ kind, noun, app }: { kind: Thing['kind']; noun: string; app: string }) {\n return (\n <Card fill=\"none\" unreadable className=\"flex h-9 items-center gap-2 px-3\">\n <ThingIcon kind={kind} />\n <span className=\"min-w-0 flex-1 truncate text-body-2 text-text-muted\">{noun} \u2014 not available.</span>\n <span className=\"shrink-0 text-caption\">\n <Link href={HREF} variant=\"quiet\" external className=\"text-text-secondary\">\n Open in {app} \u2197\n </Link>\n </span>\n </Card>\n )\n}\n\n/* ------------------------------------------------------------ the part */\n\n/**\n * A block of a description, shared live (a part). No compact or detailed:\n * a pasted block link inserts the part itself (the-author-chooses-a-links-card,\n * Accepted). The same header as a detailed card: the key and the title of\n * the file it is part of, as the link.\n */\nexport function PartCard({ kind, heading, partKey, children, state = 'ready', noun, app }: { kind: Thing['kind']; heading: string; partKey?: string; children?: ReactNode; state?: 'ready' | 'loading' | 'unavailable'; noun: string; app: string }) {\n if (state === 'loading') return <CardLoading kind={kind} layout=\"detailed\" shape={{ summary: true, rows: 0, footer: 'none' }} />\n if (state === 'unavailable') return <CardUnavailable kind={kind} noun={`A part of this ${noun.toLowerCase()}`} app={app} />\n return (\n <Card fill=\"inset\" className=\"flex flex-col gap-2 p-3\">\n <Link href={HREF} variant=\"quiet\" className=\"flex min-w-0 items-center gap-2 text-body-2-strong text-text-primary\">\n <ThingIcon kind={kind} />\n {partKey && <Key>{partKey}</Key>}\n <span className=\"truncate\">{heading}</span>\n </Link>\n <div className=\"border-l-2 border-border-default pl-3\">{children}</div>\n </Card>\n )\n}\n\n/**\n * A comment's anchor: the part it is about, quoted above the comment.\n * Unchanged from today (the two apps already agree), with the deleted line in\n * the same muted voice as \"not available.\".\n */\nexport function CommentAnchor({ text }: { text?: string }) {\n if (text === undefined) return <div className=\"border-l-2 border-border-default pl-2 text-caption text-text-muted\">The part this is about was deleted.</div>\n return (\n <div className=\"border-l-2 border-border-strong pl-2 text-caption text-text-muted\">\n <span className=\"line-clamp-2 italic\">{text}</span>\n </div>\n )\n}\n\n/* ----------------------------------------------------------- attachment */\n\n/** An attachment's chip: a paperclip and the file's name. */\nexport function AttachmentChip({ name, state = 'ready' }: { name: string; state?: 'ready' | 'loading' | 'unavailable' }) {\n if (state === 'loading') {\n return (\n <InlineChip icon={<IconPaperclip size={14} stroke={1.5} aria-hidden />}>\n <SkeletonBar className=\"h-2.5 w-16\" />\n </InlineChip>\n )\n }\n if (state === 'unavailable') {\n return (\n <InlineChip icon={<IconPaperclip size={14} stroke={1.5} aria-hidden />} className=\"text-text-muted\">\n File, not available\n </InlineChip>\n )\n }\n return (\n <InlineChip icon={<IconPaperclip size={14} stroke={1.5} aria-hidden />} href={HREF} className=\"max-w-[24ch]\">\n <span className=\"truncate\">{name}</span>\n </InlineChip>\n )\n}\n", "/**\n * Widgets 2 prototype: the placeholder things every widget on these pages\n * draws. Words are placeholders (stories-and-pages.md); the kinds are named,\n * because telling them apart is what the prototype is for.\n */\nimport type { ThingKind } from '../KindIcon'\n\n/** The colours a status draws in, as the apps' manifests name them. */\nexport type StatusColour = 'neutral' | 'blue' | 'green' | 'muted'\n\nexport interface Status {\n label: string\n colour: StatusColour\n}\n\n/** One thing a link can stand for, with every field any app draws today. */\nexport interface Thing {\n /** The kinds the prototype covers, one more than KindIcon has: a page. */\n kind: ThingKind | 'page'\n /** The noun a card's corner says. */\n noun: string\n /** The app it opens in. */\n app: string\n title: string\n /** Its key, the way people say it: an issue's \"WID-2\". */\n key?: string\n /** The line under the title: the project an issue is in, a page's Folder. */\n subtitle?: string\n /** The opening of its description, a page's text, a message's words: what the apps draw today. */\n body?: string\n /**\n * A short summary of the file, one or two sentences (Miky, 15 Oct: detailed\n * shows the summary, not the opening of the long description). No file\n * carries one yet; it is its own project.\n */\n summary?: string\n status?: Status\n assignee?: string\n author?: string\n /** Everyone who wrote a page. */\n authors?: string[]\n comments?: number\n /**\n * The reader has something unread in its conversation: they are a member\n * and it moved since they last read it (SPEC \u00A711.8, membership-replaces-following).\n */\n unread?: boolean\n /** A project's issues, drawn as rows inside its detailed card. */\n children?: { title: string; key: string; status: Status; assignee?: string }[]\n /** A project's progress line. */\n progress?: string\n /** A project's progress as numbers, for the ring. */\n done?: { done: number; total: number }\n /** A topic: how many conversations, who is in it, and its latest one. */\n conversations?: number\n members?: string[]\n latest?: { author: string; body: string; at: string }\n updated?: string\n}\n\nconst TODO: Status = { label: 'Todo', colour: 'neutral' }\nconst DOING: Status = { label: 'In progress', colour: 'blue' }\nconst DONE: Status = { label: 'Done', colour: 'green' }\n\nconst PARAGRAPH =\n 'A description long enough to need more than one line, so the card has to choose how much of it to show and where to cut it short. The rest is there when you open it.'\n\nconst SUMMARY = 'A one-sentence summary of what this is for.'\n\nexport const ISSUE: Thing = {\n kind: 'issue',\n noun: 'Issue',\n app: 'Ship',\n title: 'Item one, with a title long enough to be cut short',\n key: 'ABC-2',\n subtitle: 'Group one',\n body: PARAGRAPH,\n summary: SUMMARY,\n status: DOING,\n assignee: 'Person One',\n comments: 4,\n unread: true,\n}\n\nexport const PROJECT: Thing = {\n kind: 'project',\n noun: 'Project',\n app: 'Ship',\n title: 'Group one',\n body: PARAGRAPH,\n summary: SUMMARY,\n status: DOING,\n assignee: 'Person Two',\n progress: '3 of 8 done',\n done: { done: 3, total: 8 },\n comments: 6,\n children: [\n { title: 'Item one', key: 'ABC-1', status: DONE, assignee: 'Person One' },\n { title: 'Item two', key: 'ABC-2', status: DOING, assignee: 'Person Two' },\n { title: 'Item three', key: 'ABC-3', status: TODO },\n ],\n}\n\nexport const TOPIC: Thing = {\n kind: 'topic',\n noun: 'Topic',\n app: 'Peek',\n title: 'Place one',\n conversations: 12,\n unread: true,\n members: ['Person One', 'Person Two', 'Person Three'],\n updated: '2h ago',\n latest: { author: 'Person Three', body: 'The latest conversation in the topic, as its first message reads. It may run to a second line before it is cut short.', at: '2h ago' },\n}\n\nexport const MESSAGE: Thing = {\n kind: 'message',\n noun: 'Message',\n app: 'Peek',\n title: 'Person Two',\n author: 'Person Two',\n body: 'A message of two sentences, written by someone in a conversation. It is the words the link points to.',\n updated: '10:42',\n}\n\nexport const PAGE: Thing = {\n kind: 'page',\n noun: 'Page',\n app: 'Leaf',\n title: 'Page one',\n subtitle: 'Folder one',\n body: PARAGRAPH,\n summary: SUMMARY,\n author: 'Person One',\n authors: ['Person One', 'Person Two'],\n comments: 2,\n updated: 'Edited yesterday',\n}\n\n/** A block of a description: drawn live, as the part it is. */\nexport const PART = {\n heading: 'Item one',\n noun: 'Issue',\n app: 'Ship',\n blocks: ['Heading of the part', 'A paragraph of the part, as it reads in the description right now.', 'A point in a list', 'Another point'],\n}\n\nexport const ATTACHMENT = { name: 'picture-one.png', size: 182_400, contentType: 'image/png' }\nexport const ATTACHMENT_FILE = { name: 'document-one.pdf', size: 1_240_000, contentType: 'application/pdf' }\n\n/** A file the reader cannot read: only its kind and app are known, from its address. */\nexport const UNREADABLE = { kind: 'issue' as const, noun: 'Issue', app: 'Ship', address: 'naddr1qqjxgdmzxy6r2vfs95cryvmp956x2etp94sns' }\n\n/** A small picture for the attachment, drawn rather than fetched. */\nexport const PICTURE =\n 'data:image/svg+xml;utf8,' +\n encodeURIComponent(\n '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"480\" height=\"300\"><rect width=\"480\" height=\"300\" fill=\"#8a94a6\"/><circle cx=\"360\" cy=\"90\" r=\"40\" fill=\"#d8dde6\"/><path d=\"M0 300 L170 120 L300 260 L380 190 L480 300 Z\" fill=\"#5d6778\"/></svg>',\n )\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
3
|
+
"sources": ["../src/widgets-2/proposed.tsx", "../src/widgets-2/fixtures.ts", "../src/widgets-2/places.tsx"],
|
|
4
|
+
"sourcesContent": ["/**\n * Widgets 2 prototype: the proposed shared kit, one drawing of each widget for\n * Peek, Ship and Leaf alike. Written as the package would export it: no app\n * name and no product noun in a name or a prop. The kind's icon, the key and\n * every field arrive as data from the app's manifest.\n *\n * In `src/` on `concept/widgets-2` only, so the apps' own Storybooks can place\n * it (WID-3): `@estiva-app/ui/widgets-2`, an unlisted entry published only as a\n * `concept` prerelease. The build project shapes the real exports from it once\n * Miky and Katerina approve it.\n *\n * Second iteration, from Miky's notes on WID-2 (15 Oct):\n * - no noun in a card's corner and no \"Open in \u2026\": the icon says what it is,\n * and the title is the link;\n * - an issue shows its key; the only status and assignee are the actionable ones;\n * - detailed shows the file's short summary, not the opening of its description;\n * - hovering a chip or a compact card shows the detailed card;\n * - a project shows its progress as a ring, its status and lead as actions,\n * its comments, and each issue's assignee;\n * - a topic shows its conversations, its last update and its members, and\n * detailed adds the latest conversation;\n * - a message is drawn with its author's face; a page with its authors and\n * its last edit.\n */\nimport { IconCircle, IconCircleCheck, IconCircleDashed, IconCircleHalf2, IconCircleX, IconFileText, IconMessage2, IconPaperclip } from '@tabler/icons-react'\nimport type { ReactNode } from 'react'\nimport { Avatar } from '../Avatar'\nimport { AvatarGroup } from '../AvatarGroup'\nimport { Card } from '../Card'\nimport { cn } from '../cn'\nimport { InlineChip } from '../InlineChip'\nimport { KindIcon } from '../KindIcon'\nimport { Link } from '../Link'\nimport { MenuPanel } from '../Menu'\nimport { PreviewCard } from '../PreviewCard'\nimport { Select } from '../Select'\nimport { SkeletonBar } from '../Skeleton'\nimport { UnreadDot } from '../UnreadDot'\nimport type { Status, StatusColour, Thing } from './fixtures'\n\nconst HREF = '#'\nconst noop = () => {}\n\n/* ------------------------------------------------------------ the parts */\n\nconst STATUS_ICON: Record<StatusColour, { Icon: typeof IconCircle; tone: string }> = {\n neutral: { Icon: IconCircleDashed, tone: 'text-text-secondary' },\n blue: { Icon: IconCircleHalf2, tone: 'text-info-default' },\n green: { Icon: IconCircleCheck, tone: 'text-success-default' },\n muted: { Icon: IconCircleX, tone: 'text-text-muted' },\n}\n\n/** A status as its shape: Ship's icons, which read without colour. */\nexport function StatusMark({ status }: { status: Status }) {\n return (\n <span className=\"inline-flex shrink-0 items-center\" title={status.label}>\n <StatusIcon status={status} />\n <span className=\"sr-only\">{status.label}</span>\n </span>\n )\n}\n\n/** The status's shape alone, beside words that already name it. */\nfunction StatusIcon({ status }: { status: Status }) {\n const { Icon, tone } = STATUS_ICON[status.colour]\n return <Icon size={14} stroke={1.5} className={cn('shrink-0', tone)} aria-hidden />\n}\n\n/** The icon for a kind. A page gets its own (proposed): today every app draws it as \"another app's file\". */\nexport function ThingIcon({ kind, size = 16, tone = 'secondary' }: { kind: Thing['kind']; size?: 14 | 16; tone?: 'secondary' | 'inherit' }) {\n if (kind === 'page') return <IconFileText size={size} stroke={1.5} className={cn('shrink-0', tone === 'secondary' && 'text-text-secondary')} aria-hidden />\n return <KindIcon kind={kind} size={size} tone={tone} />\n}\n\n/** A key, the way people say it (\"ABC-2\"): before the title, quieter than it. */\nfunction Key({ children }: { children: ReactNode }) {\n return <span className=\"shrink-0 text-caption tabular-nums text-text-muted\">{children}</span>\n}\n\n/**\n * Progress as a small ring with the count beside it as a label (Miky, 15 Oct,\n * second round: the ring with the count inside was too big).\n */\nexport function ProgressRing({ done, total }: { done: number; total: number }) {\n const size = 16\n const stroke = 2\n const r = (size - stroke) / 2\n const c = 2 * Math.PI * r\n return (\n <span className=\"inline-flex shrink-0 items-center gap-1 text-caption tabular-nums text-text-secondary\" title={`${done} of ${total} done`}>\n <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} className=\"shrink-0 -rotate-90\" aria-hidden>\n <circle cx={size / 2} cy={size / 2} r={r} fill=\"none\" strokeWidth={stroke} className=\"stroke-border-default\" />\n <circle cx={size / 2} cy={size / 2} r={r} fill=\"none\" strokeWidth={stroke} strokeLinecap=\"round\" strokeDasharray={`${(c * done) / total} ${c}`} className=\"stroke-success-default\" />\n </svg>\n {done}/{total}\n </span>\n )\n}\n\n/*\n The unread dot after a count is the package's UnreadDot: the reader has\n something unread in this conversation. Only a member has unread (SPEC \u00A711.8),\n so a reader who is not one sees the count alone.\n*/\n\n/** Where the count leads: the file's conversation, at the first unread when there is one. */\nconst CONVERSATION_HREF = '#conversation'\n\n/**\n * The comment count, the words brighter and a dot after it when the reader\n * has unread there. It is a link to the conversation (Miky, fourth round:\n * \"especially needed when you have unread messages\"), and it sits above a\n * compact card's covering link, so the card opens the file and the count\n * opens its conversation.\n */\nfunction Comments({ count, unread = false, noun = 'comments' }: { count: number; unread?: boolean; noun?: string }) {\n return (\n <Link\n href={CONVERSATION_HREF}\n variant=\"plain\"\n className={cn(\n 'relative z-10 -mx-1 inline-flex shrink-0 items-center gap-1 rounded-sm px-1 text-caption tabular-nums hover:bg-bg-active',\n unread ? 'font-medium text-text-primary' : 'text-text-secondary',\n )}\n >\n <IconMessage2 size={14} stroke={1.5} aria-hidden />\n {count}\n <span className=\"sr-only\">{unread ? `${noun}, unread \u2014 open the conversation` : `${noun} \u2014 open the conversation`}</span>\n {unread && <UnreadDot className=\"h-auto w-auto\" />}\n </Link>\n )\n}\n\n/**\n * A topic's conversations: the same icon and count as every other file's\n * comments (Miky, third round), after when the last one moved.\n */\nfunction Conversations({ thing }: { thing: Thing }) {\n return (\n <span className=\"inline-flex shrink-0 items-center gap-2\">\n <span className=\"text-caption text-text-secondary\">{thing.updated}</span>\n <Comments count={thing.conversations ?? 0} unread={thing.unread} noun=\"conversations\" />\n </span>\n )\n}\n\nconst people = (names: string[] = []) => names.map((name) => ({ name }))\n\n/**\n * The kind's own pickers, the only place its status and owner show in detailed.\n * Read only, for a reader who may not change the file and in the hover preview\n * (Miky, 15 Oct: no actions on hover), they are the same two values as words,\n * in the same place.\n */\nfunction Pickers({ thing, readOnly = false }: { thing: Thing; readOnly?: boolean }) {\n if (!thing.status) return null\n if (readOnly) {\n return (\n <span className=\"flex h-6 min-w-0 items-center gap-3 text-caption text-text-secondary\">\n <span className=\"inline-flex shrink-0 items-center gap-1\">\n <StatusIcon status={thing.status} />\n {thing.status.label}\n </span>\n <span className=\"inline-flex min-w-0 items-center gap-1\">\n <Avatar name={thing.assignee} size={16} />\n <span className=\"truncate\">{thing.assignee ?? 'Unassigned'}</span>\n </span>\n </span>\n )\n }\n return (\n <>\n <Select size=\"small\" className=\"w-auto\" value=\"s\" onChange={noop} ariaLabel=\"Status\" options={[{ value: 's', label: thing.status.label, leading: <StatusIcon status={thing.status} /> }]} />\n <Select\n size=\"small\"\n className=\"w-auto\"\n value=\"a\"\n onChange={noop}\n ariaLabel={thing.kind === 'project' ? 'Lead' : 'Assignee'}\n options={[{ value: 'a', label: thing.assignee ?? 'Unassigned', leading: <Avatar name={thing.assignee} size={16} /> }]}\n />\n </>\n )\n}\n\n/* ----------------------------------------------------------------- chip */\n\nexport interface ReferenceChipProps {\n thing: Thing\n state?: 'ready' | 'loading' | 'unavailable'\n /** Hovering the chip shows the detailed card. */\n preview?: boolean\n}\n\n/** What leads a chip and a compact card: the author's face for a message, the kind's icon otherwise. */\nfunction Lead({ thing, chip = false }: { thing: Thing; chip?: boolean }) {\n if (thing.kind === 'message') return <Avatar name={thing.author} size={16} />\n return <ThingIcon kind={thing.kind} size={chip ? 14 : 16} tone={chip ? 'inherit' : 'secondary'} />\n}\n\n/**\n * A link in running text: the icon (or the author's face), the key, the\n * title cut at 24ch, the status as its shape. Loading keeps the icon (the\n * kind is known from the address); not available says so in words.\n */\nexport function ReferenceChip({ thing, state = 'ready', preview = false }: ReferenceChipProps) {\n if (state === 'loading') {\n return (\n <InlineChip icon={<ThingIcon kind={thing.kind} size={14} tone=\"inherit\" />}>\n <SkeletonBar className=\"h-2.5 w-16\" />\n </InlineChip>\n )\n }\n if (state === 'unavailable') {\n return (\n <InlineChip icon={<ThingIcon kind={thing.kind} size={14} tone=\"inherit\" />} className=\"text-text-muted\">\n {thing.noun}, not available\n </InlineChip>\n )\n }\n const chip = (\n <InlineChip icon={<Lead thing={thing} chip />} href={HREF} className=\"max-w-[28ch]\">\n {thing.key && <Key>{thing.key}</Key>}\n <span className=\"truncate\">{thing.kind === 'message' ? `${thing.author}: ${thing.body}` : thing.title}</span>\n {thing.status && <StatusMark status={thing.status} />}\n </InlineChip>\n )\n return preview ? <HoverPreview thing={thing}>{chip}</HoverPreview> : chip\n}\n\n/* ---------------------------------------------------------------- cards */\n\nexport type CardLayout = 'compact' | 'detailed'\n\n/** Compact: one 36px line. Every kind, every app. Hovering it shows the detailed card. */\nexport function CompactCard({ thing, preview = false }: { thing: Thing; preview?: boolean }) {\n const card = (\n <Card fill=\"inset\" hover=\"hairline\" className=\"relative flex h-9 items-center gap-2 px-3\">\n <Lead thing={thing} />\n {thing.kind === 'message' ? (\n <span className=\"flex min-w-0 flex-1 items-baseline gap-2 text-body-2\">\n <span className=\"shrink-0 text-body-2-strong text-text-primary\">{thing.author}</span>\n <Link href={HREF} variant=\"quiet\" cover truncate className=\"text-text-secondary\">\n {thing.body}\n </Link>\n </span>\n ) : (\n <>\n {thing.key && <Key>{thing.key}</Key>}\n <span className=\"contents text-body-2-strong text-text-primary\">\n <Link href={HREF} variant=\"quiet\" cover truncate className=\"min-w-0 flex-1\">\n {thing.title}\n </Link>\n </span>\n </>\n )}\n {thing.kind === 'topic' && <Conversations thing={thing} />}\n {thing.kind === 'page' && <span className=\"shrink-0 text-caption text-text-secondary\">{thing.updated}</span>}\n {thing.kind === 'message' && <span className=\"shrink-0 text-caption text-text-muted\">{thing.updated}</span>}\n {/* Compact keeps to one line: the comment count shows only when there is something unread in it. */}\n {thing.unread && thing.comments !== undefined && <Comments count={thing.comments} unread />}\n {thing.status && thing.kind === 'issue' && <StatusMark status={thing.status} />}\n {thing.done && <ProgressRing done={thing.done.done} total={thing.done.total} />}\n {thing.assignee && <Avatar name={thing.assignee} size={16} />}\n {thing.members && <AvatarGroup members={people(thing.members)} size={16} />}\n {thing.authors && <AvatarGroup members={people(thing.authors)} size={16} />}\n </Card>\n )\n return preview ? (\n <HoverPreview thing={thing} block>\n {card}\n </HoverPreview>\n ) : (\n card\n )\n}\n\n/**\n * Detailed: what compact cannot hold. The header is compact's line (icon, key,\n * title as the link; a project's ring or a topic's members in the corner),\n * then the summary, the kind's own extras (a project's issues, a topic's\n * latest conversation), and a footer: the pickers and the comments for an\n * issue or a project, who and when for a page and a topic.\n *\n * `bare` drops the card's frame, for the hover preview, which draws the\n * package's one elevated box around it.\n */\nexport function DetailedCard({ thing, bare = false, readOnly = bare }: { thing: Thing; bare?: boolean; readOnly?: boolean }) {\n const body = (\n <>\n <div className=\"flex min-h-[19.6px] items-center gap-2\">\n {/* The icon opens the file too, so the target is larger than the words (Miky, third round). */}\n <Link href={HREF} variant=\"quiet\" className=\"flex min-w-0 flex-1 items-center gap-2 text-body-2-strong text-text-primary\">\n <ThingIcon kind={thing.kind} />\n <span className=\"truncate\">{thing.title}</span>\n </Link>\n {thing.done && <ProgressRing done={thing.done.done} total={thing.done.total} />}\n {thing.members && <AvatarGroup members={people(thing.members)} size={16} />}\n </div>\n {/* The key sits with the project under the title: \"ABC-2 \u00B7 Group one\" (Miky, second round). */}\n {(thing.key || thing.subtitle) && <p className=\"-mt-1 pl-6 text-caption text-text-secondary\">{[thing.key, thing.subtitle].filter(Boolean).join(' \u00B7 ')}</p>}\n {thing.summary && <p className=\"line-clamp-2 text-body-2 text-text-secondary\">{thing.summary}</p>}\n {thing.children && (\n <div className=\"flex flex-col divide-y divide-border-subtle rounded-md border border-border-subtle\">\n {thing.children.map((child) => (\n <div key={child.key} className=\"relative flex h-8 items-center gap-2 px-2 hover:bg-bg-active\">\n {/* The status is the row's icon: a list of issues needs no issue icon on every row (Miky, second round). */}\n <StatusMark status={child.status} />\n <Key>{child.key}</Key>\n {/* Each issue opens on its own; the link covers its row (Miky, third round). */}\n <span className=\"contents text-body-2 text-text-primary\">\n <Link href={HREF} variant=\"quiet\" cover truncate className=\"min-w-0 flex-1\">\n {child.title}\n </Link>\n </span>\n {child.assignee ? <Avatar name={child.assignee} size={16} /> : <span className=\"size-4 shrink-0\" aria-hidden />}\n </div>\n ))}\n </div>\n )}\n {thing.latest && (\n <div className=\"flex flex-col gap-1\">\n {/*\n \"Latest\" in words: the card follows the topic, so it is not the conversation the link was sent about.\n The label names the whole box, so it sits above it, not inside (Miky, fourth round).\n */}\n <span className=\"text-caption text-text-muted\">Latest conversation</span>\n <div className=\"flex gap-2 rounded-md border border-border-subtle p-2\">\n <Avatar name={thing.latest.author} size={16} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-baseline gap-2 text-caption\">\n <span className=\"text-text-primary\">{thing.latest.author}</span>\n <span className=\"text-text-muted\">{thing.latest.at}</span>\n </div>\n <p className=\"line-clamp-2 text-body-2 text-text-secondary\">{thing.latest.body}</p>\n </div>\n </div>\n </div>\n )}\n <div className=\"flex min-h-6 items-center gap-2 border-t border-border-subtle pt-2\">\n {(thing.kind === 'issue' || thing.kind === 'project') && <Pickers thing={thing} readOnly={readOnly} />}\n {thing.kind === 'topic' && (\n <span className=\"flex items-center gap-2\">\n <span className=\"text-caption text-text-secondary\">Updated {thing.updated}</span>\n </span>\n )}\n {/* Faces only, as every other widget: names would not fit once several people wrote it (Miky, third round). */}\n {thing.authors && (\n <span className=\"flex min-w-0 items-center gap-2 text-caption text-text-secondary\">\n <AvatarGroup members={people(thing.authors)} size={16} />\n <span className=\"shrink-0\">{thing.updated}</span>\n </span>\n )}\n {thing.kind === 'topic' && (\n <span className=\"ml-auto flex\">\n <Comments count={thing.conversations ?? 0} unread={thing.unread} noun=\"conversations\" />\n </span>\n )}\n {thing.comments !== undefined && (\n <span className=\"ml-auto flex\">\n <Comments count={thing.comments} unread={thing.unread} />\n </span>\n )}\n </div>\n </>\n )\n if (bare) return <div className=\"flex flex-col gap-2 p-3\">{body}</div>\n return (\n <Card fill=\"inset\" data-card=\"detailed\" className=\"flex flex-col gap-2 p-3\">\n {body}\n </Card>\n )\n}\n\n/** The detailed card, on hover over a chip or a compact card: the package's preview card. */\nexport function HoverPreview({ thing, block = false, children }: { thing: Thing; block?: boolean; children: ReactNode }) {\n // A compact card fills its column; PreviewCard's own trigger is inline and would shrink it to its words.\n return (\n <PreviewCard side=\"bottom\" content={<DetailedCard thing={thing} bare />} wrapperClassName={block ? 'grid w-full min-w-0 grid-cols-[minmax(0,1fr)]' : 'max-w-full'}>\n {children}\n </PreviewCard>\n )\n}\n\n/** The hover preview drawn open, for a still picture of it: the same box PreviewCard draws. */\nexport function HoverPreviewOpen({ thing }: { thing: Thing }) {\n return (\n <MenuPanel className=\"w-[360px] p-0\">\n <DetailedCard thing={thing} bare />\n </MenuPanel>\n )\n}\n\n/**\n * A linked message, detailed: a card, the frame every other detailed card has\n * (Miky, 15 Oct: \"card it is\", over a quote). The time is the link.\n */\nexport function MessageCard({ thing }: { thing: Thing }) {\n return (\n <Card fill=\"inset\" className=\"flex flex-col gap-2 p-3\">\n <div className=\"flex items-center gap-2\">\n <Avatar name={thing.author} size={24} />\n <span className=\"text-body-2-strong text-text-primary\">{thing.author}</span>\n <Link href={HREF} variant=\"quiet\" className=\"text-caption text-text-muted\">\n {thing.updated}\n </Link>\n </div>\n <p className=\"line-clamp-4 text-body-2 text-text-primary\">{thing.body}</p>\n </Card>\n )\n}\n\n/* ------------------------------------------------- loading, not available */\n\n/** What a kind's detailed card will hold, known before it loads (the kind is in the address). */\nexport interface LoadingShape {\n subtitle?: boolean\n summary?: boolean\n rows?: number\n preview?: boolean\n ring?: boolean\n footer?: 'pickers' | 'people' | 'line' | 'none'\n}\n\n/** The shape a thing's detailed card takes, for its placeholder. */\nexport function loadingShapeOf(thing: Thing): LoadingShape {\n return {\n subtitle: !!(thing.subtitle || thing.key),\n summary: !!thing.summary,\n rows: thing.children?.length ?? 0,\n preview: !!thing.latest,\n ring: !!thing.done,\n footer: thing.kind === 'issue' || thing.kind === 'project' ? 'pickers' : thing.authors ? 'people' : 'line',\n }\n}\n\n/** A bar that shows on an inset card: the package's bar is the inset colour, and vanishes on one. */\nfunction Bar({ className }: { className?: string }) {\n return <SkeletonBar className={cn('bg-bg-active', className)} />\n}\n\n/**\n * Loading: the card it will become, drawn in bars, at the card's own height,\n * so the conversation does not jump when it arrives.\n */\nexport function CardLoading({ kind, layout, shape = {} }: { kind: Thing['kind']; layout: CardLayout; shape?: LoadingShape }) {\n if (layout === 'compact') {\n return (\n <Card fill=\"inset\" aria-hidden data-card=\"loading\" className=\"flex h-9 items-center gap-2 px-3 animate-skeleton-in\">\n <ThingIcon kind={kind} />\n <Bar className=\"h-3 w-2/5\" />\n <Bar className=\"ml-auto size-4 rounded-full\" />\n </Card>\n )\n }\n const { subtitle = false, summary = true, rows = 0, preview = false, ring = false, footer = 'line' } = shape\n return (\n <Card fill=\"inset\" aria-hidden data-card=\"loading\" className=\"flex flex-col gap-2 p-3 animate-skeleton-in\">\n <div className=\"flex h-[19.6px] items-center gap-2\">\n <ThingIcon kind={kind} />\n <Bar className=\"h-3.5 w-1/2\" />\n {ring && <Bar className=\"ml-auto size-4 rounded-full\" />}\n </div>\n {subtitle && (\n <div className=\"-mt-1 flex h-[14.4px] items-center pl-6\">\n <Bar className=\"h-2.5 w-20\" />\n </div>\n )}\n {summary && (\n <div className=\"flex h-[19.6px] items-center\">\n <Bar className=\"h-3 w-4/5\" />\n </div>\n )}\n {rows > 0 && (\n <div className=\"flex flex-col divide-y divide-border-subtle rounded-md border border-border-subtle\">\n {Array.from({ length: rows }, (_, i) => (\n <div key={i} className=\"flex h-8 items-center px-2\">\n <Bar className=\"h-3 w-1/3\" />\n </div>\n ))}\n </div>\n )}\n {preview && (\n <div className=\"flex flex-col gap-1\">\n <div className=\"flex h-[14.4px] items-center\">\n <Bar className=\"h-2.5 w-28\" />\n </div>\n <div className=\"flex h-[71.6px] flex-col justify-center gap-2 rounded-md border border-border-subtle p-2\">\n <Bar className=\"h-2.5 w-24\" />\n <Bar className=\"h-3 w-full\" />\n <Bar className=\"h-3 w-3/5\" />\n </div>\n </div>\n )}\n {footer !== 'none' && (\n <div className=\"flex min-h-6 items-center gap-2 border-t border-border-subtle pt-2\">\n {footer === 'pickers' ? (\n <>\n <Bar className=\"h-6 w-24 rounded-md\" />\n <Bar className=\"h-6 w-28 rounded-md\" />\n </>\n ) : footer === 'people' ? (\n <>\n <Bar className=\"size-4 rounded-full\" />\n <Bar className=\"h-3 w-40\" />\n </>\n ) : (\n <Bar className=\"h-3 w-40\" />\n )}\n </div>\n )}\n </Card>\n )\n}\n\n/**\n * Not available: one line, at compact's height, whichever layout was chosen.\n * Deleted and unreadable read the same (Widgets 2, Not in scope). Here the\n * way out stays: there is no title to click, and the app it lives in may\n * still open it for you.\n */\nexport function CardUnavailable({ kind, noun, app }: { kind: Thing['kind']; noun: string; app: string }) {\n return (\n <Card fill=\"none\" unreadable className=\"flex h-9 items-center gap-2 px-3\">\n <ThingIcon kind={kind} />\n <span className=\"min-w-0 flex-1 truncate text-body-2 text-text-muted\">{noun} \u2014 not available.</span>\n <span className=\"shrink-0 text-caption\">\n <Link href={HREF} variant=\"quiet\" external className=\"text-text-secondary\">\n Open in {app} \u2197\n </Link>\n </span>\n </Card>\n )\n}\n\n/* ------------------------------------------------------------ the part */\n\n/**\n * A block of a description, shared live (a part). No compact or detailed:\n * a pasted block link inserts the part itself (the-author-chooses-a-links-card,\n * Accepted). The same header as a detailed card: the key and the title of\n * the file it is part of, as the link.\n */\nexport function PartCard({ kind, heading, partKey, children, state = 'ready', noun, app }: { kind: Thing['kind']; heading: string; partKey?: string; children?: ReactNode; state?: 'ready' | 'loading' | 'unavailable'; noun: string; app: string }) {\n if (state === 'loading') return <CardLoading kind={kind} layout=\"detailed\" shape={{ summary: true, rows: 0, footer: 'none' }} />\n if (state === 'unavailable') return <CardUnavailable kind={kind} noun={`A part of this ${noun.toLowerCase()}`} app={app} />\n return (\n <Card fill=\"inset\" className=\"flex flex-col gap-2 p-3\">\n <Link href={HREF} variant=\"quiet\" className=\"flex min-w-0 items-center gap-2 text-body-2-strong text-text-primary\">\n <ThingIcon kind={kind} />\n {partKey && <Key>{partKey}</Key>}\n <span className=\"truncate\">{heading}</span>\n </Link>\n <div className=\"border-l-2 border-border-default pl-3\">{children}</div>\n </Card>\n )\n}\n\n/**\n * A comment's anchor: the part it is about, quoted above the comment.\n * Unchanged from today (the two apps already agree), with the deleted line in\n * the same muted voice as \"not available.\".\n */\nexport function CommentAnchor({ text }: { text?: string }) {\n if (text === undefined) return <div className=\"border-l-2 border-border-default pl-2 text-caption text-text-muted\">The part this is about was deleted.</div>\n return (\n <div className=\"border-l-2 border-border-strong pl-2 text-caption text-text-muted\">\n <span className=\"line-clamp-2 italic\">{text}</span>\n </div>\n )\n}\n\n/* ----------------------------------------------------------- attachment */\n\n/** An attachment's chip: a paperclip and the file's name. */\nexport function AttachmentChip({ name, state = 'ready' }: { name: string; state?: 'ready' | 'loading' | 'unavailable' }) {\n if (state === 'loading') {\n return (\n <InlineChip icon={<IconPaperclip size={14} stroke={1.5} aria-hidden />}>\n <SkeletonBar className=\"h-2.5 w-16\" />\n </InlineChip>\n )\n }\n if (state === 'unavailable') {\n return (\n <InlineChip icon={<IconPaperclip size={14} stroke={1.5} aria-hidden />} className=\"text-text-muted\">\n File, not available\n </InlineChip>\n )\n }\n return (\n <InlineChip icon={<IconPaperclip size={14} stroke={1.5} aria-hidden />} href={HREF} className=\"max-w-[24ch]\">\n <span className=\"truncate\">{name}</span>\n </InlineChip>\n )\n}\n", "/**\n * Widgets 2 prototype: the placeholder things every widget on these pages\n * draws. Words are placeholders (stories-and-pages.md); the kinds are named,\n * because telling them apart is what the prototype is for.\n */\nimport type { ThingKind } from '../KindIcon'\n\n/** The colours a status draws in, as the apps' manifests name them. */\nexport type StatusColour = 'neutral' | 'blue' | 'green' | 'muted'\n\nexport interface Status {\n label: string\n colour: StatusColour\n}\n\n/** One thing a link can stand for, with every field any app draws today. */\nexport interface Thing {\n /** The kinds the prototype covers, one more than KindIcon has: a page. */\n kind: ThingKind | 'page'\n /** The noun a card's corner says. */\n noun: string\n /** The app it opens in. */\n app: string\n title: string\n /** Its key, the way people say it: an issue's \"WID-2\". */\n key?: string\n /** The line under the title: the project an issue is in, a page's Folder. */\n subtitle?: string\n /** The opening of its description, a page's text, a message's words: what the apps draw today. */\n body?: string\n /**\n * A short summary of the file, one or two sentences (Miky, 15 Oct: detailed\n * shows the summary, not the opening of the long description). No file\n * carries one yet; it is its own project.\n */\n summary?: string\n status?: Status\n assignee?: string\n author?: string\n /** Everyone who wrote a page. */\n authors?: string[]\n comments?: number\n /**\n * The reader has something unread in its conversation: they are a member\n * and it moved since they last read it (SPEC \u00A711.8, membership-replaces-following).\n */\n unread?: boolean\n /** A project's issues, drawn as rows inside its detailed card. */\n children?: { title: string; key: string; status: Status; assignee?: string }[]\n /** A project's progress line. */\n progress?: string\n /** A project's progress as numbers, for the ring. */\n done?: { done: number; total: number }\n /** A topic: how many conversations, who is in it, and its latest one. */\n conversations?: number\n members?: string[]\n latest?: { author: string; body: string; at: string }\n updated?: string\n /** Where it is placed (WID-3): still loading, or not available to this reader. Ready when absent. */\n state?: 'loading' | 'unavailable'\n}\n\nconst TODO: Status = { label: 'Todo', colour: 'neutral' }\nconst DOING: Status = { label: 'In progress', colour: 'blue' }\nconst DONE: Status = { label: 'Done', colour: 'green' }\n\nconst PARAGRAPH =\n 'A description long enough to need more than one line, so the card has to choose how much of it to show and where to cut it short. The rest is there when you open it.'\n\nconst SUMMARY = 'A one-sentence summary of what this is for.'\n\nexport const ISSUE: Thing = {\n kind: 'issue',\n noun: 'Issue',\n app: 'Ship',\n title: 'Item one, with a title long enough to be cut short',\n key: 'ABC-2',\n subtitle: 'Group one',\n body: PARAGRAPH,\n summary: SUMMARY,\n status: DOING,\n assignee: 'Person One',\n comments: 4,\n unread: true,\n}\n\nexport const PROJECT: Thing = {\n kind: 'project',\n noun: 'Project',\n app: 'Ship',\n title: 'Group one',\n body: PARAGRAPH,\n summary: SUMMARY,\n status: DOING,\n assignee: 'Person Two',\n progress: '3 of 8 done',\n done: { done: 3, total: 8 },\n comments: 6,\n children: [\n { title: 'Item one', key: 'ABC-1', status: DONE, assignee: 'Person One' },\n { title: 'Item two', key: 'ABC-2', status: DOING, assignee: 'Person Two' },\n { title: 'Item three', key: 'ABC-3', status: TODO },\n ],\n}\n\nexport const TOPIC: Thing = {\n kind: 'topic',\n noun: 'Topic',\n app: 'Peek',\n title: 'Place one',\n conversations: 12,\n unread: true,\n members: ['Person One', 'Person Two', 'Person Three'],\n updated: '2h ago',\n latest: { author: 'Person Three', body: 'The latest conversation in the topic, as its first message reads. It may run to a second line before it is cut short.', at: '2h ago' },\n}\n\nexport const MESSAGE: Thing = {\n kind: 'message',\n noun: 'Message',\n app: 'Peek',\n title: 'Person Two',\n author: 'Person Two',\n body: 'A message of two sentences, written by someone in a conversation. It is the words the link points to.',\n updated: '10:42',\n}\n\nexport const PAGE: Thing = {\n kind: 'page',\n noun: 'Page',\n app: 'Leaf',\n title: 'Page one',\n subtitle: 'Folder one',\n body: PARAGRAPH,\n summary: SUMMARY,\n author: 'Person One',\n authors: ['Person One', 'Person Two'],\n comments: 2,\n updated: 'Edited yesterday',\n}\n\n/** A block of a description: drawn live, as the part it is. */\nexport const PART = {\n heading: 'Item one',\n noun: 'Issue',\n app: 'Ship',\n blocks: ['Heading of the part', 'A paragraph of the part, as it reads in the description right now.', 'A point in a list', 'Another point'],\n}\n\nexport const ATTACHMENT = { name: 'picture-one.png', size: 182_400, contentType: 'image/png' }\nexport const ATTACHMENT_FILE = { name: 'document-one.pdf', size: 1_240_000, contentType: 'application/pdf' }\n\n/** A file the reader cannot read: only its kind and app are known, from its address. */\nexport const UNREADABLE = { kind: 'issue' as const, noun: 'Issue', app: 'Ship', address: 'naddr1qqjxgdmzxy6r2vfs95cryvmp956x2etp94sns' }\n\n/** A small picture for the attachment, drawn rather than fetched. */\nexport const PICTURE =\n 'data:image/svg+xml;utf8,' +\n encodeURIComponent(\n '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"480\" height=\"300\"><rect width=\"480\" height=\"300\" fill=\"#8a94a6\"/><circle cx=\"360\" cy=\"90\" r=\"40\" fill=\"#d8dde6\"/><path d=\"M0 300 L170 120 L300 260 L380 190 L480 300 Z\" fill=\"#5d6778\"/></svg>',\n )\n", "/**\n * Where a link sits decides how it draws (Miky, 15 Oct, on the brief \"Where a\n * Link Sits\"; why: https://github.com/estiva-app/estiva-docs/blob/main/decisions/where-a-link-sits-decides-how-it-draws.md),\n * drawn once here so Peek, Ship and Leaf place the same widgets the same way\n * (WID-3). Each app's own stories put these inside its own frames.\n *\n * - A link in a sentence is a chip; a link alone on its line is a row.\n * - In a message: the one file it links gets its card under it, added by\n * default and removed with \u2715; a message that is only one link shows the card\n * instead of the row; two or more files, no card. No layout choice.\n * - In a document: no card is ever added. The chip menu on a lone-line link\n * offers Open and its card, and the card takes that line's place.\n */\nimport {\n IconCheck,\n IconExternalLink,\n IconH1,\n IconLetterT,\n IconLink,\n IconListCheck,\n IconPaperclip,\n IconTable,\n IconX,\n} from '@tabler/icons-react'\nimport { useState, type ReactNode } from 'react'\nimport { cn } from '../cn'\nimport { IconButton } from '../IconButton'\nimport { MenuItem, MenuPanel, MenuSection, MenuSeparator } from '../Menu'\nimport type { Thing } from './fixtures'\nimport { CardLoading, CardUnavailable, CompactCard, DetailedCard, loadingShapeOf, MessageCard, ReferenceChip, ThingIcon } from './proposed'\n\n/** A piece of a sentence: words, or a link to a file. */\nexport type Piece = string | Thing\n/** One line of text: a link alone on its line, or a sentence of words and links. */\nexport type Line = Thing | Piece[]\n\nconst isLink = (piece: Piece | Line): piece is Thing => typeof piece === 'object' && !Array.isArray(piece)\n\n/** Every file the lines link to, each once. */\nexport function linksIn(lines: Line[]): Thing[] {\n return [...new Set(lines.flatMap((line) => (isLink(line) ? [line] : line.filter(isLink))))]\n}\n\n/** The file whose card goes under a message: the one it links. None when it links two or more. */\nexport function cardFileOf(lines: Line[]): Thing | undefined {\n const files = linksIn(lines)\n return files.length === 1 ? files[0] : undefined\n}\n\n/** A message that is nothing but one link: it shows the card, not the row. */\nexport function isOnlyOneLink(lines: Line[]): boolean {\n return lines.length === 1 && isLink(lines[0])\n}\n\n/** A file's card: a linked message draws as a message, every other file as its detailed card; loading and not available as the proposed kit draws them. */\nexport function CardOf({ thing }: { thing: Thing }) {\n if (thing.state === 'loading') return <CardLoading kind={thing.kind} layout=\"detailed\" shape={loadingShapeOf(thing)} />\n if (thing.state === 'unavailable') return <CardUnavailable kind={thing.kind} noun={thing.noun} app={thing.app} />\n return thing.kind === 'message' ? <MessageCard thing={thing} /> : <DetailedCard thing={thing} />\n}\n\n/** A link alone on its line: its row, or loading, or not available. */\nfunction Row({ thing, preview = false }: { thing: Thing; preview?: boolean }) {\n if (thing.state === 'loading') return <CardLoading kind={thing.kind} layout=\"compact\" />\n if (thing.state === 'unavailable') return <CardUnavailable kind={thing.kind} noun={thing.noun} app={thing.app} />\n return <CompactCard thing={thing} preview={preview} />\n}\n\n/** A sentence: its words, and a chip for each link in it. */\nfunction Sentence({ pieces }: { pieces: Piece[] }) {\n return (\n <p className=\"text-body-2 text-text-primary\">\n {pieces.map((piece, i) => (typeof piece === 'string' ? <span key={i}>{piece}</span> : <ReferenceChip key={i} thing={piece} state={piece.state} preview={!piece.state} />))}\n </p>\n )\n}\n\nexport interface PlacedLinesProps {\n lines: Line[]\n /** In a document: the lone-line links switched to their card, by line index. */\n cards?: number[]\n /** In a document: a click on a lone-line link, which opens its chip menu. */\n onLink?: (index: number) => void\n /** Drawn under a line, by line index: the chip menu, open. */\n under?: (index: number) => ReactNode\n className?: string\n}\n\n/** Text whose links draw by where they sit: a chip in a sentence, a row alone on its line, or (in a document) the card chosen for that line. */\nexport function PlacedLines({ lines, cards = [], onLink, under, className }: PlacedLinesProps) {\n return (\n <div className={cn('flex flex-col gap-2', className)}>\n {lines.map((line, i) => (\n <div key={i} className=\"relative flex flex-col\">\n {isLink(line) ? (\n onLink ? (\n // While writing, a click on the link opens its menu instead of following it (the record clicking-a-chip-while-editing-opens-a-menu).\n <div\n role=\"button\"\n tabIndex={0}\n aria-haspopup=\"menu\"\n className=\"w-full min-w-0 cursor-pointer\"\n onClickCapture={(event) => {\n event.preventDefault()\n event.stopPropagation()\n onLink(i)\n }}\n onKeyDown={(event) => {\n if (event.key === 'Enter') onLink(i)\n }}\n >\n {cards.includes(i) ? <CardOf thing={line} /> : <Row thing={line} />}\n </div>\n ) : cards.includes(i) ? (\n <CardOf thing={line} />\n ) : (\n <Row thing={line} preview />\n )\n ) : (\n <Sentence pieces={line} />\n )}\n {under?.(i)}\n </div>\n ))}\n </div>\n )\n}\n\nexport interface CardUnderProps {\n thing: Thing\n /** The \u2715 that removes it: under the message box before sending, or the author's own on a sent message. */\n onRemove?: () => void\n /** On a sent message the \u2715 shows on hover; under the box it always shows. */\n removeOnHover?: boolean\n className?: string\n}\n\n/** The one file's card under a message, and the \u2715 that removes it. */\nexport function CardUnder({ thing, onRemove, removeOnHover = false, className }: CardUnderProps) {\n return (\n <div className={cn('group/card flex items-start gap-1', className)}>\n <div className=\"min-w-0 flex-1\">\n <CardOf thing={thing} />\n </div>\n {onRemove && (\n // At the card's top corner, not its middle: a tall card's \u2715 halfway down reads as belonging to nothing.\n <div className=\"flex shrink-0 self-start pt-2\">\n <IconButton aria-label=\"Remove card\" tooltip=\"Remove card\" onClick={onRemove} className={cn(removeOnHover && 'opacity-0 group-hover/card:opacity-100 focus-visible:opacity-100')}>\n <IconX size={16} />\n </IconButton>\n </div>\n )}\n </div>\n )\n}\n\nexport interface PlacedMessageProps {\n lines: Line[]\n /** The card was removed with \u2715: before sending, or by the author afterwards. */\n removed?: boolean\n /** Draws the \u2715; without it the card shows with none, as to anyone but the author. */\n onRemove?: () => void\n removeOnHover?: boolean\n /**\n * An old message, sent before Widgets 2 with a `compact` tag for each of\n * these: no card was added to it, and each still draws as a row under it.\n */\n oldCompact?: Thing[]\n className?: string\n}\n\n/**\n * A message's text, and under it the one file's card. A file with its card\n * under the message draws no row in it (a chip in a sentence still draws), so\n * a message that is only one link shows its card alone.\n */\nexport function PlacedMessage({ lines, removed = false, onRemove, removeOnHover, oldCompact, className }: PlacedMessageProps) {\n const file = removed || oldCompact ? undefined : cardFileOf(lines)\n const shown = file ? lines.filter((line) => line !== file) : lines\n return (\n <div className={cn('flex flex-col gap-2', className)}>\n {shown.length > 0 && <PlacedLines lines={shown} />}\n {file && <CardUnder thing={file} onRemove={onRemove} removeOnHover={removeOnHover} className=\"max-w-[480px]\" />}\n {oldCompact?.map((thing, i) => (\n <CompactCard key={i} thing={thing} preview />\n ))}\n </div>\n )\n}\n\n/** A sent message whose author can remove its card with \u2715, live: try it. */\nexport function SentMessage({ lines, author = true, className }: { lines: Line[]; author?: boolean; className?: string }) {\n const [removed, setRemoved] = useState(false)\n return <PlacedMessage lines={lines} removed={removed} onRemove={author ? () => setRemoved(true) : undefined} removeOnHover className={className} />\n}\n\nexport interface ChipMenuProps {\n thing: Thing\n /**\n * A lone-line link offers its card; a chip in a sentence offers Open only,\n * and so does a link to what has no card worth choosing (a Folder, a file\n * that is not a picture).\n */\n alone?: boolean\n shownAs?: 'row' | 'card'\n onShowAs?: (as: 'row' | 'card') => void\n onOpen?: () => void\n}\n\n/** What a link in a document offers its writer: Open, and on a lone-line link of a kind that has one, its card in that line's place. */\nexport function ChipMenu({ thing, alone = true, shownAs = 'row', onShowAs, onOpen }: ChipMenuProps) {\n return (\n <MenuPanel className=\"w-[200px]\">\n <div role=\"menu\" aria-label=\"Link\" className=\"flex flex-col gap-0.5\">\n <MenuItem role=\"menuitem\" label=\"Open\" leading={<IconExternalLink size={16} />} onClick={onOpen} />\n {alone && (\n <>\n <MenuSeparator />\n <div role=\"group\" aria-label=\"Show as\">\n <MenuSection label=\"Show as\">\n {(['row', 'card'] as const).map((as) => (\n <MenuItem\n key={as}\n role=\"menuitemradio\"\n aria-checked={shownAs === as}\n label={as === 'row' ? 'Row' : 'Card'}\n trailing={shownAs === as ? <IconCheck size={16} aria-hidden /> : undefined}\n onClick={() => onShowAs?.(as)}\n />\n ))}\n </MenuSection>\n </div>\n </>\n )}\n </div>\n </MenuPanel>\n )\n}\n\n/** A document's text with its chip menu, live: click a lone-line link, then choose Row or Card. */\nexport function PlacedDocument({ lines, initialCards = [], className }: { lines: Line[]; initialCards?: number[]; className?: string }) {\n const [cards, setCards] = useState(initialCards)\n const [open, setOpen] = useState<number | null>(null)\n return (\n <PlacedLines\n lines={lines}\n cards={cards}\n className={className}\n onLink={(i) => setOpen(open === i ? null : i)}\n under={(i) => {\n const line = lines[i]\n if (open !== i || !isLink(line)) return null\n return (\n <div className=\"absolute left-0 top-full z-10 mt-1\">\n <ChipMenu\n thing={line}\n shownAs={cards.includes(i) ? 'card' : 'row'}\n onOpen={() => setOpen(null)}\n onShowAs={(as) => {\n setCards(as === 'card' ? [...cards, i] : cards.filter((c) => c !== i))\n setOpen(null)\n }}\n />\n </div>\n )\n }}\n />\n )\n}\n\n/**\n * A description's and a page's `/` menu with the widget entry (Miky's QA note,\n * 15 Oct: \"`/` section where you can use `[` to insert a widget\"). Format rows\n * shortened to three; Insert as the app has it, plus the link.\n */\nexport function SlashMenuWithLink({ file = true, table = true }: { file?: boolean; table?: boolean }) {\n return (\n <MenuPanel className=\"w-[260px]\">\n <div role=\"menu\" aria-label=\"Insert\" className=\"flex flex-col gap-0.5\">\n <MenuSection label=\"Format\">\n <MenuItem label=\"Text\" leading={<IconLetterT size={16} />} />\n <MenuItem label=\"Heading\" leading={<IconH1 size={16} />} shortcut=\"#\" />\n <MenuItem label=\"Checklist\" leading={<IconListCheck size={16} />} shortcut=\"[]\" />\n </MenuSection>\n <MenuSeparator />\n <MenuSection label=\"Insert\">\n {table && <MenuItem label=\"Table\" leading={<IconTable size={16} />} />}\n {file && <MenuItem label=\"File\" leading={<IconPaperclip size={16} />} />}\n <MenuItem label=\"Link to a file\" leading={<IconLink size={16} />} shortcut=\"[\" selected />\n </MenuSection>\n </div>\n </MenuPanel>\n )\n}\n\n/** The `[` list the widget entry opens: the files to link, each led by its kind's icon. */\nexport function LinkPicker({ files, query = '' }: { files: Thing[]; query?: string }) {\n return (\n <MenuPanel className=\"w-[320px]\">\n <div role=\"listbox\" aria-label=\"Link to a file\" className=\"flex flex-col gap-0.5\">\n <MenuSection label={query ? `Files matching \u201C${query}\u201D` : 'Recent files'}>\n {files.map((thing, i) => (\n <MenuItem key={i} role=\"option\" label={thing.kind === 'message' ? `${thing.author}: ${thing.body}` : thing.title} leading={<ThingIcon kind={thing.kind} />} hint={thing.key && <span className=\"text-caption text-text-secondary\">{thing.key}</span>} selected={i === 0} />\n ))}\n </MenuSection>\n </div>\n </MenuPanel>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["IconPaperclip", "cn", "MenuPanel", "Fragment", "jsx", "jsxs", "cn", "MenuPanel", "IconPaperclip"]
|
|
7
7
|
}
|
package/package.json
CHANGED
package/registry.json
CHANGED
|
@@ -56,6 +56,8 @@ export interface Thing {
|
|
|
56
56
|
members?: string[]
|
|
57
57
|
latest?: { author: string; body: string; at: string }
|
|
58
58
|
updated?: string
|
|
59
|
+
/** Where it is placed (WID-3): still loading, or not available to this reader. Ready when absent. */
|
|
60
|
+
state?: 'loading' | 'unavailable'
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
const TODO: Status = { label: 'Todo', colour: 'neutral' }
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a link sits decides how it draws (Miky, 15 Oct, on the brief "Where a
|
|
3
|
+
* Link Sits"; why: https://github.com/estiva-app/estiva-docs/blob/main/decisions/where-a-link-sits-decides-how-it-draws.md),
|
|
4
|
+
* drawn once here so Peek, Ship and Leaf place the same widgets the same way
|
|
5
|
+
* (WID-3). Each app's own stories put these inside its own frames.
|
|
6
|
+
*
|
|
7
|
+
* - A link in a sentence is a chip; a link alone on its line is a row.
|
|
8
|
+
* - In a message: the one file it links gets its card under it, added by
|
|
9
|
+
* default and removed with ✕; a message that is only one link shows the card
|
|
10
|
+
* instead of the row; two or more files, no card. No layout choice.
|
|
11
|
+
* - In a document: no card is ever added. The chip menu on a lone-line link
|
|
12
|
+
* offers Open and its card, and the card takes that line's place.
|
|
13
|
+
*/
|
|
14
|
+
import {
|
|
15
|
+
IconCheck,
|
|
16
|
+
IconExternalLink,
|
|
17
|
+
IconH1,
|
|
18
|
+
IconLetterT,
|
|
19
|
+
IconLink,
|
|
20
|
+
IconListCheck,
|
|
21
|
+
IconPaperclip,
|
|
22
|
+
IconTable,
|
|
23
|
+
IconX,
|
|
24
|
+
} from '@tabler/icons-react'
|
|
25
|
+
import { useState, type ReactNode } from 'react'
|
|
26
|
+
import { cn } from '../cn'
|
|
27
|
+
import { IconButton } from '../IconButton'
|
|
28
|
+
import { MenuItem, MenuPanel, MenuSection, MenuSeparator } from '../Menu'
|
|
29
|
+
import type { Thing } from './fixtures'
|
|
30
|
+
import { CardLoading, CardUnavailable, CompactCard, DetailedCard, loadingShapeOf, MessageCard, ReferenceChip, ThingIcon } from './proposed'
|
|
31
|
+
|
|
32
|
+
/** A piece of a sentence: words, or a link to a file. */
|
|
33
|
+
export type Piece = string | Thing
|
|
34
|
+
/** One line of text: a link alone on its line, or a sentence of words and links. */
|
|
35
|
+
export type Line = Thing | Piece[]
|
|
36
|
+
|
|
37
|
+
const isLink = (piece: Piece | Line): piece is Thing => typeof piece === 'object' && !Array.isArray(piece)
|
|
38
|
+
|
|
39
|
+
/** Every file the lines link to, each once. */
|
|
40
|
+
export function linksIn(lines: Line[]): Thing[] {
|
|
41
|
+
return [...new Set(lines.flatMap((line) => (isLink(line) ? [line] : line.filter(isLink))))]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** The file whose card goes under a message: the one it links. None when it links two or more. */
|
|
45
|
+
export function cardFileOf(lines: Line[]): Thing | undefined {
|
|
46
|
+
const files = linksIn(lines)
|
|
47
|
+
return files.length === 1 ? files[0] : undefined
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** A message that is nothing but one link: it shows the card, not the row. */
|
|
51
|
+
export function isOnlyOneLink(lines: Line[]): boolean {
|
|
52
|
+
return lines.length === 1 && isLink(lines[0])
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** A file's card: a linked message draws as a message, every other file as its detailed card; loading and not available as the proposed kit draws them. */
|
|
56
|
+
export function CardOf({ thing }: { thing: Thing }) {
|
|
57
|
+
if (thing.state === 'loading') return <CardLoading kind={thing.kind} layout="detailed" shape={loadingShapeOf(thing)} />
|
|
58
|
+
if (thing.state === 'unavailable') return <CardUnavailable kind={thing.kind} noun={thing.noun} app={thing.app} />
|
|
59
|
+
return thing.kind === 'message' ? <MessageCard thing={thing} /> : <DetailedCard thing={thing} />
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** A link alone on its line: its row, or loading, or not available. */
|
|
63
|
+
function Row({ thing, preview = false }: { thing: Thing; preview?: boolean }) {
|
|
64
|
+
if (thing.state === 'loading') return <CardLoading kind={thing.kind} layout="compact" />
|
|
65
|
+
if (thing.state === 'unavailable') return <CardUnavailable kind={thing.kind} noun={thing.noun} app={thing.app} />
|
|
66
|
+
return <CompactCard thing={thing} preview={preview} />
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** A sentence: its words, and a chip for each link in it. */
|
|
70
|
+
function Sentence({ pieces }: { pieces: Piece[] }) {
|
|
71
|
+
return (
|
|
72
|
+
<p className="text-body-2 text-text-primary">
|
|
73
|
+
{pieces.map((piece, i) => (typeof piece === 'string' ? <span key={i}>{piece}</span> : <ReferenceChip key={i} thing={piece} state={piece.state} preview={!piece.state} />))}
|
|
74
|
+
</p>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface PlacedLinesProps {
|
|
79
|
+
lines: Line[]
|
|
80
|
+
/** In a document: the lone-line links switched to their card, by line index. */
|
|
81
|
+
cards?: number[]
|
|
82
|
+
/** In a document: a click on a lone-line link, which opens its chip menu. */
|
|
83
|
+
onLink?: (index: number) => void
|
|
84
|
+
/** Drawn under a line, by line index: the chip menu, open. */
|
|
85
|
+
under?: (index: number) => ReactNode
|
|
86
|
+
className?: string
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Text whose links draw by where they sit: a chip in a sentence, a row alone on its line, or (in a document) the card chosen for that line. */
|
|
90
|
+
export function PlacedLines({ lines, cards = [], onLink, under, className }: PlacedLinesProps) {
|
|
91
|
+
return (
|
|
92
|
+
<div className={cn('flex flex-col gap-2', className)}>
|
|
93
|
+
{lines.map((line, i) => (
|
|
94
|
+
<div key={i} className="relative flex flex-col">
|
|
95
|
+
{isLink(line) ? (
|
|
96
|
+
onLink ? (
|
|
97
|
+
// While writing, a click on the link opens its menu instead of following it (the record clicking-a-chip-while-editing-opens-a-menu).
|
|
98
|
+
<div
|
|
99
|
+
role="button"
|
|
100
|
+
tabIndex={0}
|
|
101
|
+
aria-haspopup="menu"
|
|
102
|
+
className="w-full min-w-0 cursor-pointer"
|
|
103
|
+
onClickCapture={(event) => {
|
|
104
|
+
event.preventDefault()
|
|
105
|
+
event.stopPropagation()
|
|
106
|
+
onLink(i)
|
|
107
|
+
}}
|
|
108
|
+
onKeyDown={(event) => {
|
|
109
|
+
if (event.key === 'Enter') onLink(i)
|
|
110
|
+
}}
|
|
111
|
+
>
|
|
112
|
+
{cards.includes(i) ? <CardOf thing={line} /> : <Row thing={line} />}
|
|
113
|
+
</div>
|
|
114
|
+
) : cards.includes(i) ? (
|
|
115
|
+
<CardOf thing={line} />
|
|
116
|
+
) : (
|
|
117
|
+
<Row thing={line} preview />
|
|
118
|
+
)
|
|
119
|
+
) : (
|
|
120
|
+
<Sentence pieces={line} />
|
|
121
|
+
)}
|
|
122
|
+
{under?.(i)}
|
|
123
|
+
</div>
|
|
124
|
+
))}
|
|
125
|
+
</div>
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface CardUnderProps {
|
|
130
|
+
thing: Thing
|
|
131
|
+
/** The ✕ that removes it: under the message box before sending, or the author's own on a sent message. */
|
|
132
|
+
onRemove?: () => void
|
|
133
|
+
/** On a sent message the ✕ shows on hover; under the box it always shows. */
|
|
134
|
+
removeOnHover?: boolean
|
|
135
|
+
className?: string
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** The one file's card under a message, and the ✕ that removes it. */
|
|
139
|
+
export function CardUnder({ thing, onRemove, removeOnHover = false, className }: CardUnderProps) {
|
|
140
|
+
return (
|
|
141
|
+
<div className={cn('group/card flex items-start gap-1', className)}>
|
|
142
|
+
<div className="min-w-0 flex-1">
|
|
143
|
+
<CardOf thing={thing} />
|
|
144
|
+
</div>
|
|
145
|
+
{onRemove && (
|
|
146
|
+
// At the card's top corner, not its middle: a tall card's ✕ halfway down reads as belonging to nothing.
|
|
147
|
+
<div className="flex shrink-0 self-start pt-2">
|
|
148
|
+
<IconButton aria-label="Remove card" tooltip="Remove card" onClick={onRemove} className={cn(removeOnHover && 'opacity-0 group-hover/card:opacity-100 focus-visible:opacity-100')}>
|
|
149
|
+
<IconX size={16} />
|
|
150
|
+
</IconButton>
|
|
151
|
+
</div>
|
|
152
|
+
)}
|
|
153
|
+
</div>
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface PlacedMessageProps {
|
|
158
|
+
lines: Line[]
|
|
159
|
+
/** The card was removed with ✕: before sending, or by the author afterwards. */
|
|
160
|
+
removed?: boolean
|
|
161
|
+
/** Draws the ✕; without it the card shows with none, as to anyone but the author. */
|
|
162
|
+
onRemove?: () => void
|
|
163
|
+
removeOnHover?: boolean
|
|
164
|
+
/**
|
|
165
|
+
* An old message, sent before Widgets 2 with a `compact` tag for each of
|
|
166
|
+
* these: no card was added to it, and each still draws as a row under it.
|
|
167
|
+
*/
|
|
168
|
+
oldCompact?: Thing[]
|
|
169
|
+
className?: string
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* A message's text, and under it the one file's card. A file with its card
|
|
174
|
+
* under the message draws no row in it (a chip in a sentence still draws), so
|
|
175
|
+
* a message that is only one link shows its card alone.
|
|
176
|
+
*/
|
|
177
|
+
export function PlacedMessage({ lines, removed = false, onRemove, removeOnHover, oldCompact, className }: PlacedMessageProps) {
|
|
178
|
+
const file = removed || oldCompact ? undefined : cardFileOf(lines)
|
|
179
|
+
const shown = file ? lines.filter((line) => line !== file) : lines
|
|
180
|
+
return (
|
|
181
|
+
<div className={cn('flex flex-col gap-2', className)}>
|
|
182
|
+
{shown.length > 0 && <PlacedLines lines={shown} />}
|
|
183
|
+
{file && <CardUnder thing={file} onRemove={onRemove} removeOnHover={removeOnHover} className="max-w-[480px]" />}
|
|
184
|
+
{oldCompact?.map((thing, i) => (
|
|
185
|
+
<CompactCard key={i} thing={thing} preview />
|
|
186
|
+
))}
|
|
187
|
+
</div>
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** A sent message whose author can remove its card with ✕, live: try it. */
|
|
192
|
+
export function SentMessage({ lines, author = true, className }: { lines: Line[]; author?: boolean; className?: string }) {
|
|
193
|
+
const [removed, setRemoved] = useState(false)
|
|
194
|
+
return <PlacedMessage lines={lines} removed={removed} onRemove={author ? () => setRemoved(true) : undefined} removeOnHover className={className} />
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export interface ChipMenuProps {
|
|
198
|
+
thing: Thing
|
|
199
|
+
/**
|
|
200
|
+
* A lone-line link offers its card; a chip in a sentence offers Open only,
|
|
201
|
+
* and so does a link to what has no card worth choosing (a Folder, a file
|
|
202
|
+
* that is not a picture).
|
|
203
|
+
*/
|
|
204
|
+
alone?: boolean
|
|
205
|
+
shownAs?: 'row' | 'card'
|
|
206
|
+
onShowAs?: (as: 'row' | 'card') => void
|
|
207
|
+
onOpen?: () => void
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** What a link in a document offers its writer: Open, and on a lone-line link of a kind that has one, its card in that line's place. */
|
|
211
|
+
export function ChipMenu({ thing, alone = true, shownAs = 'row', onShowAs, onOpen }: ChipMenuProps) {
|
|
212
|
+
return (
|
|
213
|
+
<MenuPanel className="w-[200px]">
|
|
214
|
+
<div role="menu" aria-label="Link" className="flex flex-col gap-0.5">
|
|
215
|
+
<MenuItem role="menuitem" label="Open" leading={<IconExternalLink size={16} />} onClick={onOpen} />
|
|
216
|
+
{alone && (
|
|
217
|
+
<>
|
|
218
|
+
<MenuSeparator />
|
|
219
|
+
<div role="group" aria-label="Show as">
|
|
220
|
+
<MenuSection label="Show as">
|
|
221
|
+
{(['row', 'card'] as const).map((as) => (
|
|
222
|
+
<MenuItem
|
|
223
|
+
key={as}
|
|
224
|
+
role="menuitemradio"
|
|
225
|
+
aria-checked={shownAs === as}
|
|
226
|
+
label={as === 'row' ? 'Row' : 'Card'}
|
|
227
|
+
trailing={shownAs === as ? <IconCheck size={16} aria-hidden /> : undefined}
|
|
228
|
+
onClick={() => onShowAs?.(as)}
|
|
229
|
+
/>
|
|
230
|
+
))}
|
|
231
|
+
</MenuSection>
|
|
232
|
+
</div>
|
|
233
|
+
</>
|
|
234
|
+
)}
|
|
235
|
+
</div>
|
|
236
|
+
</MenuPanel>
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** A document's text with its chip menu, live: click a lone-line link, then choose Row or Card. */
|
|
241
|
+
export function PlacedDocument({ lines, initialCards = [], className }: { lines: Line[]; initialCards?: number[]; className?: string }) {
|
|
242
|
+
const [cards, setCards] = useState(initialCards)
|
|
243
|
+
const [open, setOpen] = useState<number | null>(null)
|
|
244
|
+
return (
|
|
245
|
+
<PlacedLines
|
|
246
|
+
lines={lines}
|
|
247
|
+
cards={cards}
|
|
248
|
+
className={className}
|
|
249
|
+
onLink={(i) => setOpen(open === i ? null : i)}
|
|
250
|
+
under={(i) => {
|
|
251
|
+
const line = lines[i]
|
|
252
|
+
if (open !== i || !isLink(line)) return null
|
|
253
|
+
return (
|
|
254
|
+
<div className="absolute left-0 top-full z-10 mt-1">
|
|
255
|
+
<ChipMenu
|
|
256
|
+
thing={line}
|
|
257
|
+
shownAs={cards.includes(i) ? 'card' : 'row'}
|
|
258
|
+
onOpen={() => setOpen(null)}
|
|
259
|
+
onShowAs={(as) => {
|
|
260
|
+
setCards(as === 'card' ? [...cards, i] : cards.filter((c) => c !== i))
|
|
261
|
+
setOpen(null)
|
|
262
|
+
}}
|
|
263
|
+
/>
|
|
264
|
+
</div>
|
|
265
|
+
)
|
|
266
|
+
}}
|
|
267
|
+
/>
|
|
268
|
+
)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* A description's and a page's `/` menu with the widget entry (Miky's QA note,
|
|
273
|
+
* 15 Oct: "`/` section where you can use `[` to insert a widget"). Format rows
|
|
274
|
+
* shortened to three; Insert as the app has it, plus the link.
|
|
275
|
+
*/
|
|
276
|
+
export function SlashMenuWithLink({ file = true, table = true }: { file?: boolean; table?: boolean }) {
|
|
277
|
+
return (
|
|
278
|
+
<MenuPanel className="w-[260px]">
|
|
279
|
+
<div role="menu" aria-label="Insert" className="flex flex-col gap-0.5">
|
|
280
|
+
<MenuSection label="Format">
|
|
281
|
+
<MenuItem label="Text" leading={<IconLetterT size={16} />} />
|
|
282
|
+
<MenuItem label="Heading" leading={<IconH1 size={16} />} shortcut="#" />
|
|
283
|
+
<MenuItem label="Checklist" leading={<IconListCheck size={16} />} shortcut="[]" />
|
|
284
|
+
</MenuSection>
|
|
285
|
+
<MenuSeparator />
|
|
286
|
+
<MenuSection label="Insert">
|
|
287
|
+
{table && <MenuItem label="Table" leading={<IconTable size={16} />} />}
|
|
288
|
+
{file && <MenuItem label="File" leading={<IconPaperclip size={16} />} />}
|
|
289
|
+
<MenuItem label="Link to a file" leading={<IconLink size={16} />} shortcut="[" selected />
|
|
290
|
+
</MenuSection>
|
|
291
|
+
</div>
|
|
292
|
+
</MenuPanel>
|
|
293
|
+
)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** The `[` list the widget entry opens: the files to link, each led by its kind's icon. */
|
|
297
|
+
export function LinkPicker({ files, query = '' }: { files: Thing[]; query?: string }) {
|
|
298
|
+
return (
|
|
299
|
+
<MenuPanel className="w-[320px]">
|
|
300
|
+
<div role="listbox" aria-label="Link to a file" className="flex flex-col gap-0.5">
|
|
301
|
+
<MenuSection label={query ? `Files matching “${query}”` : 'Recent files'}>
|
|
302
|
+
{files.map((thing, i) => (
|
|
303
|
+
<MenuItem key={i} role="option" label={thing.kind === 'message' ? `${thing.author}: ${thing.body}` : thing.title} leading={<ThingIcon kind={thing.kind} />} hint={thing.key && <span className="text-caption text-text-secondary">{thing.key}</span>} selected={i === 0} />
|
|
304
|
+
))}
|
|
305
|
+
</MenuSection>
|
|
306
|
+
</div>
|
|
307
|
+
</MenuPanel>
|
|
308
|
+
)
|
|
309
|
+
}
|
package/src/widgets-2.ts
CHANGED