@activecollab/components
Version:
ActiveCollab Components
355 lines (329 loc) • 17 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.messageComponents = exports.MessageDialog = exports.DocLinkContext = void 0;
var _react = _interopRequireWildcard(require("react"));
var _styledComponents = _interopRequireDefault(require("styled-components"));
var _BreakPoints = require("../../components/BreakPoints");
var _Button = require("../../components/Button");
var _Columns = require("../../components/Columns");
var _IconButton = require("../../components/IconButton");
var _Icons = require("../../components/Icons");
var _InfoBox = require("../../components/InfoBox");
var _Media = require("../../components/Media");
var _Modal = require("../../components/Modal");
var _Styles = require("../../components/PortableText/Styles");
var _Row = require("../../components/Row");
var _Stack = require("../../components/Stack");
var _Tiles = require("../../components/Tiles");
var _TitledText = require("../../components/TitledText");
var _Tooltip = require("../../components/Tooltip");
var _Typography = require("../../components/Typography");
var _Typography2 = require("../../components/Typography/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; }
/**
* The in-app message dialog, as a Presentation mock.
*
* Two pieces, mirroring how the product is split:
*
* - {@link MessageDialog} — the hollow dialog shell (width, closing,
* feedback), built only from design-system parts. In the product this shell
* lives in the application as `InAppMessageDialog`.
* - {@link messageComponents} — a demo message element catalog injected into
* the design-system Portable Text engine, exactly like the typed cards
* injected into the hollow StackedCard shell. In the product the catalog is
* application code; the presentation ships this demo copy so the stories
* need no app imports.
*/
/* ---- the dialog shell ---------------------------------------------- */
var DIALOG_WIDTH = {
sm: "520px",
md: "720px",
lg: "880px"
};
/* Feedback — the thumbs up / down slot. Down-vote reuses the up icon,
rotated (the DS set only ships ThumbUpOutlineIcon). */
var StyledFeedback = _styledComponents.default.div.withConfig({
displayName: "MessageDialog__StyledFeedback",
componentId: "sc-1kt5lil-0"
})(["display:flex;align-items:center;justify-content:center;gap:8px;padding:8px 24px 28px;", "{padding:4px 24px 24px;}.fb-question{margin-right:4px;}.fb-btn svg{width:18px;height:18px;}.fb-down svg{transform:rotate(180deg);}"], _BreakPoints.screenBelow.sm);
var DialogFeedback = function DialogFeedback(_ref) {
var onVote = _ref.onVote;
var _useState = (0, _react.useState)(null),
_useState2 = _slicedToArray(_useState, 2),
voted = _useState2[0],
setVoted = _useState2[1];
var vote = (0, _react.useCallback)(function (value) {
setVoted(value);
onVote === null || onVote === void 0 || onVote(value);
}, [onVote]);
return /*#__PURE__*/_react.default.createElement(StyledFeedback, null, voted ? /*#__PURE__*/_react.default.createElement(_Typography.Body2, {
color: "secondary"
}, "Thanks for your feedback!") : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_Typography.Body2, {
color: "secondary",
className: "fb-question"
}, "Was this helpful?"), /*#__PURE__*/_react.default.createElement(_Tooltip.Tooltip, {
title: "Yes, helpful"
}, /*#__PURE__*/_react.default.createElement(_IconButton.IconButton, {
type: "button",
variant: "text gray",
className: "fb-btn",
"aria-label": "Yes, helpful",
onClick: function onClick() {
return vote("up");
}
}, /*#__PURE__*/_react.default.createElement(_Icons.ThumbUpOutlineIcon, null))), /*#__PURE__*/_react.default.createElement(_Tooltip.Tooltip, {
title: "No, not helpful"
}, /*#__PURE__*/_react.default.createElement(_IconButton.IconButton, {
type: "button",
variant: "text gray",
className: "fb-btn fb-down",
"aria-label": "No, not helpful",
onClick: function onClick() {
return vote("down");
}
}, /*#__PURE__*/_react.default.createElement(_Icons.ThumbUpOutlineIcon, null)))));
};
/* The shell is behaviour only: it sizes itself, closes on click-outside /
Esc (DS Modal) / the X, and shows the feedback slot when an onVote handler
is supplied. Content is whatever the caller renders as children — usually
the Portable Text engine with the demo catalog injected. */
var StyledDialog = _styledComponents.default.div.withConfig({
displayName: "MessageDialog__StyledDialog",
componentId: "sc-1kt5lil-1"
})(["position:relative;box-sizing:border-box;display:flex;flex-direction:column;width:calc(100% - 32px);max-width:", ";max-height:calc(100vh - 96px);margin:56px auto 40px;overflow-y:auto;background-color:var(--page-paper-main);color:var(--color-theme-900);border-radius:12px;box-shadow:var(--shadow-primary);", "{margin:16px auto;}.dlg-close{position:absolute;top:12px;right:12px;z-index:2;}.dlg-content{padding:40px;}", "{.dlg-content{padding:32px 20px 24px;}}"], function (_ref2) {
var $size = _ref2.$size;
return DIALOG_WIDTH[$size];
}, _BreakPoints.screenBelow.sm, _BreakPoints.screenBelow.sm);
var MessageDialog = exports.MessageDialog = function MessageDialog(_ref3) {
var open = _ref3.open,
onClose = _ref3.onClose,
_ref3$size = _ref3.size,
size = _ref3$size === void 0 ? "md" : _ref3$size,
onVote = _ref3.onVote,
children = _ref3.children;
return /*#__PURE__*/_react.default.createElement(_Modal.Modal, {
open: open,
onClose: onClose
}, /*#__PURE__*/_react.default.createElement(StyledDialog, {
$size: size
}, /*#__PURE__*/_react.default.createElement("div", {
className: "dlg-close"
}, /*#__PURE__*/_react.default.createElement(_Tooltip.Tooltip, {
title: "Close"
}, /*#__PURE__*/_react.default.createElement(_IconButton.IconButton, {
type: "button",
variant: "text gray",
"aria-label": "Close",
onClick: onClose
}, /*#__PURE__*/_react.default.createElement(_Icons.CancelCrossIcon, null)))), /*#__PURE__*/_react.default.createElement("div", {
className: "dlg-content"
}, children), onVote ? /*#__PURE__*/_react.default.createElement(DialogFeedback, {
onVote: onVote
}) : null));
};
/* ---- doc-link wiring ------------------------------------------------ */
/** How a doc-link mark opens its article (by a slug-like reference). The
surface that renders the message supplies the handler — in the product it
closes the dialog and opens the article Sheet. */
var DocLinkContext = exports.DocLinkContext = /*#__PURE__*/_react.default.createContext(function () {
return undefined;
});
/* ---- the demo message element catalog ------------------------------- */
/* Inline marks ------------------------------------------------------- */
/* interface-name — the name of an interface element, e.g. the Security page. */
var StyledInterfaceName = _styledComponents.default.span.withConfig({
displayName: "MessageDialog__StyledInterfaceName",
componentId: "sc-1kt5lil-2"
})(["padding:0 4px;border-radius:4px;background-color:var(--color-theme-300);color:var(--color-theme-900);font-weight:600;white-space:nowrap;"]);
var InterfaceNameMark = function InterfaceNameMark(_ref4) {
var children = _ref4.children;
return /*#__PURE__*/_react.default.createElement(StyledInterfaceName, null, children);
};
/* keyboard-key — a keyboard key cap. */
var StyledKeyboardKey = _styledComponents.default.kbd.withConfig({
displayName: "MessageDialog__StyledKeyboardKey",
componentId: "sc-1kt5lil-3"
})(["display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;border:1px solid var(--border-primary);border-bottom-width:2px;border-radius:5px;background-color:var(--color-theme-200);color:var(--color-theme-900);font-family:ui-monospace,\"SFMono-Regular\",Menlo,Consolas,monospace;font-size:11px;font-weight:600;line-height:1;vertical-align:baseline;"]);
var KeyboardKeyMark = function KeyboardKeyMark(_ref5) {
var children = _ref5.children;
return /*#__PURE__*/_react.default.createElement(StyledKeyboardKey, null, children);
};
/* doc-link — an inline link that opens a full help article. The mark carries
an article reference; the open behaviour comes from DocLinkContext, so the
authored content stays declarative. */
/* Built on the engine's inline anchor so a doc-link sits in the sentence
exactly like a default link mark — same size, weight and line box as the
surrounding copy — just recoloured. */
var StyledDocLink = (0, _styledComponents.default)(_Styles.StyledLink).withConfig({
displayName: "MessageDialog__StyledDocLink",
componentId: "sc-1kt5lil-4"
})(["color:var(--color-secondary);text-decoration-color:var(--color-secondary);"]);
var DocLinkMark = function DocLinkMark(_ref6) {
var mark = _ref6.mark,
children = _ref6.children;
var openArticle = (0, _react.useContext)(DocLinkContext);
return /*#__PURE__*/_react.default.createElement(StyledDocLink, {
href: "#",
onClick: function onClick(event) {
event.preventDefault();
openArticle(mark === null || mark === void 0 ? void 0 : mark.article);
}
}, children);
};
/* Component objects --------------------------------------------------- */
/* callout — rendered with the DS InfoBox; the catalog keeps its own tone
vocabulary and maps it to InfoBox types. */
var CALLOUT_TYPE = {
info: "default",
note: "note",
success: "success"
};
var CalloutObject = function CalloutObject(_ref7) {
var _ref8;
var node = _ref7.node,
children = _ref7.children;
return /*#__PURE__*/_react.default.createElement(_InfoBox.InfoBox, {
type: CALLOUT_TYPE[(_ref8 = node.tone) !== null && _ref8 !== void 0 ? _ref8 : "info"],
title: node.title,
showIcon: true
}, children);
};
/* button-reference — a real DS Button shown for reference as its own block,
so a message can point at a button instead of screenshotting it. It is
intentionally non-interactive. */
var StyledButtonReference = _styledComponents.default.div.withConfig({
displayName: "MessageDialog__StyledButtonReference",
componentId: "sc-1kt5lil-5"
})(["display:flex;.c-button{pointer-events:none;}"]);
var ButtonReferenceObject = function ButtonReferenceObject(_ref9) {
var _ref0;
var node = _ref9.node;
if (typeof node.label !== "string") {
return null;
}
return /*#__PURE__*/_react.default.createElement(StyledButtonReference, {
"aria-hidden": "true"
}, /*#__PURE__*/_react.default.createElement(_Button.Button, {
variant: (_ref0 = node.variant) !== null && _ref0 !== void 0 ? _ref0 : "primary",
size: "small",
type: "button",
tabIndex: -1
}, node.label));
};
/* row — a horizontal run of blocks (e.g. two button references). */
var RowObject = function RowObject(_ref1) {
var _ref10;
var node = _ref1.node,
children = _ref1.children;
return /*#__PURE__*/_react.default.createElement(_Row.Row, {
space: (_ref10 = node.space) !== null && _ref10 !== void 0 ? _ref10 : "sm"
}, children);
};
/* tiles — an equal-size mosaic of blocks. */
var TilesObject = function TilesObject(_ref11) {
var _ref12;
var node = _ref11.node,
children = _ref11.children;
return /*#__PURE__*/_react.default.createElement(_Tiles.Tiles, {
columns: (_ref12 = node.columns) !== null && _ref12 !== void 0 ? _ref12 : 3,
space: "md",
collapseBelow: "sm"
}, children);
};
/* split — text beside media, with the one layout rule the messages follow:
screenshots sit on the right, flat illustrations on the left. `widths` is
authored as [text, media] and reordered with the columns. */
var SplitObject = function SplitObject(_ref13) {
var _ref14;
var node = _ref13.node,
children = _ref13.children;
var media = node.media;
var widths = (_ref14 = node.widths) !== null && _ref14 !== void 0 ? _ref14 : [1, 1];
var textColumn = /*#__PURE__*/_react.default.createElement(_Stack.Stack, {
space: "lg"
}, children);
// A split without media degrades to its text column instead of crashing.
if (!(media !== null && media !== void 0 && media.src)) {
return textColumn;
}
var screenshot = media.variant === "screenshot";
var mediaColumn = /*#__PURE__*/_react.default.createElement(_Media.Media, {
src: media.src,
alt: media.alt,
variant: media.variant,
caption: media.caption
});
return /*#__PURE__*/_react.default.createElement(_Columns.Columns, {
widths: screenshot ? widths : [...widths].reverse(),
space: "xl",
alignY: "center",
collapseBelow: "md"
}, screenshot ? textColumn : mediaColumn, screenshot ? mediaColumn : textColumn);
};
/* Default-block overrides --------------------------------------------- */
/* The engine's defaults use Body 1 primary; the messages are designed on the
quieter Body 2 secondary voice, so the catalog overrides the text blocks —
demonstrating the `blocks` half of the injection contract. */
var ParagraphBlock = function ParagraphBlock(_ref15) {
var children = _ref15.children;
return /*#__PURE__*/_react.default.createElement(_Typography.Body2, {
color: "secondary",
lineHeight: "loose"
}, children);
};
var ListItemBlock = function ListItemBlock(_ref16) {
var children = _ref16.children;
return /*#__PURE__*/_react.default.createElement(_Typography2.Typography, {
variant: "Body 2",
as: "li",
color: "secondary",
lineHeight: "loose"
}, children);
};
/* Titled sections speak in the quiet Header2 subhead voice rather than the
engine's default Title2 for level 2, and keep a roomier rhythm between a
titled section's content nodes. */
var TitledTextBlock = function TitledTextBlock(_ref17) {
var block = _ref17.block,
children = _ref17.children;
var _ref18 = block,
title = _ref18.title,
level = _ref18.level;
return /*#__PURE__*/_react.default.createElement(_TitledText.TitledText, {
title: title,
variant: level === 1 ? "title1" : "header2"
}, children ? /*#__PURE__*/_react.default.createElement(_Stack.Stack, {
space: "sm"
}, children) : undefined);
};
/** The demo catalog the presentations inject into the Portable Text engine —
the stand-in for the application's `messageComponents` map. */
var messageComponents = exports.messageComponents = {
marks: {
"interface-name": InterfaceNameMark,
"keyboard-key": KeyboardKeyMark,
"doc-link": DocLinkMark
},
objects: {
callout: CalloutObject,
"button-reference": ButtonReferenceObject,
row: RowObject,
tiles: TilesObject,
split: SplitObject
},
blocks: {
paragraph: ParagraphBlock,
"list-item": ListItemBlock,
"titled-text": TitledTextBlock
}
};
//# sourceMappingURL=MessageDialog.js.map