@maple-kit/ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +9 -0
  3. package/README.md +39 -0
  4. package/dist/composer/actions.d.ts +15 -0
  5. package/dist/composer/actions.js +59 -0
  6. package/dist/composer/attachments.d.ts +33 -0
  7. package/dist/composer/attachments.js +166 -0
  8. package/dist/composer/badge.d.ts +13 -0
  9. package/dist/composer/badge.js +25 -0
  10. package/dist/composer/body.d.ts +13 -0
  11. package/dist/composer/body.js +119 -0
  12. package/dist/composer/composer.d.ts +23 -0
  13. package/dist/composer/composer.js +122 -0
  14. package/dist/composer/css.js +559 -0
  15. package/dist/composer/detail.d.ts +32 -0
  16. package/dist/composer/detail.js +102 -0
  17. package/dist/composer/emoji.d.ts +54 -0
  18. package/dist/composer/emoji.js +131 -0
  19. package/dist/composer/index.d.ts +15 -0
  20. package/dist/composer/index.js +15 -0
  21. package/dist/composer/inset.js +20 -0
  22. package/dist/composer/leave.d.ts +19 -0
  23. package/dist/composer/leave.js +34 -0
  24. package/dist/composer/peek.d.ts +21 -0
  25. package/dist/composer/peek.js +29 -0
  26. package/dist/composer/phrase.d.ts +27 -0
  27. package/dist/composer/phrase.js +30 -0
  28. package/dist/composer/picker-emoji.d.ts +34 -0
  29. package/dist/composer/picker-emoji.js +48 -0
  30. package/dist/composer/prompt.js +31 -0
  31. package/dist/composer/scope.d.ts +11 -0
  32. package/dist/composer/scope.js +16 -0
  33. package/dist/composer/target.d.ts +13 -0
  34. package/dist/composer/target.js +45 -0
  35. package/dist/context.d.ts +21 -0
  36. package/dist/context.js +16 -0
  37. package/dist/data.d.ts +52 -0
  38. package/dist/data.js +30 -0
  39. package/dist/icons/branch.d.ts +4 -0
  40. package/dist/icons/branch.js +8 -0
  41. package/dist/icons/cog.d.ts +4 -0
  42. package/dist/icons/cog.js +8 -0
  43. package/dist/icons/commit.d.ts +4 -0
  44. package/dist/icons/commit.js +5 -0
  45. package/dist/icons/crossfade.d.ts +15 -0
  46. package/dist/icons/crossfade.js +27 -0
  47. package/dist/icons/element.d.ts +4 -0
  48. package/dist/icons/element.js +5 -0
  49. package/dist/icons/icon.d.ts +24 -0
  50. package/dist/icons/icon.js +27 -0
  51. package/dist/icons/index.d.ts +13 -0
  52. package/dist/icons/index.js +13 -0
  53. package/dist/icons/noplace.d.ts +4 -0
  54. package/dist/icons/noplace.js +5 -0
  55. package/dist/icons/region.d.ts +4 -0
  56. package/dist/icons/region.js +8 -0
  57. package/dist/icons/smiley.d.ts +4 -0
  58. package/dist/icons/smiley.js +8 -0
  59. package/dist/icons/sparkle.d.ts +4 -0
  60. package/dist/icons/sparkle.js +8 -0
  61. package/dist/icons/target.d.ts +4 -0
  62. package/dist/icons/target.js +5 -0
  63. package/dist/icons/text.d.ts +4 -0
  64. package/dist/icons/text.js +5 -0
  65. package/dist/index.d.ts +11 -0
  66. package/dist/index.js +11 -0
  67. package/dist/island/account.d.ts +10 -0
  68. package/dist/island/account.js +52 -0
  69. package/dist/island/comments.d.ts +24 -0
  70. package/dist/island/comments.js +40 -0
  71. package/dist/island/content.d.ts +10 -0
  72. package/dist/island/content.js +26 -0
  73. package/dist/island/context.d.ts +58 -0
  74. package/dist/island/context.js +23 -0
  75. package/dist/island/css.d.ts +13 -0
  76. package/dist/island/css.js +1020 -0
  77. package/dist/island/drag.d.ts +10 -0
  78. package/dist/island/drag.js +77 -0
  79. package/dist/island/filters.d.ts +10 -0
  80. package/dist/island/filters.js +63 -0
  81. package/dist/island/header.d.ts +32 -0
  82. package/dist/island/header.js +47 -0
  83. package/dist/island/index.d.ts +18 -0
  84. package/dist/island/index.js +18 -0
  85. package/dist/island/island.d.ts +12 -0
  86. package/dist/island/island.js +111 -0
  87. package/dist/island/item.d.ts +12 -0
  88. package/dist/island/item.js +158 -0
  89. package/dist/island/language.d.ts +55 -0
  90. package/dist/island/language.js +125 -0
  91. package/dist/island/leaf.js +17 -0
  92. package/dist/island/list.d.ts +12 -0
  93. package/dist/island/list.js +31 -0
  94. package/dist/island/new-comment.d.ts +18 -0
  95. package/dist/island/new-comment.js +56 -0
  96. package/dist/island/settings.d.ts +10 -0
  97. package/dist/island/settings.js +134 -0
  98. package/dist/island/stagger.d.ts +12 -0
  99. package/dist/island/stagger.js +4 -0
  100. package/dist/island/time.d.ts +17 -0
  101. package/dist/island/time.js +29 -0
  102. package/dist/island/trigger.d.ts +10 -0
  103. package/dist/island/trigger.js +39 -0
  104. package/dist/language.d.ts +45 -0
  105. package/dist/language.js +66 -0
  106. package/dist/maple.d.ts +25 -0
  107. package/dist/maple.js +65 -0
  108. package/dist/marks/avatar.d.ts +16 -0
  109. package/dist/marks/avatar.js +36 -0
  110. package/dist/marks/css.d.ts +12 -0
  111. package/dist/marks/css.js +371 -0
  112. package/dist/marks/frame.d.ts +25 -0
  113. package/dist/marks/frame.js +53 -0
  114. package/dist/marks/geometry.d.ts +55 -0
  115. package/dist/marks/geometry.js +61 -0
  116. package/dist/marks/index.d.ts +14 -0
  117. package/dist/marks/index.js +14 -0
  118. package/dist/marks/label.d.ts +23 -0
  119. package/dist/marks/label.js +31 -0
  120. package/dist/marks/layer.d.ts +16 -0
  121. package/dist/marks/layer.js +194 -0
  122. package/dist/marks/leaf.d.ts +18 -0
  123. package/dist/marks/leaf.js +7 -0
  124. package/dist/marks/mark.d.ts +28 -0
  125. package/dist/marks/mark.js +43 -0
  126. package/dist/marks/nudge.d.ts +50 -0
  127. package/dist/marks/nudge.js +76 -0
  128. package/dist/marks/paint.d.ts +11 -0
  129. package/dist/marks/paint.js +19 -0
  130. package/dist/marks/placement.d.ts +24 -0
  131. package/dist/marks/placement.js +28 -0
  132. package/dist/marks/ring.d.ts +28 -0
  133. package/dist/marks/ring.js +85 -0
  134. package/dist/marks/shape.d.ts +21 -0
  135. package/dist/marks/shape.js +51 -0
  136. package/dist/notice/css.d.ts +11 -0
  137. package/dist/notice/css.js +74 -0
  138. package/dist/notice/index.d.ts +4 -0
  139. package/dist/notice/index.js +4 -0
  140. package/dist/notice/language.d.ts +15 -0
  141. package/dist/notice/language.js +14 -0
  142. package/dist/notice/notice.d.ts +14 -0
  143. package/dist/notice/notice.js +49 -0
  144. package/dist/part.d.ts +9 -0
  145. package/dist/part.js +11 -0
  146. package/dist/picker/css.d.ts +13 -0
  147. package/dist/picker/css.js +141 -0
  148. package/dist/picker/index.d.ts +6 -0
  149. package/dist/picker/index.js +6 -0
  150. package/dist/picker/language.d.ts +14 -0
  151. package/dist/picker/language.js +17 -0
  152. package/dist/picker/picker.d.ts +18 -0
  153. package/dist/picker/picker.js +164 -0
  154. package/dist/picker/session.d.ts +16 -0
  155. package/dist/picker/session.js +35 -0
  156. package/dist/picker/target.d.ts +23 -0
  157. package/dist/picker/target.js +37 -0
  158. package/dist/root.d.ts +42 -0
  159. package/dist/root.js +75 -0
  160. package/dist/shots.js +32 -0
  161. package/dist/slot.d.ts +28 -0
  162. package/dist/slot.js +52 -0
  163. package/dist/slots.d.ts +24 -0
  164. package/dist/slots.js +75 -0
  165. package/dist/stylesheet.d.ts +15 -0
  166. package/dist/stylesheet.js +188 -0
  167. package/dist/theme.d.ts +20 -0
  168. package/dist/theme.js +13 -0
  169. package/dist/tip.d.ts +40 -0
  170. package/dist/tip.js +82 -0
  171. package/dist/tokens.d.ts +28 -0
  172. package/dist/tokens.js +180 -0
  173. package/package.json +56 -0
