@activecollab/components
Version:
ActiveCollab Components
391 lines (379 loc) • 21.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.LinkingCommandsButton = void 0;
var _react = _interopRequireWildcard(require("react"));
var _styledComponents = _interopRequireDefault(require("styled-components"));
var _Button = require("../../components/Button");
var _ChooseV = require("../../components/ChooseV2");
var _Dialog = require("../../components/Dialog");
var _IconButton = require("../../components/IconButton");
var _Icons = require("../../components/Icons");
var _Loaders = require("../../components/Loaders");
var _Tooltip = require("../../components/Tooltip");
var _Typography = require("../../components/Typography");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
/**
* "Linking and Commands" button — a plain design-system secondary button (small,
* rounded, no icon, no AI gradient: this is not an AI feature). Clicking it opens
* a read-only reference dialog.
*
* This mock mirrors the shipped `activecollab-next` implementation
* (`src/components/tasks/SourceCode/LinkingAndCommandsButton.jsx` +
* `LinkingAndCommandsDialog.jsx`, task #457754): a 640px dialog with a short
* pitch, a centered ChooseV2 to switch conventions (Linking default), and two
* tabs. Linking shows the reference forms for this task and a copyable
* `git checkout -b …` suggestion; Commands shows an API-shaped table of action
* words and slash commands, plus a fixed example commit. Both the reference
* guide and the suggested-branch request are represented by short skeleton
* loads, the way the real dialog fetches them once it is opened.
*/
var SANS = '-apple-system, BlinkMacSystemFont, "Roboto", "Helvetica Neue", Arial, sans-serif';
var MONO = "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace";
/** The example task these instructions are written against. */
var TASK_ID = 4231;
/** Task URL, in production built with `~/utils/urls` `getTaskUrl`. */
var TASK_URL = "https://app.activecollab.com/1/projects/2110/tasks/".concat(TASK_ID);
/**
* Suggested branch name for this task — in production it is resolved per repo
* via GET .../tasks/{task_id}/source/suggest-branch-name. Mocked here.
*/
var SUGGESTED_BRANCH = "ilija/".concat(TASK_ID, "-add-cacak-region-support");
/** POSIX-safe single-quote escaping for a value dropped into a shell command. */
var shellQuote = function shellQuote(value) {
return "'".concat(value.replace(/'/g, "'\\''"), "'");
};
/* ------------------------------------------------------------------ */
/* Reference-guide data — shaped like the GET .../source/reference-guide */
/* payload the real dialog is driven by: a map of action words plus a */
/* flat list of slash commands. Descriptions are localised server-side; */
/* tokens and examples never translate. */
/* ------------------------------------------------------------------ */
/** Action words act on the linked task from a title, commit, or PR body. */
var ACTION_WORDS = {
close: {
primary: "Close",
example: "Close #".concat(TASK_ID),
description: "Closes the task when its pull request is merged.",
aliases: ["Closes", "Fixes", "Fixed", "Resolves", "Resolved"]
}
};
/** Slash commands act on the task from a commit message or PR description. */
var COMMANDS = [{
name: "spend",
syntax: "/spend {duration} [of {job type}]",
example: "/spend 1h 30m of Design",
description: "Logs the time you spent on the task."
}, {
name: "estimate",
syntax: "/estimate {duration}",
example: "/estimate 4h",
description: "Sets the task's time estimate."
}, {
name: "comment",
syntax: "/comment {text}",
example: "/comment Deployed to staging",
description: "Posts a comment on the task."
}, {
name: "done",
syntax: "/done",
example: "/done",
description: "Marks the task complete.",
aliases: ["/complete"]
}, {
name: "reopen",
syntax: "/reopen",
example: "/reopen",
description: "Reopens a completed task."
}, {
name: "assign",
syntax: "/assign @{member}",
example: "/assign @ilija",
description: "Assigns the task to a team member."
}, {
name: "label",
syntax: "/label {name}",
example: "/label In Progress",
description: "Adds a label to the task."
}];
/** Action words first, then the slash commands — the shipped row order. */
var TABLE_ROWS = [...Object.values(ACTION_WORDS).map(function (word) {
return {
key: "action-".concat(word.primary),
example: word.example,
description: word.description,
aliases: word.aliases
};
}), ...COMMANDS.map(function (command) {
return {
key: "command-".concat(command.name),
example: command.example,
format: command.syntax,
description: command.description,
aliases: command.aliases
};
})];
/**
* Illustrative commit message: closes the task on the subject line, logs time,
* leaves a note. Fixed sample text — only the task id is interpolated, and
* durations are printed verbatim (never reformatted).
*/
var EXAMPLE_COMMIT = "Fix ranks reset when a contract is archived, closes #".concat(TASK_ID, "\n\n/spend 1h 30m\n/comment Verified on staging, ranks hold after archive");
/* ------------------------------------------------------------------ */
/* Trigger button — plain DS secondary button, rounded to the compact */
/* "Suggest Subtasks" shape (6px), no icon, no gradient. */
/* ------------------------------------------------------------------ */
var TriggerButton = (0, _styledComponents.default)(_Button.Button).withConfig({
displayName: "LinkingCommandsButton__TriggerButton",
componentId: "sc-11q4kcm-0"
})(["&&{border-radius:6px;}"]);
/** Wider than the default 540px dialog, matching the shipped 640px width. */
var StyledDialog = (0, _styledComponents.default)(_Dialog.Dialog).withConfig({
displayName: "LinkingCommandsButton__StyledDialog",
componentId: "sc-11q4kcm-1"
})(["&&{width:640px;max-width:calc(100vw - 64px);}"]);
var StyledTitleRow = _styledComponents.default.div.withConfig({
displayName: "LinkingCommandsButton__StyledTitleRow",
componentId: "sc-11q4kcm-2"
})(["display:flex;align-items:center;justify-content:space-between;gap:12px;"]);
var StyledBody = _styledComponents.default.div.withConfig({
displayName: "LinkingCommandsButton__StyledBody",
componentId: "sc-11q4kcm-3"
})(["font-family:", ";display:flex;flex-direction:column;gap:16px;max-height:60vh;overflow-y:auto;.code{font-family:", ";background:var(--color-theme-200);color:var(--color-theme-900);border-radius:4px;padding:2px 6px;font-size:12px;}.lc-term{font-weight:600;color:var(--color-theme-900);}.lc-leads{display:flex;flex-direction:column;gap:8px;}.lc-lead{display:flex;align-items:flex-start;gap:8px;}.lc-lead svg{flex-shrink:0;width:16px;height:16px;margin-top:2px;color:var(--color-theme-600);}.lc-choosectl > div{justify-content:center;}.lc-tabbody{display:flex;flex-direction:column;gap:20px;}.lc-commands{gap:16px;}.lc-block{display:flex;flex-direction:column;gap:8px;}.lc-list{display:flex;flex-direction:column;gap:6px;margin:0;padding-left:20px;list-style:disc;}.lc-table{display:grid;grid-template-columns:max-content 1fr;column-gap:24px;}.lc-th{padding-bottom:8px;border-bottom:1px solid var(--border-primary);}.lc-td{padding:12px 0;border-bottom:1px solid var(--border-primary);}.lc-format{display:block;margin-top:4px;font-family:", ";}.lc-aliases{margin-top:12px;}.lc-skeleton{display:flex;flex-direction:column;gap:12px;}.lc-skel{border-radius:6px;}.lc-skel-line{height:20px;}.lc-skel-block{height:36px;width:100%;}.lc-skel-2-3{width:66%;}.lc-skel-1-2{width:50%;}.lc-branch-skel{width:100%;height:36px;border-radius:6px;}"], SANS, MONO, MONO);
/* Copyable code snippet — mirrors the shared CopySnippet: a theme-200 <pre>
with a copy control that reveals on hover (hidden when hideCopyButton). */
var StyledSnippet = _styledComponents.default.div.withConfig({
displayName: "LinkingCommandsButton__StyledSnippet",
componentId: "sc-11q4kcm-4"
})(["position:relative;pre{margin:0;background:var(--color-theme-200);color:var(--color-theme-900);border-radius:6px;padding:12px 16px;overflow-x:auto;font-family:", ";font-size:13px;line-height:1.5;}.lc-copy-btn{position:absolute;top:6px;right:6px;opacity:0;transition:opacity 0.15s ease;}&:hover .lc-copy-btn{opacity:1;}.lc-copy-btn svg{width:16px;height:16px;}"], MONO);
var TAB_OPTIONS = [{
id: "linking",
name: "Linking"
}, {
id: "commands",
name: "Commands"
}];
var CopySnippet = function CopySnippet(_ref) {
var code = _ref.code,
_ref$hideCopyButton = _ref.hideCopyButton,
hideCopyButton = _ref$hideCopyButton === void 0 ? false : _ref$hideCopyButton;
var _useState = (0, _react.useState)(false),
_useState2 = _slicedToArray(_useState, 2),
copied = _useState2[0],
setCopied = _useState2[1];
var copy = function copy() {
var _navigator$clipboard;
// eslint-disable-next-line compat/compat
void ((_navigator$clipboard = navigator.clipboard) === null || _navigator$clipboard === void 0 ? void 0 : _navigator$clipboard.writeText(code));
setCopied(true);
window.setTimeout(function () {
return setCopied(false);
}, 1500);
};
return /*#__PURE__*/_react.default.createElement(StyledSnippet, null, /*#__PURE__*/_react.default.createElement("pre", null, /*#__PURE__*/_react.default.createElement("code", null, code)), !hideCopyButton && /*#__PURE__*/_react.default.createElement(_Tooltip.Tooltip, {
title: copied ? "Copied!" : "Copy"
}, /*#__PURE__*/_react.default.createElement("button", {
type: "button",
className: "lc-copy-btn",
onClick: copy,
"aria-label": "Copy command",
style: {
border: "none",
background: "transparent",
cursor: "pointer",
padding: 4,
display: "inline-flex",
color: "var(--color-theme-600)"
}
}, /*#__PURE__*/_react.default.createElement(_Icons.CopyIcon, null))));
};
var LinkingTab = function LinkingTab(_ref2) {
var branchLoading = _ref2.branchLoading,
branch = _ref2.branch;
var command = branch ? "git checkout -b ".concat(shellQuote(branch)) : "";
return /*#__PURE__*/_react.default.createElement("div", {
className: "lc-tabbody"
}, /*#__PURE__*/_react.default.createElement("div", {
className: "lc-block"
}, /*#__PURE__*/_react.default.createElement(_Typography.Body2, {
color: "secondary"
}, "Mention the task in your git activity and it links to this task on its own:"), /*#__PURE__*/_react.default.createElement("ul", {
className: "lc-list"
}, /*#__PURE__*/_react.default.createElement("li", null, /*#__PURE__*/_react.default.createElement(_Typography.Body2, {
color: "secondary"
}, /*#__PURE__*/_react.default.createElement("span", {
className: "lc-term"
}, "In a branch name:"), " the raw task ID", " ", /*#__PURE__*/_react.default.createElement("span", {
className: "code"
}, TASK_ID))), /*#__PURE__*/_react.default.createElement("li", null, /*#__PURE__*/_react.default.createElement(_Typography.Body2, {
color: "secondary"
}, /*#__PURE__*/_react.default.createElement("span", {
className: "lc-term"
}, "In a title, commit message, or description:"), " ", /*#__PURE__*/_react.default.createElement("span", {
className: "code"
}, "#", TASK_ID), " or", " ", /*#__PURE__*/_react.default.createElement("span", {
className: "code"
}, "AC-", TASK_ID))), /*#__PURE__*/_react.default.createElement("li", null, /*#__PURE__*/_react.default.createElement(_Typography.Body2, {
color: "secondary"
}, /*#__PURE__*/_react.default.createElement("span", {
className: "lc-term"
}, "Anywhere:"), " the task URL", " ", /*#__PURE__*/_react.default.createElement("span", {
className: "code"
}, TASK_URL))))), branchLoading || command ? /*#__PURE__*/_react.default.createElement("div", {
className: "lc-block"
}, /*#__PURE__*/_react.default.createElement(_Typography.Body2, {
color: "secondary"
}, "Give the branch a name with the task number in it and it links the moment you push. Create one from the suggested name:"), branchLoading ? /*#__PURE__*/_react.default.createElement(_Loaders.SkeletonLoader, {
className: "lc-branch-skel"
}) : /*#__PURE__*/_react.default.createElement(CopySnippet, {
code: command
})) : null);
};
var CommandsTab = function CommandsTab() {
return /*#__PURE__*/_react.default.createElement("div", {
className: "lc-tabbody lc-commands"
}, /*#__PURE__*/_react.default.createElement(_Typography.Body2, {
color: "secondary"
}, "The table below lists action words and slash commands that you can use in a commit message or a pull request description to act on the linked task. Example:"), /*#__PURE__*/_react.default.createElement(CopySnippet, {
code: EXAMPLE_COMMIT,
hideCopyButton: true
}), /*#__PURE__*/_react.default.createElement("div", {
className: "lc-table"
}, /*#__PURE__*/_react.default.createElement(_Typography.Caption1, {
color: "tertiary",
className: "lc-th"
}, "Command"), /*#__PURE__*/_react.default.createElement(_Typography.Caption1, {
color: "tertiary",
className: "lc-th"
}, "What it does"), TABLE_ROWS.map(function (row) {
var _row$aliases;
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, {
key: row.key
}, /*#__PURE__*/_react.default.createElement("div", {
className: "lc-td"
}, /*#__PURE__*/_react.default.createElement("span", {
className: "code"
}, row.example), row.format ? /*#__PURE__*/_react.default.createElement(_Typography.Caption1, {
color: "tertiary",
className: "lc-format"
}, row.format) : null), /*#__PURE__*/_react.default.createElement("div", {
className: "lc-td"
}, /*#__PURE__*/_react.default.createElement(_Typography.Body2, {
color: "secondary"
}, row.description), (_row$aliases = row.aliases) !== null && _row$aliases !== void 0 && _row$aliases.length ? /*#__PURE__*/_react.default.createElement(_Typography.Body2, {
color: "secondary",
className: "lc-aliases"
}, "Aliases: ", row.aliases.join(", ")) : null));
})));
};
var LinkingCommandsButton = exports.LinkingCommandsButton = function LinkingCommandsButton() {
var _useState3 = (0, _react.useState)(false),
_useState4 = _slicedToArray(_useState3, 2),
open = _useState4[0],
setOpen = _useState4[1];
var _useState5 = (0, _react.useState)("linking"),
_useState6 = _slicedToArray(_useState5, 2),
tab = _useState6[0],
setTab = _useState6[1];
var _useState7 = (0, _react.useState)(true),
_useState8 = _slicedToArray(_useState7, 2),
guideLoading = _useState8[0],
setGuideLoading = _useState8[1];
var _useState9 = (0, _react.useState)(true),
_useState0 = _slicedToArray(_useState9, 2),
branchLoading = _useState0[0],
setBranchLoading = _useState0[1];
var _useState1 = (0, _react.useState)(null),
_useState10 = _slicedToArray(_useState1, 2),
suggestedBranch = _useState10[0],
setSuggestedBranch = _useState10[1];
// Mock the two endpoint loads the real dialog fires once it opens: the
// reference guide resolves first (~0.6s), then the suggested branch name
// (~1.1s), each showing its own skeleton until it lands.
(0, _react.useEffect)(function () {
if (!open) return undefined;
setGuideLoading(true);
setBranchLoading(true);
setSuggestedBranch(null);
var guideTimer = window.setTimeout(function () {
return setGuideLoading(false);
}, 600);
var branchTimer = window.setTimeout(function () {
setSuggestedBranch(SUGGESTED_BRANCH);
setBranchLoading(false);
}, 1100);
return function () {
window.clearTimeout(guideTimer);
window.clearTimeout(branchTimer);
};
}, [open]);
var openDialog = function openDialog() {
setTab("linking");
setOpen(true);
};
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(TriggerButton, {
type: "button",
variant: "secondary",
size: "small",
onClick: openDialog
}, "Linking and Commands"), /*#__PURE__*/_react.default.createElement(StyledDialog, {
open: open,
onClose: function onClose() {
return setOpen(false);
},
enableBackgroundClick: true
}, /*#__PURE__*/_react.default.createElement(_Dialog.Dialog.Title, {
disableDefaultHeading: true
}, /*#__PURE__*/_react.default.createElement(StyledTitleRow, null, /*#__PURE__*/_react.default.createElement(_Typography.Header3, null, "Linking and Commands"), /*#__PURE__*/_react.default.createElement(_IconButton.IconButton, {
type: "button",
variant: "text gray",
"aria-label": "Close",
onClick: function onClick() {
return setOpen(false);
}
}, /*#__PURE__*/_react.default.createElement(_Icons.CancelCrossIcon, null)))), /*#__PURE__*/_react.default.createElement(_Dialog.Dialog.ContentDivider, null), /*#__PURE__*/_react.default.createElement(_Dialog.Dialog.Content, null, /*#__PURE__*/_react.default.createElement(StyledBody, null, guideLoading ? /*#__PURE__*/_react.default.createElement("div", {
className: "lc-skeleton"
}, /*#__PURE__*/_react.default.createElement(_Loaders.SkeletonLoader, {
className: "lc-skel lc-skel-line lc-skel-2-3"
}), /*#__PURE__*/_react.default.createElement(_Loaders.SkeletonLoader, {
className: "lc-skel lc-skel-block"
}), /*#__PURE__*/_react.default.createElement(_Loaders.SkeletonLoader, {
className: "lc-skel lc-skel-block"
}), /*#__PURE__*/_react.default.createElement(_Loaders.SkeletonLoader, {
className: "lc-skel lc-skel-line lc-skel-1-2"
})) : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_Typography.Body2, null, "Keep a task in sync with your code without leaving your editor."), /*#__PURE__*/_react.default.createElement("div", {
className: "lc-leads"
}, /*#__PURE__*/_react.default.createElement("div", {
className: "lc-lead"
}, /*#__PURE__*/_react.default.createElement(_Icons.HashIcon, null), /*#__PURE__*/_react.default.createElement(_Typography.Body2, {
color: "secondary"
}, /*#__PURE__*/_react.default.createElement("span", {
className: "lc-term"
}, "Linking:"), " Mention a task in a branch, pull request, or commit and they will be connected automatically.")), /*#__PURE__*/_react.default.createElement("div", {
className: "lc-lead"
}, /*#__PURE__*/_react.default.createElement(_Icons.CommandLineIcon, null), /*#__PURE__*/_react.default.createElement(_Typography.Body2, {
color: "secondary"
}, /*#__PURE__*/_react.default.createElement("span", {
className: "lc-term"
}, "Commands:"), " Add short instructions in a commit or pull request description to act on the task: log time, close it, or leave a comment."))), /*#__PURE__*/_react.default.createElement(_ChooseV.ChooseV2, {
className: "lc-choosectl",
options: TAB_OPTIONS,
selected: [tab],
onChange: function onChange(ids) {
return setTab(ids[0]);
}
}), tab === "linking" ? /*#__PURE__*/_react.default.createElement(LinkingTab, {
branchLoading: branchLoading,
branch: suggestedBranch
}) : /*#__PURE__*/_react.default.createElement(CommandsTab, null))))));
};
LinkingCommandsButton.displayName = "LinkingCommandsButton";
//# sourceMappingURL=LinkingCommandsButton.js.map