@maple-kit/ui 0.5.0 → 0.7.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.
@@ -6,10 +6,10 @@ export interface MapleActionsProps extends AsChildProps {
6
6
  readonly className?: string;
7
7
  readonly children?: ReactNode;
8
8
  }
9
- /** Leaves the draft where it is. */
10
- export declare const CANCEL_LABEL = "Cancel";
11
- /** The noun: it is what the reviewer is making. */
12
- export declare const SEND_LABEL = "Comment";
9
+ /** Closes the composer and keeps what was written, unsent. */
10
+ export declare const KEEP_LABEL = "Keep";
11
+ /** The verb, because publishing is the act and the comment already exists. */
12
+ export declare const PUBLISH_LABEL = "Publish";
13
13
  /** The two controls, and room beside them for a later one. */
14
14
  export declare const MapleActions: import("react").ForwardRefExoticComponent<MapleActionsProps & import("react").RefAttributes<HTMLElement>>;
15
15
  //#endregion
@@ -3,20 +3,20 @@ import { useComposerScope } from "./scope.js";
3
3
  import { createElement, forwardRef } from "react";
4
4
  import { useMaple, useMapleClient } from "@maple-kit/react";
5
5
  //#region src/composer/actions.ts
6
- const CANCEL_LABEL = "Cancel";
7
- const SEND_LABEL = "Comment";
6
+ const KEEP_LABEL = "Keep";
7
+ const PUBLISH_LABEL = "Publish";
8
8
  const VIEW_LABELS = {
9
9
  close: "Close",
10
10
  resolve: "Resolve",
11
11
  reopen: "Reopen"
12
12
  };
13
13
  const MapleActions = forwardRef(function MapleActions(props, ref) {
14
- const { composer } = useMaple();
14
+ const { composer, publishing } = useMaple();
15
15
  const client = useMapleClient();
16
16
  const scope = useComposerScope("Maple.Actions");
17
17
  const Element = props.asChild ? Slot : "footer";
18
- const send = () => {
19
- client.send().then(() => scope.clear(), () => void 0);
18
+ const publish = () => {
19
+ client.publish().then(() => scope.clear(), () => void 0);
20
20
  };
21
21
  const className = props.className ? `mk-composer-foot ${props.className}` : "mk-composer-foot";
22
22
  if (composer.viewing !== void 0) return createElement(Element, {
@@ -29,13 +29,14 @@ const MapleActions = forwardRef(function MapleActions(props, ref) {
29
29
  }, props.children, createElement("span", { className: "mk-composer-fill" }), createElement("button", {
30
30
  type: "button",
31
31
  className: "mk-btn mk-btn-quiet mk-press",
32
- onClick: () => client.closeComposer()
33
- }, CANCEL_LABEL), createElement("button", {
32
+ disabled: composer.body.trim() === "",
33
+ onClick: () => client.keepDraft()
34
+ }, KEEP_LABEL), createElement("button", {
34
35
  type: "button",
35
36
  className: "mk-btn mk-btn-primary mk-press",
36
- disabled: composer.body.trim() === "" || composer.sending,
37
- onClick: send
38
- }, SEND_LABEL));
37
+ disabled: composer.body.trim() === "" || publishing,
38
+ onClick: publish
39
+ }, PUBLISH_LABEL));
39
40
  });
40
41
  function reading(id, client) {
41
42
  const status = client.getState().comments.find((one) => one.id === id)?.status;
@@ -56,4 +57,4 @@ function moveOn(id, done, client) {
56
57
  }, done ? VIEW_LABELS.reopen : VIEW_LABELS.resolve);
57
58
  }
58
59
  //#endregion
59
- export { CANCEL_LABEL, MapleActions, SEND_LABEL, VIEW_LABELS };
60
+ export { KEEP_LABEL, MapleActions, PUBLISH_LABEL, VIEW_LABELS };
@@ -1,5 +1,5 @@
1
1
  import { SHEET_BREAKPOINT_PX } from "../tokens.js";
2
- import { CANCEL_LABEL, MapleActions, MapleActionsProps, SEND_LABEL } from "./actions.js";
2
+ import { KEEP_LABEL, MapleActions, MapleActionsProps, PUBLISH_LABEL } from "./actions.js";
3
3
  import { ATTACH_WORDS, MapleAttachments, MapleAttachmentsProps } from "./attachments.js";
4
4
  import { CONTEXT_LABELS, MapleContextBadge, MapleContextProps } from "./badge.js";
5
5
  import { COMPOSER_PLACEHOLDER, MapleBody, MapleBodyProps } from "./body.js";