@@ -0,0 +1,119 @@
1
+ import { Slot, composeRefs } from "../slot.js";
2
+ import { searchEmoji, shortcodeAt, writeAt } from "./emoji.js";
3
+ import { EmojiGrid, MapleEmoji } from "./picker-emoji.js";
4
+ import { createElement, forwardRef, useEffect, useRef, useState } from "react";
5
+ import { useMaple, useMapleClient } from "@maple-kit/react";
6
+ //#region src/composer/body.ts
7
+ const COMPOSER_PLACEHOLDER = "What is wrong with this?";
8
+ const MapleBody = forwardRef(function MapleBody(props, ref) {
9
+ const { composer } = useMaple();
10
+ const client = useMapleClient();
11
+ const field = useRef(null);
12
+ const [open, setOpen] = useState(false);
13
+ const suggest = useSuggestions(composer.body);
14
+ const reading = composer.viewing !== void 0;
15
+ const Element = props.asChild ? Slot : "textarea";
16
+ useEffect(() => {
17
+ if (composer.open && !reading) field.current?.focus();
18
+ }, [composer.open, reading]);
19
+ if (reading) return createElement("p", { className: [
20
+ "mk-composer-row",
21
+ "mk-read",
22
+ "mk-body",
23
+ props.className
24
+ ].filter(Boolean).join(" ") }, composer.body);
25
+ const write = (glyph, over) => {
26
+ const caret = field.current?.selectionStart ?? composer.body.length;
27
+ const from = over?.start ?? caret;
28
+ const next = writeAt(composer.body, from, caret, glyph);
29
+ client.setBody(next.body);
30
+ suggest.clear();
31
+ setOpen(false);
32
+ restore(field, next.caret);
33
+ };
34
+ const area = createElement(Element, {
35
+ ref: composeRefs(ref, field),
36
+ className: [
37
+ "mk-field",
38
+ "mk-body",
39
+ props.className
40
+ ].filter(Boolean).join(" "),
41
+ value: composer.body,
42
+ placeholder: COMPOSER_PLACEHOLDER,
43
+ "aria-label": COMPOSER_PLACEHOLDER,
44
+ onChange: (event) => {
45
+ client.setBody(event.target.value);
46
+ suggest.read(event.target.value, event.target.selectionStart);
47
+ },
48
+ onKeyDown: (event) => suggest.onKeyDown(event, write),
49
+ onBlur: () => suggest.clear()
50
+ });
51
+ return createElement("div", { className: "mk-composer-row mk-field-wrap" }, area, props.emoji === false ? null : createElement(MapleEmoji, {
52
+ open,
53
+ onToggle: setOpen,
54
+ onPick: (glyph) => write(glyph)
55
+ }), menu(suggest, write));
56
+ });
57
+ function menu(suggest, write) {
58
+ if (!suggest.at || suggest.found.length === 0) return null;
59
+ return createElement(EmojiGrid, {
60
+ className: "mk-emoji-menu",
61
+ emoji: suggest.found,
62
+ active: suggest.active,
63
+ onPick: (glyph) => write(glyph, suggest.at)
64
+ });
65
+ }
66
+ function useSuggestions(body) {
67
+ const [caret, setCaret] = useState();
68
+ const [dismissed, setDismissed] = useState(false);
69
+ const [active, setActive] = useState(0);
70
+ const at = dismissed || caret === void 0 ? void 0 : shortcodeAt(body, caret);
71
+ const found = at ? searchEmoji(at.query) : [];
72
+ const clear = () => {
73
+ setDismissed(true);
74
+ setActive(0);
75
+ };
76
+ const read = (next, where) => {
77
+ setCaret(where ?? void 0);
78
+ setDismissed(false);
79
+ setActive(0);
80
+ };
81
+ const onKeyDown = (event, write) => {
82
+ if (!at || found.length === 0) return;
83
+ if (event.key === "Escape") {
84
+ event.stopPropagation();
85
+ return clear();
86
+ }
87
+ const step = stepFor(event.key);
88
+ if (step !== 0) {
89
+ event.preventDefault();
90
+ return setActive((was) => (was + step + found.length) % found.length);
91
+ }
92
+ if (event.key !== "Enter" && event.key !== "Tab") return;
93
+ event.preventDefault();
94
+ const glyph = found[active]?.glyph;
95
+ if (glyph) write(glyph, at);
96
+ };
97
+ return {
98
+ at,
99
+ found,
100
+ active,
101
+ read,
102
+ clear,
103
+ onKeyDown
104
+ };
105
+ }
106
+ function stepFor(key) {
107
+ if (key === "ArrowDown" || key === "ArrowRight") return 1;
108
+ return key === "ArrowUp" || key === "ArrowLeft" ? -1 : 0;
109
+ }
110
+ function restore(field, caret) {
111
+ requestAnimationFrame(() => {
112
+ const node = field.current;
113
+ if (!node) return;
114
+ node.focus();
115
+ node.setSelectionRange(caret, caret);
116
+ });
117
+ }
118
+ //#endregion
119
+ export { COMPOSER_PLACEHOLDER, MapleBody };
@@ -0,0 +1,23 @@
1
+ import { AsChildProps } from "../slot.js";
2
+ import { LeaveAsk } from "./leave.js";
3
+ import { SheetDetent } from "./scope.js";
4
+ import { ReactNode } from "react";
5
+ //#region src/composer/composer.d.ts
6
+ /** How the composer is mounted. No prop here changes its look. */
7
+ export interface MapleComposerProps extends AsChildProps {
8
+ readonly className?: string;
9
+ readonly children?: ReactNode;
10
+ /** The frame to inset while the panel is open, rather than cover. */
11
+ readonly inset?: ElementCSSInlineStyle | null;
12
+ /** Hold Space to see through the panel. On unless an application says otherwise. */
13
+ readonly peek?: boolean;
14
+ /** The same `LeaveAsk` the controller was given, so the confirm is drawn here. */
15
+ readonly leave?: LeaveAsk;
16
+ }
17
+ /** What a screen reader calls the panel. */
18
+ export declare const COMPOSER_LABEL = "New comment";
19
+ /** Expand and collapse, in the words the two detents actually do. */
20
+ export declare const DETENT_LABELS: Readonly<Record<SheetDetent, string>>;
21
+ /** The panel. Every part below it reads the controller, never a prop. */
22
+ export declare const MapleComposer: import("react").ForwardRefExoticComponent<MapleComposerProps & import("react").RefAttributes<HTMLElement>>;
23
+ //#endregion
@@ -0,0 +1,122 @@
1
+ import { useMapleUi } from "../context.js";
2
+ import { dataAttributes } from "../data.js";
3
+ import { Slot } from "../slot.js";
4
+ import { ComposerScope } from "./scope.js";
5
+ import { useInset } from "./inset.js";
6
+ import { useHoldToPeek } from "./peek.js";
7
+ import { LeavePrompt } from "./prompt.js";
8
+ import { Fragment, createElement, forwardRef, useEffect, useMemo, useState } from "react";
9
+ import { useMaple, useMapleClient } from "@maple-kit/react";
10
+ import { imageFrom, previewOf } from "@maple-kit/core/screenshot";
11
+ //#region src/composer/composer.ts
12
+ const COMPOSER_LABEL = "New comment";
13
+ const DETENT_LABELS = {
14
+ full: "Show less of the comment",
15
+ half: "Show more of the comment"
16
+ };
17
+ const MapleComposer = forwardRef(function MapleComposer(props, ref) {
18
+ const { composer, user } = useMaple();
19
+ const client = useMapleClient();
20
+ const { container } = useMapleUi("Maple.Composer");
21
+ const view = container.ownerDocument.defaultView;
22
+ const [panel, setPanel] = useState(null);
23
+ const [motion, setMotion] = useState({
24
+ open: composer.open,
25
+ moving: false
26
+ });
27
+ const scope = useScopeValue(composer.open, view, props.peek !== false);
28
+ if (motion.open !== composer.open) setMotion({
29
+ open: composer.open,
30
+ moving: true
31
+ });
32
+ useInset(props.inset, composer.open ? panel : null, view);
33
+ const Element = props.asChild ? Slot : "section";
34
+ const grabber = createElement(Grabber, {
35
+ detent: scope.detent,
36
+ toggle: scope.toggleDetent
37
+ });
38
+ return createElement(Fragment, null, createElement(ComposerScope.Provider, { value: scope }, createElement(Element, {
39
+ ...shellAttributes(composer.open, scope, motion),
40
+ ...dataAttributes({ provenance: user ? "server" : "guest" }),
41
+ className: props.className ? `mk-composer ${props.className}` : "mk-composer",
42
+ role: "dialog",
43
+ "aria-label": COMPOSER_LABEL,
44
+ ref: composeInto(ref, setPanel),
45
+ onKeyDown: (event) => onEscape(event, client),
46
+ onPaste: (event) => onPaste(event, scope),
47
+ onDragOver: (event) => event.preventDefault(),
48
+ onDrop: (event) => onDrop(event, scope),
49
+ onTransitionEnd: (event) => {
50
+ if (event.target === event.currentTarget) setMotion({
51
+ open: composer.open,
52
+ moving: false
53
+ });
54
+ }
55
+ }, props.asChild ? props.children : createElement(Fragment, null, grabber, props.children))), props.leave ? createElement(LeavePrompt, { ask: props.leave }) : null);
56
+ });
57
+ function shellAttributes(open, scope, motion) {
58
+ return {
59
+ "data-mk-open": String(open),
60
+ "data-mk-peek": String(scope.peeking),
61
+ "data-mk-detent": scope.detent,
62
+ "data-mk-moving": String(motion.moving),
63
+ inert: !open
64
+ };
65
+ }
66
+ function Grabber(props) {
67
+ return createElement("button", {
68
+ type: "button",
69
+ className: "mk-grab mk-hit mk-press",
70
+ "aria-label": DETENT_LABELS[props.detent],
71
+ "aria-expanded": props.detent === "full",
72
+ onClick: props.toggle
73
+ });
74
+ }
75
+ function onEscape(event, client) {
76
+ if (event.key !== "Escape") return;
77
+ event.stopPropagation();
78
+ client.closeComposer();
79
+ }
80
+ function onPaste(event, scope) {
81
+ const image = imageFrom(event.clipboardData);
82
+ if (!image) return;
83
+ event.preventDefault();
84
+ scope.offer(image);
85
+ }
86
+ function onDrop(event, scope) {
87
+ const image = imageFrom(event.dataTransfer);
88
+ if (!image) return;
89
+ event.preventDefault();
90
+ scope.offer(image);
91
+ }
92
+ function composeInto(ref, set) {
93
+ return (node) => {
94
+ set(node);
95
+ if (typeof ref === "function") ref(node);
96
+ else if (ref) ref.current = node;
97
+ };
98
+ }
99
+ function useScopeValue(open, view, peekable) {
100
+ const [detent, setDetent] = useState("half");
101
+ const [pending, setPending] = useState();
102
+ const peeking = useHoldToPeek(view, open && peekable);
103
+ useEffect(() => pending ? () => pending.preview.revoke() : void 0, [pending]);
104
+ return useMemo(() => ({
105
+ peeking,
106
+ detent,
107
+ toggleDetent: () => setDetent((was) => was === "half" ? "full" : "half"),
108
+ pending,
109
+ offer: (image, source) => setPending({
110
+ image,
111
+ preview: previewOf(image),
112
+ source: source ?? "offered"
113
+ }),
114
+ clear: () => setPending(void 0)
115
+ }), [
116
+ peeking,
117
+ detent,
118
+ pending
119
+ ]);
120
+ }
121
+ //#endregion
122
+ export { COMPOSER_LABEL, DETENT_LABELS, MapleComposer };