UNPKG

@hitachivantara/uikit-react-lab

Version:

Contributed React components to UI Kit by the community.

375 lines (374 loc) 10.5 kB
import { useCallback, useMemo, useRef, useState } from "react"; import { HvButton, HvCheckBox, HvColorPicker, HvDialogContent, HvDialogTitle, HvIconButton, HvListItem, HvMultiButton, HvSelectionList, theme } from "@hitachivantara/uikit-react-core"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; import { NodeResizer, NodeToolbar, Position, useReactFlow } from "reactflow"; import { ActualSize, Bold, Delete, FontSize, Fullscreen, Italic, Palette } from "@hitachivantara/uikit-react-icons"; import { css, cx } from "@emotion/css"; import Popover from "@mui/material/Popover"; //#region src/Flow/nodes/StickyNode.tsx var defaultData = { title: "Sticky Note", backgroundColor: theme.colors.warningSubtle, borderColor: theme.colors.warningSubtle, textColor: theme.colors.text, hasShadow: true, bold: false, italic: false, fontSize: 14, expanded: true, visible: true }; var classes = { root: css({ width: "100%", height: "100%", position: "relative", display: "flex", outline: "none" }), nodeToolbar: css({ backgroundColor: "transparent", borderRadius: theme.radii.full, top: 10 }), buttonsContainer: css({ padding: theme.space.xs }), textAreaRoot: css({ flex: 1, width: "100%", border: "none", background: "transparent", outline: "none" }), textAreaInput: css({ resize: "none", height: "100%", width: "100%", padding: theme.space.xs, "&:focus-visible": { outline: "none" }, paddingRight: 32 }), textAreaInputFolded: css({ resize: "none", width: "100%", padding: 0, border: "none", overflow: "hidden", minHeight: "1.5rem", height: "auto" }), colorConfig: css({ display: "flex", flexDirection: "column", gap: theme.space.sm }), folded: css({ width: 34, height: 34 }), expandButton: css({ position: "absolute", top: theme.space.xxs, right: theme.space.xxs }), fontSizes: css({ maxHeight: 160, overflowY: "auto", padding: theme.space.xs }) }; var colorsToConfig = [ "textColor", "backgroundColor", "borderColor" ]; var fontSizes = [ 10, 11, 12, 14, 16, 20, 24, 32, 36, 40, 48, 64, 96, 128 ]; var StickyNode = ({ id, selected, data = {} }) => { const mergedData = useMemo(() => ({ ...defaultData, ...data }), [data]); const [text, setText] = useState(""); const { setNodes } = useReactFlow(); const [editing, setEditing] = useState(false); const [toolbarVisible, setToolbarVisible] = useState(false); const [colorsConfigOpen, setColorsConfigOpen] = useState(false); const [fontSizeConfigOpen, setFontSizeConfigOpen] = useState(false); const [fontSize, setFontSize] = useState(mergedData.fontSize ?? 14); const colorConfigBtnRef = useRef(null); const fontSizeConfigBtnRef = useRef(null); const handleToggleBold = useCallback(() => { setNodes((nds) => nds.map((node) => { if (node.id === id) node.data = { ...node.data, bold: !node.data?.bold }; return node; })); }, [setNodes, id]); const handleToggleItalic = useCallback(() => { setNodes((nds) => nds.map((node) => { if (node.id === id) node.data = { ...node.data, italic: !node.data?.italic }; return node; })); }, [setNodes, id]); const handleChangeFontSize = useCallback((size) => { setFontSize(size); setNodes((nds) => nds.map((node) => { if (node.id === id) node.data = { ...node.data, fontSize: size }; return node; })); setFontSizeConfigOpen(false); }, [setNodes, id]); const handleToggleExpand = useCallback(() => { setNodes((nds) => nds.map((node) => { if (node.id === id) node.data = { ...node.data, expanded: !node.data?.expanded }; return node; })); }, [setNodes, id]); const handleResetColors = useCallback(() => { setNodes((nds) => nds.map((node) => { if (node.id === id) node.data = { ...node.data, backgroundColor: defaultData.backgroundColor, borderColor: defaultData.borderColor, textColor: defaultData.textColor, hasShadow: defaultData.hasShadow }; return node; })); }, [setNodes, id]); const handleDeleteSticky = useCallback(() => { setNodes((nds) => nds.filter((node) => node.id !== id)); mergedData.onDelete?.(); }, [ mergedData, setNodes, id ]); if (!mergedData.visible) return null; return /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsxs(Popover, { open: colorsConfigOpen, anchorEl: colorConfigBtnRef.current, onClose: () => { setColorsConfigOpen(false); setEditing(false); }, anchorOrigin: { vertical: "bottom", horizontal: "left" }, transformOrigin: { vertical: "top", horizontal: "left" }, children: [/* @__PURE__ */ jsx(HvDialogTitle, { children: "Customize Colors" }), /* @__PURE__ */ jsx(HvDialogContent, { children: /* @__PURE__ */ jsxs("div", { className: classes.colorConfig, children: [ colorsToConfig.map((c) => /* @__PURE__ */ jsx(HvColorPicker, { label: `${c.charAt(0).toUpperCase() + c.slice(1).replace("Color", " Color")}`, value: mergedData[c] ?? "", onChange: (color) => { setNodes((nds) => nds.map((node) => { if (node.id === id) node.data = { ...node.data, [c]: color }; return node; })); } }, c)), /* @__PURE__ */ jsx(HvCheckBox, { label: "Drop Shadow", defaultChecked: mergedData.hasShadow, onChange: (_, checked) => { setNodes((nds) => nds.map((node) => { if (node.id === id) node.data = { ...node.data, hasShadow: checked }; return node; })); } }), /* @__PURE__ */ jsx(HvButton, { variant: "secondarySubtle", onClick: handleResetColors, children: "Reset to defaults" }) ] }) })] }), /* @__PURE__ */ jsx(Popover, { open: fontSizeConfigOpen, anchorEl: fontSizeConfigBtnRef.current, onClose: () => { setFontSizeConfigOpen(false); setEditing(false); }, anchorOrigin: { vertical: "bottom", horizontal: "right" }, transformOrigin: { vertical: "top", horizontal: "right" }, children: /* @__PURE__ */ jsx(HvSelectionList, { className: classes.fontSizes, value: fontSize, onChange: (evt, newValue) => { handleChangeFontSize(newValue); }, children: fontSizes.map((size) => /* @__PURE__ */ jsx(HvListItem, { value: size, children: size }, size)) }) }), /* @__PURE__ */ jsxs("div", { className: cx(classes.root, "nowheel", !mergedData.expanded && classes.folded), style: { backgroundColor: mergedData.backgroundColor ?? theme.colors.bgContainer, boxShadow: mergedData.hasShadow ? "0 8px 12px rgba(65,65,65,0.25)" : "none", border: mergedData.borderColor ? `1px solid ${mergedData.borderColor}` : "none", fontSize: `${mergedData.fontSize ?? 14}px`, lineHeight: `${mergedData.fontSize ?? 14}px` }, children: [mergedData.expanded && /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx(NodeResizer, { isVisible: selected, minWidth: 100, minHeight: 75, lineStyle: { color: theme.colors.primary, borderStyle: "solid" }, handleStyle: { width: 6, height: 6, border: `1px solid ${theme.colors.primary}`, backgroundColor: mergedData.backgroundColor ?? "transparent", borderRadius: theme.radii.full } }), /* @__PURE__ */ jsx(NodeToolbar, { isVisible: editing || toolbarVisible || selected, position: Position.Top, className: classes.nodeToolbar, onMouseEnter: () => setToolbarVisible(true), onMouseLeave: () => setToolbarVisible(false), children: /* @__PURE__ */ jsx("div", { className: classes.buttonsContainer, children: /* @__PURE__ */ jsxs(HvMultiButton, { children: [ /* @__PURE__ */ jsx(HvIconButton, { "aria-label": "Font Size", title: "Font Size", ref: fontSizeConfigBtnRef, onClick: () => { setEditing(true); setFontSizeConfigOpen(true); }, children: /* @__PURE__ */ jsx(FontSize, {}) }), /* @__PURE__ */ jsx(HvIconButton, { "aria-label": "Bold", title: "Bold", selected: mergedData.bold, onClick: handleToggleBold, children: /* @__PURE__ */ jsx(Bold, {}) }), /* @__PURE__ */ jsx(HvIconButton, { "aria-label": "Italic", title: "Italic", selected: mergedData.italic, onClick: handleToggleItalic, children: /* @__PURE__ */ jsx(Italic, {}) }), /* @__PURE__ */ jsx(HvIconButton, { "aria-label": "Customize Colors", title: "Customize Colors", ref: colorConfigBtnRef, onClick: () => { setEditing(true); setColorsConfigOpen(true); }, children: /* @__PURE__ */ jsx(Palette, {}) }), /* @__PURE__ */ jsx(HvIconButton, { "aria-label": "Delete", title: "Delete", onClick: handleDeleteSticky, children: /* @__PURE__ */ jsx(Delete, {}) }) ] }) }) }), /* @__PURE__ */ jsxs("div", { onMouseEnter: () => setToolbarVisible(true), onMouseLeave: () => setToolbarVisible(false), className: classes.textAreaRoot, children: [/* @__PURE__ */ jsx(HvIconButton, { className: classes.expandButton, title: "Fold", onClick: handleToggleExpand, children: /* @__PURE__ */ jsx(ActualSize, {}) }), /* @__PURE__ */ jsx("textarea", { id: `sticky-textarea-${id}`, value: text || "", onChange: (e) => setText(e.target.value), className: classes.textAreaInput, placeholder: "Type here...", style: { color: mergedData.textColor ?? theme.colors.text, fontWeight: mergedData.bold ? "bold" : "normal", fontStyle: mergedData.italic ? "italic" : "normal", fontSize: mergedData.fontSize ?? "14px" }, onFocus: () => setEditing(true), onBlur: () => { setEditing(false); setColorsConfigOpen(false); } })] }) ] }), !mergedData.expanded && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(HvIconButton, { title: /* @__PURE__ */ jsx("span", { style: { whiteSpace: "pre-wrap", wordBreak: "break-word" }, children: text }), onClick: handleToggleExpand, children: /* @__PURE__ */ jsx(Fullscreen, {}) }) })] }) ] }); }; //#endregion export { StickyNode };