@@ -13,4 +13,4 @@ import { KIND_WORDS, LEAVE_DISCARD, LEAVE_KEEP, QUOTE_LIMIT, TARGET_PREFIX, UNNA
13
13
  import { EMOJI_COPY, EMOJI_ICON_PX, EmojiGrid, EmojiGridProps, MapleEmoji, MapleEmojiProps } from "./picker-emoji.js";
14
14
  import { KIND_AUTO, KIND_LABEL, MapleScoreCard, MapleScoreProps, SCORE_LABEL } from "./score.js";
15
15
  import { CLOSE_LABEL, DETAIL_ATTRIBUTE, MapleTarget, MapleTargetProps } from "./target.js";
16
- export { ATTACH_WORDS, MapleActions as Actions, MapleAttachments as Attachments, MapleBody as Body, CANCEL_LABEL, CLOSE_LABEL, COMPOSER_LABEL, COMPOSER_PLACEHOLDER, CONTEXT_LABELS, MapleComposer as Composer, ComposerScopeError, MapleContextBadge as Context, DETAIL_ATTRIBUTE, DETENT_LABELS, MapleDetail as Detail, EMOJI_COPY, EMOJI_ICON_PX, MapleEmoji as Emoji, type EmojiChoice, EmojiGrid, type EmojiGridProps, KIND_AUTO, KIND_LABEL, KIND_WORDS, LEAVE_DISCARD, LEAVE_KEEP, type LeaveAsk, MapleActions, type MapleActionsProps, MapleAttachments, type MapleAttachmentsProps, MapleBody, type MapleBodyProps, MapleComposer, type MapleComposerProps, MapleContextBadge, type MapleContextProps, MapleDetail, type MapleDetailProps, MapleEmoji, type MapleEmojiProps, MapleScoreCard, type MapleScoreProps, MapleTarget, type MapleTargetProps, QUOTE_LIMIT, REVIEW_EMOJI, SCORE_LABEL, SEND_LABEL, SHEET_BREAKPOINT_PX, SHORTCODE_LIMIT, MapleScoreCard as Score, type SheetDetent, type Shortcode, TARGET_PREFIX, MapleTarget as Target, UNNAMED_TARGET, type Written, appended, createLeaveAsk, detailRows, leaveMessage, peeks, quotedText, searchEmoji, shortcodeAt, targetName, targetPhrase, writeAt };
16
+ export { ATTACH_WORDS, MapleActions as Actions, MapleAttachments as Attachments, MapleBody as Body, CLOSE_LABEL, COMPOSER_LABEL, COMPOSER_PLACEHOLDER, CONTEXT_LABELS, MapleComposer as Composer, ComposerScopeError, MapleContextBadge as Context, DETAIL_ATTRIBUTE, DETENT_LABELS, MapleDetail as Detail, EMOJI_COPY, EMOJI_ICON_PX, MapleEmoji as Emoji, type EmojiChoice, EmojiGrid, type EmojiGridProps, KEEP_LABEL, KIND_AUTO, KIND_LABEL, KIND_WORDS, LEAVE_DISCARD, LEAVE_KEEP, type LeaveAsk, MapleActions, type MapleActionsProps, MapleAttachments, type MapleAttachmentsProps, MapleBody, type MapleBodyProps, MapleComposer, type MapleComposerProps, MapleContextBadge, type MapleContextProps, MapleDetail, type MapleDetailProps, MapleEmoji, type MapleEmojiProps, MapleScoreCard, type MapleScoreProps, MapleTarget, type MapleTargetProps, PUBLISH_LABEL, QUOTE_LIMIT, REVIEW_EMOJI, SCORE_LABEL, SHEET_BREAKPOINT_PX, SHORTCODE_LIMIT, MapleScoreCard as Score, type SheetDetent, type Shortcode, TARGET_PREFIX, MapleTarget as Target, UNNAMED_TARGET, type Written, appended, createLeaveAsk, detailRows, leaveMessage, peeks, quotedText, searchEmoji, shortcodeAt, targetName, targetPhrase, writeAt };
@@ -1,6 +1,6 @@
1
1
  import { SHEET_BREAKPOINT_PX } from "../tokens.js";
2
2
  import { ComposerScopeError } from "./scope.js";
3
- import { CANCEL_LABEL, MapleActions, SEND_LABEL } from "./actions.js";
3
+ import { KEEP_LABEL, MapleActions, PUBLISH_LABEL } from "./actions.js";
4
4
  import { ATTACH_WORDS, MapleAttachments } from "./attachments.js";
5
5
  import { CONTEXT_LABELS, MapleContextBadge } from "./badge.js";
6
6
  import { REVIEW_EMOJI, SHORTCODE_LIMIT, appended, searchEmoji, shortcodeAt, writeAt } from "./emoji.js";
@@ -13,4 +13,4 @@ import { MapleDetail, detailRows } from "./detail.js";
13
13
  import { createLeaveAsk } from "./leave.js";
14
14
  import { KIND_AUTO, KIND_LABEL, MapleScoreCard, SCORE_LABEL } from "./score.js";
15
15
  import { CLOSE_LABEL, DETAIL_ATTRIBUTE, MapleTarget } from "./target.js";
16
- export { ATTACH_WORDS, MapleActions as Actions, MapleAttachments as Attachments, MapleBody as Body, CANCEL_LABEL, CLOSE_LABEL, COMPOSER_LABEL, COMPOSER_PLACEHOLDER, CONTEXT_LABELS, MapleComposer as Composer, ComposerScopeError, MapleContextBadge as Context, DETAIL_ATTRIBUTE, DETENT_LABELS, MapleDetail as Detail, EMOJI_COPY, EMOJI_ICON_PX, MapleEmoji as Emoji, EmojiGrid, KIND_AUTO, KIND_LABEL, KIND_WORDS, LEAVE_DISCARD, LEAVE_KEEP, MapleActions, MapleAttachments, MapleBody, MapleComposer, MapleContextBadge, MapleDetail, MapleEmoji, MapleScoreCard, MapleTarget, QUOTE_LIMIT, REVIEW_EMOJI, SCORE_LABEL, SEND_LABEL, SHEET_BREAKPOINT_PX, SHORTCODE_LIMIT, MapleScoreCard as Score, TARGET_PREFIX, MapleTarget as Target, UNNAMED_TARGET, appended, createLeaveAsk, detailRows, leaveMessage, peeks, quotedText, searchEmoji, shortcodeAt, targetName, targetPhrase, writeAt };
16
+ export { ATTACH_WORDS, MapleActions as Actions, MapleAttachments as Attachments, MapleBody as Body, CLOSE_LABEL, COMPOSER_LABEL, COMPOSER_PLACEHOLDER, CONTEXT_LABELS, MapleComposer as Composer, ComposerScopeError, MapleContextBadge as Context, DETAIL_ATTRIBUTE, DETENT_LABELS, MapleDetail as Detail, EMOJI_COPY, EMOJI_ICON_PX, MapleEmoji as Emoji, EmojiGrid, KEEP_LABEL, KIND_AUTO, KIND_LABEL, KIND_WORDS, LEAVE_DISCARD, LEAVE_KEEP, MapleActions, MapleAttachments, MapleBody, MapleComposer, MapleContextBadge, MapleDetail, MapleEmoji, MapleScoreCard, MapleTarget, PUBLISH_LABEL, QUOTE_LIMIT, REVIEW_EMOJI, SCORE_LABEL, SHEET_BREAKPOINT_PX, SHORTCODE_LIMIT, MapleScoreCard as Score, TARGET_PREFIX, MapleTarget as Target, UNNAMED_TARGET, appended, createLeaveAsk, detailRows, leaveMessage, peeks, quotedText, searchEmoji, shortcodeAt, targetName, targetPhrase, writeAt };
@@ -0,0 +1,10 @@
1
+ import { PartProps } from "../part.js";
2
+ import { ReactNode } from "react";
3
+ //#region src/island/approve.d.ts
4
+ /** The row. Its children replace everything inside it. */
5
+ export interface ApproveProps extends PartProps {
6
+ readonly children?: ReactNode;
7
+ }
8
+ /** The sign-off, as one row above the picks. */
9
+ export declare const Approve: import("react").ForwardRefExoticComponent<ApproveProps & import("react").RefAttributes<HTMLDivElement>>;
10
+ //#endregion
@@ -0,0 +1,40 @@
1
+ import { cx, renderPart } from "../part.js";
2
+ import { APPROVE_COPY } from "./language.js";
3
+ import { createElement, forwardRef } from "react";
4
+ import { useMaple, useMapleClient } from "@maple-kit/react";
5
+ //#region src/island/approve.ts
6
+ const Approve = forwardRef(function Approve(props, ref) {
7
+ const { asChild, children, className, ...rest } = props;
8
+ const state = useMaple();
9
+ const client = useMapleClient();
10
+ if (state.approval?.supported !== true) return null;
11
+ const mine = state.myApproval;
12
+ return renderPart("div", asChild, {
13
+ ...rest,
14
+ className: cx("mk-approve", className),
15
+ "data-mk-approved": mine === null ? "no" : "yes",
16
+ ref
17
+ }, children ?? [createElement("span", {
18
+ key: "said",
19
+ className: "mk-approve-said"
20
+ }, said(state, mine)), control(state, mine, client)]);
21
+ });
22
+ function said(state, mine) {
23
+ if (mine) return APPROVE_COPY.yours;
24
+ const others = state.approvals.length;
25
+ if (others > 0) return APPROVE_COPY.others(state.approvals[0]?.author.name, others);
26
+ return state.approval?.required === true ? APPROVE_COPY.wanted : APPROVE_COPY.offered;
27
+ }
28
+ function control(state, mine, client) {
29
+ const signedIn = state.user !== null;
30
+ return createElement("button", {
31
+ key: "act",
32
+ type: "button",
33
+ className: "mk-approve-do mk-press",
34
+ disabled: !signedIn,
35
+ ...signedIn ? {} : { title: APPROVE_COPY.signIn },
36
+ onClick: () => void (mine ? client.unapprove() : client.approve())
37
+ }, mine ? APPROVE_COPY.withdraw : APPROVE_COPY.approve);
38
+ }
39
+ //#endregion
40
+ export { Approve };
@@ -11,6 +11,7 @@ function islandCss() {
11
11
  row(),
12
12
  rowDetail(),
13
13
  developer(),
14
+ ledger(),
14
15
  newComment(),
15
16
  keyframes(),
16
17
  sheet()
@@ -945,6 +946,144 @@ function developer() {
945
946
  display: none;
946
947
  }
947
948
 
949
+ `.trim();
950
+ }
951
+ function ledger() {
952
+ return `
953
+ .mk-unsent {
954
+ flex: none;
955
+ border-top: 1px solid var(--mk-line);
956
+ background: var(--mk-sunk);
957
+ }
958
+
959
+ .mk-unsent-head {
960
+ display: flex;
961
+ align-items: center;
962
+ gap: 8px;
963
+ padding: 7px 10px 5px;
964
+ }
965
+
966
+ .mk-unsent-label {
967
+ flex: 1 1 auto;
968
+ color: var(--mk-faint);
969
+ font-size: 9px;
970
+ font-weight: 700;
971
+ letter-spacing: 0.09em;
972
+ text-transform: uppercase;
973
+ }
974
+
975
+ .mk-unsent-copy {
976
+ flex: none;
977
+ padding: 2px 8px;
978
+ border: 1px solid var(--mk-line);
979
+ border-radius: 999px;
980
+ background: transparent;
981
+ color: var(--mk-muted);
982
+ font: inherit;
983
+ font-size: 11px;
984
+ font-weight: 600;
985
+ cursor: pointer;
986
+ }
987
+
988
+ .mk-unsent-publish {
989
+ flex: none;
990
+ font-size: 11px;
991
+ padding: 3px 11px;
992
+ }
993
+
994
+ .mk-unsent-rows {
995
+ display: flex;
996
+ flex-direction: column;
997
+ gap: 2px;
998
+ margin: 0;
999
+ padding: 0 6px 7px;
1000
+ list-style: none;
1001
+ max-height: 132px;
1002
+ overflow-y: auto;
1003
+ }
1004
+
1005
+ .mk-unsent-row {
1006
+ display: flex;
1007
+ align-items: center;
1008
+ gap: 4px;
1009
+ border-radius: 6px;
1010
+ }
1011
+
1012
+ .mk-unsent-row:hover {
1013
+ background: var(--mk-bg);
1014
+ }
1015
+
1016
+ .mk-unsent-body {
1017
+ flex: 1 1 auto;
1018
+ min-width: 0;
1019
+ padding: 4px 6px;
1020
+ border: 0;
1021
+ background: transparent;
1022
+ color: var(--mk-fg);
1023
+ font: inherit;
1024
+ font-size: 12px;
1025
+ text-align: start;
1026
+ white-space: nowrap;
1027
+ overflow: hidden;
1028
+ text-overflow: ellipsis;
1029
+ cursor: pointer;
1030
+ }
1031
+
1032
+ .mk-unsent-drop {
1033
+ flex: none;
1034
+ padding: 2px 7px;
1035
+ border: 0;
1036
+ background: transparent;
1037
+ color: var(--mk-faint);
1038
+ font: inherit;
1039
+ font-size: 11px;
1040
+ cursor: pointer;
1041
+ }
1042
+
1043
+ .mk-unsent-drop:hover {
1044
+ color: var(--mk-fg);
1045
+ }
1046
+
1047
+ .mk-approve {
1048
+ flex: none;
1049
+ display: flex;
1050
+ align-items: center;
1051
+ gap: 8px;
1052
+ padding: 7px 10px;
1053
+ border-top: 1px solid var(--mk-line);
1054
+ background: var(--mk-sunk);
1055
+ }
1056
+
1057
+ .mk-approve-said {
1058
+ flex: 1 1 auto;
1059
+ min-width: 0;
1060
+ color: var(--mk-muted);
1061
+ font-size: 11.5px;
1062
+ line-height: 1.35;
1063
+ }
1064
+
1065
+ .mk-approve[data-mk-approved="yes"] .mk-approve-said {
1066
+ color: var(--mk-fg);
1067
+ }
1068
+
1069
+ .mk-approve-do {
1070
+ flex: none;
1071
+ padding: 3px 11px;
1072
+ border: 1px solid var(--mk-line);
1073
+ border-radius: 999px;
1074
+ background: var(--mk-bg);
1075
+ color: var(--mk-fg);
1076
+ font: inherit;
1077
+ font-size: 11px;
1078
+ font-weight: 600;
1079
+ cursor: pointer;
1080
+ }
1081
+
1082
+ .mk-approve-do:disabled {
1083
+ color: var(--mk-faint);
1084
+ cursor: not-allowed;
1085
+ }
1086
+
948
1087
  `.trim();
949
1088
  }
950
1089
  function newComment() {
@@ -1,4 +1,5 @@
1
1
  import { Account, AccountProps } from "./account.js";
2
+ import { Approve, ApproveProps } from "./approve.js";
2
3
  import { FilterCounts, countsFor, numbersFor, orphanReason, resolutionsFor } from "./comments.js";
3
4
  import { IslandContent, IslandContentProps } from "./content.js";
4
5
  import { DragHandlers, IslandContextError, IslandContextValue, IslandPhase, PointerEventLike, reasonOf, useIsland } from "./context.js";
@@ -8,12 +9,13 @@ import { Filters, FiltersProps } from "./filters.js";
8
9
  import { Branch, BranchProps, Header, HeaderProps, Logo, LogoProps } from "./header.js";
9
10
  import { Island, IslandProps } from "./island.js";
10
11
  import { Item, ItemProps, TIME_DELAY_MS } from "./item.js";
11
- import { FILTER_LABELS, ISLAND_COPY, SETTINGS_COPY, kindPhrase, openLabel } from "./language.js";
12
+ import { APPROVE_COPY, FILTER_LABELS, ISLAND_COPY, SETTINGS_COPY, UNSENT_COPY, kindPhrase, openLabel } from "./language.js";
12
13
  import { List, ListProps } from "./list.js";
13
14
  import { NewComment, NewCommentProps, PickButton, PickButtonProps } from "./new-comment.js";
14
15
  import { Settings, SettingsProps } from "./settings.js";
15
16
  import { STAGGER_ROWS } from "./stagger.js";
16
17
  import { absoluteTime, relativeTime } from "./time.js";
17
18
  import { IslandTrigger, IslandTriggerProps } from "./trigger.js";
19
+ import { Unsent, UnsentProps } from "./unsent.js";
18
20
  import { WORDMARK_SIZE_PX, WORDMARK_WORD_SCALE, Wordmark, WordmarkProps } from "./wordmark.js";
19
- export { Account, type AccountProps, Branch, type BranchProps, DRAGGING_ATTRIBUTE, DRAG_THRESHOLD_PX, type DragHandlers, FILTER_LABELS, type FilterCounts, Filters, type FiltersProps, Header, type HeaderProps, ISLAND_COPY, Island, IslandContent, type IslandContentProps, IslandContextError, type IslandContextValue, type IslandPhase, type IslandProps, IslandTrigger, type IslandTriggerProps, Item, type ItemProps, List, type ListProps, Logo, type LogoProps, NewComment, type NewCommentProps, PickButton, type PickButtonProps, type PointerEventLike, SETTINGS_COPY, STAGGER_ROWS, Settings, type SettingsProps, TIME_DELAY_MS, WORDMARK_SIZE_PX, WORDMARK_WORD_SCALE, Wordmark, type WordmarkProps, absoluteTime, countsFor, islandCss, kindPhrase, numbersFor, openLabel, orphanReason, reasonOf, relativeTime, resolutionsFor, useDrag, useIsland };
21
+ export { APPROVE_COPY, Account, type AccountProps, Approve, type ApproveProps, Branch, type BranchProps, DRAGGING_ATTRIBUTE, DRAG_THRESHOLD_PX, type DragHandlers, FILTER_LABELS, type FilterCounts, Filters, type FiltersProps, Header, type HeaderProps, ISLAND_COPY, Island, IslandContent, type IslandContentProps, IslandContextError, type IslandContextValue, type IslandPhase, type IslandProps, IslandTrigger, type IslandTriggerProps, Item, type ItemProps, List, type ListProps, Logo, type LogoProps, NewComment, type NewCommentProps, PickButton, type PickButtonProps, type PointerEventLike, SETTINGS_COPY, STAGGER_ROWS, Settings, type SettingsProps, TIME_DELAY_MS, UNSENT_COPY, Unsent, type UnsentProps, WORDMARK_SIZE_PX, WORDMARK_WORD_SCALE, Wordmark, type WordmarkProps, absoluteTime, countsFor, islandCss, kindPhrase, numbersFor, openLabel, orphanReason, reasonOf, relativeTime, resolutionsFor, useDrag, useIsland };
@@ -1,7 +1,8 @@
1
1
  import { STAGGER_ROWS } from "./stagger.js";
2
2
  import { islandCss } from "./css.js";
3
- import { FILTER_LABELS, ISLAND_COPY, SETTINGS_COPY, kindPhrase, openLabel } from "./language.js";
3
+ import { APPROVE_COPY, FILTER_LABELS, ISLAND_COPY, SETTINGS_COPY, UNSENT_COPY, kindPhrase, openLabel } from "./language.js";
4
4
  import { Account } from "./account.js";
5
+ import { Approve } from "./approve.js";
5
6
  import { countsFor, numbersFor, orphanReason, resolutionsFor } from "./comments.js";
6
7
  import { IslandContextError, reasonOf, useIsland } from "./context.js";
7
8
  import { IslandContent } from "./content.js";
@@ -16,4 +17,5 @@ import { List } from "./list.js";
16
17
  import { NewComment, PickButton } from "./new-comment.js";
17
18
  import { Settings } from "./settings.js";
18
19
  import { IslandTrigger } from "./trigger.js";
19
- export { Account, Branch, DRAGGING_ATTRIBUTE, DRAG_THRESHOLD_PX, FILTER_LABELS, Filters, Header, ISLAND_COPY, Island, IslandContent, IslandContextError, IslandTrigger, Item, List, Logo, NewComment, PickButton, SETTINGS_COPY, STAGGER_ROWS, Settings, TIME_DELAY_MS, WORDMARK_SIZE_PX, WORDMARK_WORD_SCALE, Wordmark, absoluteTime, countsFor, islandCss, kindPhrase, numbersFor, openLabel, orphanReason, reasonOf, relativeTime, resolutionsFor, useDrag, useIsland };
20
+ import { Unsent } from "./unsent.js";
21
+ export { APPROVE_COPY, Account, Approve, Branch, DRAGGING_ATTRIBUTE, DRAG_THRESHOLD_PX, FILTER_LABELS, Filters, Header, ISLAND_COPY, Island, IslandContent, IslandContextError, IslandTrigger, Item, List, Logo, NewComment, PickButton, SETTINGS_COPY, STAGGER_ROWS, Settings, TIME_DELAY_MS, UNSENT_COPY, Unsent, WORDMARK_SIZE_PX, WORDMARK_WORD_SCALE, Wordmark, absoluteTime, countsFor, islandCss, kindPhrase, numbersFor, openLabel, orphanReason, reasonOf, relativeTime, resolutionsFor, useDrag, useIsland };
@@ -29,6 +29,35 @@ export declare const SETTINGS_COPY: {
29
29
  readonly hint: "It goes until you reload. A comment arriving, or a link to one, brings it straight back.";
30
30
  };
31
31
  };
32
+ /**
33
+ * The sign-off row. It says why before it says what: "Approve" alone reads as
34
+ * a button with no stakes, and the stakes are the whole point of the row.
35
+ */
36
+ export declare const APPROVE_COPY: {
37
+ readonly offered: "Looked and found nothing wrong? Say so here.";
38
+ readonly wanted: "This pull request is held until somebody approves the preview.";
39
+ readonly yours: "You approved this preview.";
40
+ readonly others: (name: string | undefined, count: number) => string;
41
+ readonly approve: "Approve";
42
+ readonly withdraw: "Withdraw";
43
+ readonly signIn: "Sign in first: an approval nobody can be named for is not one.";
44
+ };
45
+ /**
46
+ * The unsent list. A comment is a draft until it is published, and every word
47
+ * here has to carry that without ever calling it a failure.
48
+ */
49
+ export declare const UNSENT_COPY: {
50
+ readonly heading: (count: number) => string;
51
+ readonly publish: (count: number) => string;
52
+ readonly publishing: "Publishing…";
53
+ readonly copy: "Copy";
54
+ readonly copied: "Copied";
55
+ readonly copyHint: "Every unsent comment as markdown, to paste into an agent or a ticket.";
56
+ readonly resumeHint: "Open it again";
57
+ readonly discard: "Throw this one away";
58
+ readonly discardGlyph: "✕";
59
+ readonly blank: "(nothing written yet)";
60
+ };
32
61
  /** Copy with no better home than a name. */
33
62
  export declare const ISLAND_COPY: {
34
63
  readonly title: "Comments";
@@ -92,6 +92,27 @@ const ACCOUNT_COPY = {
92
92
  linkedAs: (login) => login === void 0 ? "Linked." : `Linked as ${login}.`,
93
93
  unlinkHint: "Forgets the token here. GitHub keeps the authorisation until you revoke it."
94
94
  };
95
+ const APPROVE_COPY = {
96
+ offered: "Looked and found nothing wrong? Say so here.",
97
+ wanted: "This pull request is held until somebody approves the preview.",
98
+ yours: "You approved this preview.",
99
+ others: (name, count) => count > 1 ? `Approved by ${name ?? "somebody"} and ${String(count - 1)} more.` : `Approved by ${name ?? "somebody"}.`,
100
+ approve: "Approve",
101
+ withdraw: "Withdraw",
102
+ signIn: "Sign in first: an approval nobody can be named for is not one."
103
+ };
104
+ const UNSENT_COPY = {
105
+ heading: (count) => `Unsent · ${String(count)}`,
106
+ publish: (count) => count === 1 ? "Publish" : `Publish all ${String(count)}`,
107
+ publishing: "Publishing…",
108
+ copy: "Copy",
109
+ copied: "Copied",
110
+ copyHint: "Every unsent comment as markdown, to paste into an agent or a ticket.",
111
+ resumeHint: "Open it again",
112
+ discard: "Throw this one away",
113
+ discardGlyph: "✕",
114
+ blank: "(nothing written yet)"
115
+ };
95
116
  const ISLAND_COPY = {
96
117
  title: "Comments",
97
118
  wordmark: "Maple",
@@ -123,4 +144,4 @@ function kindPhrase(kind, human) {
123
144
  return kind === "region" ? `an area of ${human}` : human;
124
145
  }
125
146
  //#endregion
126
- export { ACCOUNT_COPY, CORNER_LABELS, CORNER_ORDER, FILTERS_LABEL, FILTER_LABELS, FILTER_ORDER, ISLAND_COPY, PICK_LABELS, PICK_ORDER, SETTINGS_COPY, TALLY_ORDER, THEME_LABELS, THEME_TITLES, kindPhrase, openLabel, pickTitle, tallyTitle, triggerLabel };
147
+ export { ACCOUNT_COPY, APPROVE_COPY, CORNER_LABELS, CORNER_ORDER, FILTERS_LABEL, FILTER_LABELS, FILTER_ORDER, ISLAND_COPY, PICK_LABELS, PICK_ORDER, SETTINGS_COPY, TALLY_ORDER, THEME_LABELS, THEME_TITLES, UNSENT_COPY, kindPhrase, openLabel, pickTitle, tallyTitle, triggerLabel };
@@ -0,0 +1,10 @@
1
+ import { PartProps } from "../part.js";
2
+ import { ReactNode } from "react";
3
+ //#region src/island/unsent.d.ts
4
+ /** The section. Its children replace everything inside it. */
5
+ export interface UnsentProps extends PartProps {
6
+ readonly children?: ReactNode;
7
+ }
8
+ /** The unsent list, drawn only while something is waiting in it. */
9
+ export declare const Unsent: import("react").ForwardRefExoticComponent<UnsentProps & import("react").RefAttributes<HTMLDivElement>>;
10
+ //#endregion
@@ -0,0 +1,59 @@
1
+ import { cx, renderPart } from "../part.js";
2
+ import { UNSENT_COPY } from "./language.js";
3
+ import { createElement, forwardRef, useState } from "react";
4
+ import { useMaple, useMapleClient } from "@maple-kit/react";
5
+ //#region src/island/unsent.ts
6
+ const COPIED_MS = 1600;
7
+ const Unsent = forwardRef(function Unsent(props, ref) {
8
+ const { asChild, children, className, ...rest } = props;
9
+ const { drafts, publishing } = useMaple();
10
+ const client = useMapleClient();
11
+ const [copied, setCopied] = useState(false);
12
+ if (drafts.length === 0) return null;
13
+ const copy = () => {
14
+ navigator.clipboard.writeText(client.draftsAsMarkdown()).then(() => {
15
+ setCopied(true);
16
+ setTimeout(() => setCopied(false), COPIED_MS);
17
+ }, () => void 0);
18
+ };
19
+ return renderPart("div", asChild, {
20
+ ...rest,
21
+ className: cx("mk-unsent", className),
22
+ ref
23
+ }, children ?? [createElement("div", {
24
+ key: "head",
25
+ className: "mk-unsent-head"
26
+ }, createElement("span", { className: "mk-unsent-label" }, UNSENT_COPY.heading(drafts.length)), createElement("button", {
27
+ type: "button",
28
+ className: "mk-unsent-copy",
29
+ title: UNSENT_COPY.copyHint,
30
+ onClick: copy
31
+ }, copied ? UNSENT_COPY.copied : UNSENT_COPY.copy), createElement("button", {
32
+ type: "button",
33
+ className: "mk-btn mk-btn-primary mk-press mk-unsent-publish",
34
+ disabled: publishing,
35
+ onClick: () => void client.publish().catch(() => void 0)
36
+ }, publishing ? UNSENT_COPY.publishing : UNSENT_COPY.publish(drafts.length))), createElement("ul", {
37
+ key: "rows",
38
+ className: "mk-unsent-rows"
39
+ }, ...drafts.map((draft) => row(draft, client)))]);
40
+ });
41
+ function row(draft, client) {
42
+ return createElement("li", {
43
+ key: draft.id,
44
+ className: "mk-unsent-row"
45
+ }, createElement("button", {
46
+ type: "button",
47
+ className: "mk-unsent-body",
48
+ title: UNSENT_COPY.resumeHint,
49
+ onClick: () => client.resumeDraft(draft.id)
50
+ }, draft.body.trim() === "" ? UNSENT_COPY.blank : draft.body), createElement("button", {
51
+ type: "button",
52
+ className: "mk-unsent-drop",
53
+ "aria-label": UNSENT_COPY.discard,
54
+ title: UNSENT_COPY.discard,
55
+ onClick: () => client.discardDraft(draft.id)
56
+ }, UNSENT_COPY.discardGlyph));
57
+ }
58
+ //#endregion
59
+ export { Unsent };
package/dist/maple.js CHANGED
@@ -8,6 +8,7 @@ import { MapleDetail } from "./composer/detail.js";
8
8
  import { MapleScoreCard } from "./composer/score.js";
9
9
  import { MapleTarget } from "./composer/target.js";
10
10
  import { PICK_ORDER } from "./island/language.js";
11
+ import { Approve } from "./island/approve.js";
11
12
  import { IslandContent } from "./island/content.js";
12
13
  import { Filters } from "./island/filters.js";
13
14
  import { Branch, Header, Logo } from "./island/header.js";
@@ -17,6 +18,7 @@ import { List } from "./island/list.js";
17
18
  import { NewComment, PickButton } from "./island/new-comment.js";
18
19
  import { Settings } from "./island/settings.js";
19
20
  import { IslandTrigger } from "./island/trigger.js";
21
+ import { Unsent } from "./island/unsent.js";
20
22
  import { MapleMarkLayer } from "./marks/layer.js";
21
23
  import { MapleNotice } from "./notice/notice.js";
22
24
  import { MaplePicker } from "./picker/picker.js";
@@ -45,7 +47,7 @@ function inventory(branch, label, defaultOpen) {
45
47
  }, createElement(IslandTrigger), createElement(IslandContent, null, createElement(Header, null, createElement(Logo), createElement(Branch, {
46
48
  branch,
47
49
  ...label === void 0 ? {} : { label }
48
- }), createElement(Settings)), createElement(MapleNotice, { during: LOAD_CALLS }), createElement(Filters), createElement(List, { children: (comment) => createElement(Item, { comment }) }), createElement(NewComment, null, ...PICK_ORDER.map((kind) => createElement(PickButton, {
50
+ }), createElement(Settings)), createElement(MapleNotice, { during: LOAD_CALLS }), createElement(Filters), createElement(List, { children: (comment) => createElement(Item, { comment }) }), createElement(Unsent), createElement(Approve), createElement(NewComment, null, ...PICK_ORDER.map((kind) => createElement(PickButton, {
49
51
  key: kind,
50
52
  kind
51
53
  })))));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maple-kit/ui",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "Maple's composed reviewer parts: the marks, the island and the composer",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -32,8 +32,8 @@
32
32
  "./package.json": "./package.json"
33
33
  },
34
34
  "dependencies": {
35
- "@maple-kit/core": "0.5.0",
36
- "@maple-kit/react": "0.5.0"
35
+ "@maple-kit/core": "0.7.0",
36
+ "@maple-kit/react": "0.7.0"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "react": "^18.2.0 || ^19.0.0",