UNPKG

@hitachivantara/uikit-react-lab

Version:

Contributed React components for the NEXT UI Kit.

440 lines (439 loc) 14 kB
import { jsxs, Fragment, jsx } from "react/jsx-runtime"; import { useMemo, useState, useRef, useCallback } from "react"; import { css, cx } from "@emotion/css"; import Popover from "@mui/material/Popover"; import { useReactFlow, NodeResizer, NodeToolbar, Position } from "reactflow"; import { theme, HvDialogTitle, HvDialogContent, HvColorPicker, HvCheckBox, HvButton, HvSelectionList, HvListItem, HvMultiButton, HvIconButton } from "@hitachivantara/uikit-react-core"; import { FontSize, Bold, Italic, Palette, Delete, ActualSize, Fullscreen } from "@hitachivantara/uikit-react-icons"; const 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 }; const 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 }) }; const colorsToConfig = ["textColor", "backgroundColor", "borderColor"]; const fontSizes = [10, 11, 12, 14, 16, 20, 24, 32, 36, 40, 48, 64, 96, 128]; const 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, {}) } ) }) ] } ) ] }); }; export { StickyNode };