UNPKG

react-ui89

Version:

A collection of React components that mimic a common style of user interfaces from the late 80s and early 90s.

1,036 lines (985 loc) 43.8 kB
import * as React from 'react'; import React__default, { createContext, useContext, useState, useEffect, useRef, useMemo, useCallback } from 'react'; import DatePicker from 'react-datepicker'; import { useFloating, size, autoUpdate, useClick, useDismiss, useRole, useInteractions, FloatingPortal, FloatingFocusManager } from '@floating-ui/react'; import Timeout from 'smart-timeout'; import { createPortal } from 'react-dom'; import { toast, ToastContainer } from 'react-toastify'; var Ui89Theme; (function (Ui89Theme) { Ui89Theme["primary"] = "primary"; Ui89Theme["secondary"] = "secondary"; Ui89Theme["info"] = "info"; Ui89Theme["success"] = "success"; Ui89Theme["warning"] = "warning"; Ui89Theme["danger"] = "danger"; })(Ui89Theme || (Ui89Theme = {})); var Ui89Look; (function (Ui89Look) { Ui89Look["main"] = "main"; Ui89Look["side"] = "side"; })(Ui89Look || (Ui89Look = {})); const Ui89OverrideContext = createContext({}); const Ui89OverrideProvider = ({ routerPush, children, }) => { return (React__default.createElement(Ui89OverrideContext.Provider, { value: { routerPush } }, children)); }; const useUi89Overrides = () => { const context = useContext(Ui89OverrideContext); if (context === undefined) { throw new Error("useUi89Overrides must be used within a Ui89OverrideProvider"); } return context; }; function Ui89BreadcrumbsItem({ index, item, onSelect, }) { const style = { "--ui89-index": index }; item.url !== undefined ? "a" : "div"; const overrides = useUi89Overrides(); const onClick = (e) => { if (item.url !== undefined) { if (item.url.startsWith("/")) { if (overrides.routerPush !== undefined) { e.preventDefault(); overrides.routerPush(item.url); } } } }; return (React__default.createElement("a", { className: `ui-89-reset-a ui89-breadcrumbs__item`, href: item.url, style: style, onClick: onClick }, React__default.createElement("div", { className: "ui89-breadcrumbs__item__background" }), item.label)); } function Ui89Breadcrumbs({ theme = Ui89Theme.primary, items, onSelect, }) { return (React__default.createElement("div", { className: `ui89-breadcrumbs ui89-typo-special ui89-chosen-theme-${theme}` }, [...items.entries()].reverse().map(([index, item]) => (React__default.createElement(Ui89BreadcrumbsItem, { key: index, index: index, item: item, onSelect: onSelect }))))); } function HoverShadow({ children, }) { return (React__default.createElement("span", { className: "ui89-hover-shadow" }, React__default.createElement("span", { className: "ui89-hover-shadow__bottom" }), React__default.createElement("span", { className: "ui89-hover-shadow__right" }), children)); } var Ui89ButtonPropsSize; (function (Ui89ButtonPropsSize) { Ui89ButtonPropsSize["standard"] = "standard"; Ui89ButtonPropsSize["square"] = "square"; })(Ui89ButtonPropsSize || (Ui89ButtonPropsSize = {})); var Ui89ButtonPropsType; (function (Ui89ButtonPropsType) { Ui89ButtonPropsType["submit"] = "submit"; Ui89ButtonPropsType["reset"] = "reset"; })(Ui89ButtonPropsType || (Ui89ButtonPropsType = {})); function Ui89Button({ theme = Ui89Theme.primary, size = Ui89ButtonPropsSize.standard, type, block, onClick, href, children, autoDisableOnClick = true, disabled, activated, }) { const overrides = useUi89Overrides(); const [clicking, setClicking] = useState(false); let localDisabled = disabled || (autoDisableOnClick && clicking); async function onAnchorClick(e) { if (localDisabled) { // The anchor tag does not support the disabled attribute so we do this. return; } if (clicking) { // No double clicking allowed. return; } try { setClicking(true); if (href !== undefined) { if (href.startsWith("/")) { if (overrides.routerPush !== undefined) { e.preventDefault(); overrides.routerPush(href); } } } } finally { setClicking(false); } } async function onButtonClick(e) { if (localDisabled) { // The anchor tag does not support the disabled attribute so we do this. return; } if (clicking) { // No double clicking allowed. return; } try { setClicking(true); if (onClick === undefined) { // No handler. return; } await onClick(); } finally { setClicking(false); } } let containerClass = ["ui89-button", `ui89-button--size-${size}`].join(" "); let buttonClass = [ "ui89-button__button", "ui89-typo-special", `ui89-chosen-theme-${theme}`, activated ? "ui89-button__button--active" : undefined, block ? "ui89-button__button--block" : undefined, disabled ? "ui89-button__button--disabled" : undefined, clicking ? "ui89-button__button--active" : undefined, ].join(" "); return (React__default.createElement("div", { className: containerClass }, React__default.createElement(HoverShadow, null, href ? (React__default.createElement("a", { className: "ui-89-reset-a", role: "button", href: href, onClick: onAnchorClick }, React__default.createElement("span", { className: buttonClass }, children))) : (React__default.createElement("button", { className: "ui-89-reset-button", type: type || "button", onClick: onButtonClick, disabled: localDisabled }, React__default.createElement("span", { className: buttonClass }, children)))))); } function Ui89Card({ topLeftCenter, topCenter, children, }) { const hasTopContent = topLeftCenter || topCenter; return (React__default.createElement("div", { className: `ui89-card ${hasTopContent ? "ui89-card--has-top" : ""}` }, React__default.createElement("div", { className: "ui89-card__inside" }, topLeftCenter && (React__default.createElement("div", { className: "ui89-card__top-left-center" }, topLeftCenter)), topCenter && React__default.createElement("div", { className: "ui89-card__top-center" }, topCenter), hasTopContent && React__default.createElement("div", { className: "space-vertical-1" }), children, hasTopContent && React__default.createElement("div", { className: "space-vertical-1" })))); } function Ui89CardHorizontalConnection({ children, overflow, }) { return (React__default.createElement("div", { className: `ui89-card__horizontal-connection ${overflow ? "ui89-card__horizontal-connection--overflow" : ""}` }, children)); } function Ui89DateTimePicker(props) { function datepickerOnChange(value) { if (props.onChange) { props.onChange(value); } } return (React__default.createElement("span", { className: "ui89-date-time-picker" }, React__default.createElement(DatePicker, { className: ["ui89-input-box", "ui89-typo-normal"].join(" "), calendarClassName: "ui89-typo-normal", showTimeSelect: true, dateFormat: "MM/dd/yyyy HH:mm:ss", timeFormat: "HH:mm", selected: props.value, onChange: datepickerOnChange, popperPlacement: "bottom-start" }))); } const TimeAnimation = ({ children }) => { const [currentTime, setCurrentTime] = useState(new Date()); useEffect(() => { const tick = () => { const now = new Date(); setCurrentTime(now); // Calculate time until the next exact second const msUntilNextSecond = 1000 - now.getMilliseconds(); setTimeout(tick, msUntilNextSecond); }; // Start the first tick const msUntilNextSecond = 1000 - currentTime.getMilliseconds(); const timeoutId = setTimeout(tick, msUntilNextSecond); return () => clearTimeout(timeoutId); }, []); return React__default.createElement(React__default.Fragment, null, children({ now: currentTime })); }; function dateFormat(date, format) { const placeholders = { YYYY: date.getFullYear().toString(), MM: (date.getMonth() + 1).toString().padStart(2, "0"), DD: date.getDate().toString().padStart(2, "0"), HH: date.getHours().toString().padStart(2, "0"), mm: date.getMinutes().toString().padStart(2, "0"), ss: date.getSeconds().toString().padStart(2, "0"), hh: (date.getHours() % 12 || 12).toString().padStart(2, "0"), A: date.getHours() >= 12 ? "PM" : "AM", }; // Replace placeholders in the format string let formattedDate = format; for (const [key, value] of Object.entries(placeholders)) { formattedDate = formattedDate.replace(key, value); } return formattedDate; } function Ui89DigitalClock({ format = "HH:mm:ss", }) { function render({ now }) { return dateFormat(now, format); } return (React__default.createElement("span", { className: "ui89-typo-special" }, React__default.createElement(TimeAnimation, null, render))); } function Ui89HighlightText({ theme, block, children, }) { return (React__default.createElement("span", { className: `ui89-highlight-text ui89-chosen-theme-${theme} ${block ? "ui89-highlight-text--block" : null}` }, children)); } var Ui89HrPropsLook; (function (Ui89HrPropsLook) { Ui89HrPropsLook["straight"] = "straight"; Ui89HrPropsLook["dotted"] = "dotted"; Ui89HrPropsLook["dashed"] = "dashed"; Ui89HrPropsLook["double"] = "double"; })(Ui89HrPropsLook || (Ui89HrPropsLook = {})); function Ui89Hr({ look = "straight", theme }) { return (React__default.createElement("div", { className: `ui-89-hr ${`ui-89-hr--${look}`} ${theme !== undefined ? "ui-89-hr--use-theme" : ""} ${theme !== undefined ? `ui89-chosen-theme-${theme}` : ""}` }, React__default.createElement("div", { className: "ui-89-hr__double" }))); } function Ui89InputCheckBox(props) { function toggle() { if (props.onChange) { props.onChange(!props.value); } } return (React__default.createElement("span", { className: "ui89-input-check-box", onClick: toggle }, React__default.createElement("span", { className: "ui89-input-check-box__x" }, props.value ? React__default.createElement(React__default.Fragment, null, "X") : React__default.createElement(React__default.Fragment, null, "\u00A0")))); } function Ui89InputCheckText(props) { function toggle() { if (props.onChange) { props.onChange(!props.value); } } return (React__default.createElement("span", { className: "ui89-input-check-text", onClick: toggle }, props.value ? "[X]" : "[ ]")); } function Ui89InputFileUpload({ value, onChange, }) { const inputRef = useRef(null); function implOnChange(e) { if (!onChange) { return; } if (e.target.files === null) { onChange(null); return; } if (e.target.files.length === 0) { onChange(null); return; } onChange(e.target.files[0]); } function onClick() { if (inputRef.current === null) { return; } inputRef.current.click(); } return (React__default.createElement("div", null, React__default.createElement("input", { ref: inputRef, className: "ui89-typo-special", type: "file", onChange: implOnChange, hidden: true }), value ? (React__default.createElement("div", { className: "ui89-input-file-upload" }, React__default.createElement(Ui89Button, { onClick: onClick }, "Change"), React__default.createElement("span", { className: `ui89-input-file-upload__info ui89-text-single-line ui89-text-single-line--ellipsis-left`, title: value.name }, value.name))) : (React__default.createElement(Ui89Button, { onClick: onClick }, "Upload")))); } const useResizeObserver = (ref) => { const [size, setSize] = useState({ width: 0, height: 0 }); useEffect(() => { const observer = new ResizeObserver((entries) => { for (let entry of entries) { setSize({ width: entry.borderBoxSize?.[0]?.inlineSize ?? entry.contentRect.width, height: entry.borderBoxSize?.[0]?.blockSize ?? entry.contentRect.height, }); } }); if (ref.current) { observer.observe(ref.current); } return () => { observer.disconnect(); }; }, [ref.current]); return { size, }; }; const useScrollYPosition = (ref) => { const [scrollY, setScrollY] = useState(0); const ticking = useRef(false); const observer = useRef(null); useEffect(() => { const element = ref.current; if (!element) return; const handleScroll = () => { if (!ticking.current) { ticking.current = true; requestAnimationFrame(() => { setScrollY(element.scrollTop); ticking.current = false; }); } }; element.addEventListener("scroll", handleScroll, { passive: true }); observer.current = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.type === "childList" && ref.current !== element) { element.removeEventListener("scroll", handleScroll); ref.current?.addEventListener("scroll", handleScroll, { passive: true, }); } } }); observer.current.observe(document.body, { childList: true, subtree: true }); return () => { element.removeEventListener("scroll", handleScroll); observer.current?.disconnect(); observer.current = null; }; }, [ref]); return scrollY; }; function popAnyEntry(map) { const iterator = map.keys().next(); if (!iterator.done) { const key = iterator.value; const value = map.get(key); map.delete(key); return value; } return undefined; } /** * Virtualization at row level. Great for long lists. * * Can be used to implement table components with few columns. */ const Ui89VirtualList = React__default.memo((props) => { const keyCounter = useRef(0); const scrollContainer = useRef(null); const scrollAreaContainer = useRef(null); const { size } = useResizeObserver(scrollContainer); const scrollY = useScrollYPosition(scrollContainer); const rowHeight = props.rowHeight ?? 50; const totalHeight = rowHeight * props.rows.length; const [visibleRows, setVisibleRows] = useState(new Map()); function updateVisibleRows(cache) { if (size.height === 0) { setVisibleRows(new Map()); return; } const firstIndex = Math.max(0, Math.floor(scrollY / rowHeight) - 1); const length = Math.min(props.rows.length - firstIndex, Math.ceil(size.height / rowHeight) + 2); const deletedRows = new Map(cache); // Must find the ones that are no longer visible. for (let index = firstIndex; index < firstIndex + length; index++) { let row = props.rows[index]; let key = props.getRowKey ? props.getRowKey(row) : String(index); deletedRows.delete(key); } const newVisibleRows = new Map(); for (let index = firstIndex; index < firstIndex + length; index++) { let row = props.rows[index]; let key = props.getRowKey ? props.getRowKey(row) : String(index); let existingRow = cache.get(key); if (existingRow !== undefined) { if (existingRow.row === row) ; else { // Data has changed, must update render. existingRow.render = props.renderRow({ index, row }); } newVisibleRows.set(key, existingRow); continue; } let oldRow = popAnyEntry(deletedRows); if (oldRow !== undefined) { oldRow.index = index; oldRow.row = row; oldRow.userKey = key; oldRow.render = props.renderRow({ index, row }); oldRow.style = { transform: `translateY(${index * rowHeight}px)`, height: `${rowHeight}px`, }; newVisibleRows.set(key, oldRow); } else { // New row. newVisibleRows.set(key, { index, row, key: keyCounter.current++, userKey: key, render: props.renderRow({ index, row }), style: { transform: `translateY(${index * rowHeight}px)`, height: `${rowHeight}px`, }, }); } } setVisibleRows(newVisibleRows); } useEffect(() => { // The renderRow function may have a reference to the rows array. if we // do not throw away our cache we risk leaving rows referencing // stale data. updateVisibleRows(new Map()); }, [props.rows, props.renderRow]); useEffect(() => { updateVisibleRows(visibleRows); }, [scrollY, size.height]); const orderedVisibleRows = useMemo(() => { return Array.from(visibleRows.values()).sort((a, b) => a.index - b.index); }, [visibleRows]); return (React__default.createElement("div", { ref: scrollContainer, className: "ui89-virtual-list", style: { maxHeight: props.maxHeight } }, React__default.createElement("div", { ref: scrollAreaContainer, className: "ui89-virtual-list__scroll-area", style: { height: `${totalHeight}px` } }, orderedVisibleRows.map((visibleRow) => (React__default.createElement("div", { key: visibleRow.userKey, className: "ui89-virtual-list__row", style: visibleRow.style }, visibleRow.render)))))); }); function Ui89Scene({ look = Ui89Look.main, children }) { return (React__default.createElement("div", { className: `ui89-scene ui-89-look-${look} ui89-typo-normal ui89-scrollbar` }, children)); } /** * This is a very performant and customizable dropdown selector that * allows you to choose from a list of options. */ function Ui89InputSelect(props) { const [isOpen, setIsOpen] = useState(false); const { refs, floatingStyles, context } = useFloating({ open: isOpen, onOpenChange: setIsOpen, middleware: [ size({ apply({ availableWidth, availableHeight, elements }) { let width = elements.reference.getBoundingClientRect().width; // Change styles, e.g. Object.assign(elements.floating.style, { width: `${width}px`, maxHeight: `${Math.max(0, availableHeight)}px`, }); }, }), ], whileElementsMounted: autoUpdate, placement: "bottom", strategy: "fixed", }); const click = useClick(context); const dismiss = useDismiss(context); const role = useRole(context); const { getReferenceProps, getFloatingProps } = useInteractions([ click, dismiss, role, ]); const getOptionKey = useMemo(() => { return props.getOptionKey ?? ((option) => option); }, [props.getOptionKey]); const options = useMemo(() => { if (props.options === undefined) { return []; } return props.options; }, [props.options]); useMemo(() => { return new Map(options.map((option) => [getOptionKey(option), option])); }, [options]); function isOptionSelected(option) { if (props.value === undefined) { // Definitely not selected. return false; } return getOptionKey(option) === getOptionKey(props.value); } function optionTitle(option) { return String(option); } const selectOption = useCallback((option) => { if (props.onChange !== undefined) { props.onChange(option); } setIsOpen(false); }, [props.onChange]); const renderOption = useCallback(({ row }) => { const isSelected = isOptionSelected(row); return (React__default.createElement("div", { className: [ "ui89-input-select__menu__item", isSelected ? "ui89-input-select__menu__item--selected" : null, ].join(" "), title: optionTitle(row), key: getOptionKey(row), onClick: () => selectOption(row) }, props.renderOption !== undefined ? props.renderOption(row) : row)); }, [options, selectOption]); return (React__default.createElement("div", { className: "ui89-input-select" }, React__default.createElement("div", { ref: refs.setReference, className: [ "ui89-input-box", "ui89-input-box--unselectable", "ui89-input-box--clickable", "ui89-text-single-line", ].join(" "), tabIndex: 0, title: props.value !== undefined ? optionTitle(props.value) : undefined, ...getReferenceProps() }, props.value !== undefined ? (React__default.createElement(React__default.Fragment, null, props.renderOption !== undefined ? props.renderOption(props.value) : props.value)) : (React__default.createElement(React__default.Fragment, null, "Select..."))), isOpen && (React__default.createElement(FloatingPortal, null, React__default.createElement(FloatingFocusManager, { context: context, modal: false }, React__default.createElement("div", { ref: refs.setFloating, className: "ui89-input-select__menu", style: floatingStyles }, React__default.createElement(Ui89Scene, null, options.length > 0 ? (React__default.createElement(Ui89VirtualList, { maxHeight: "300px", rowHeight: props.optionHeight ?? 32, rows: options, renderRow: renderOption })) : (React__default.createElement("div", { className: [ "ui89-input-select__menu__item", "ui89-input-select__menu__item--disabled", ].join(" ") }, "<empty>"))))))))); } let uniqueId = 0; function throttledTimeout() { const id = String(uniqueId++); let callback; return { call(delay, f) { callback = f; if (Timeout.pending(id)) { Timeout.restart(id); } else { Timeout.set(id, () => callback(), delay); } }, /** * If there is a call that has been scheduled, remove it from the queue. */ abort() { Timeout.clear(id, true); }, }; } function useUpdatedRef(value) { const valueRef = useRef(value); useEffect(() => { valueRef.current = value; }, [value]); return valueRef; } function useDelayedOnChange(props) { const valueRef = useUpdatedRef(props.value); const onChangeRef = useUpdatedRef(props.onChange); const [intermediateValue, setIntermediateValue] = useState(props.defaultValue || props.value); useEffect(() => { stateRef.current.setValue(props.value); }, [props.value]); function callOnChange() { let newVal = stateRef.current.value; if (props.filter !== undefined) { newVal = props.filter(newVal); } if (newVal !== valueRef.current) { onChangeRef.current?.call(null, newVal); } return newVal; } class StateUnknown { state = "unknown"; value; throttledTimeout; constructor() { this.throttledTimeout = throttledTimeout(); } setValue(newVal) { setIntermediateValue(newVal); } onChange(newVal) { setIntermediateValue(newVal); stateRef.current.throttledTimeout.call(300, callOnChange); } onFocus() { let newState = new StateFocus(); newState.value = stateRef.current.value; setState(newState); } onBlur() { } } class StateFocus { state = "focus"; value; throttledTimeout; constructor() { this.throttledTimeout = throttledTimeout(); } setValue(newVal) { // Ignore. } onChange(newVal) { setIntermediateValue(newVal); stateRef.current.throttledTimeout.call(300, callOnChange); } onFocus() { } onBlur() { let newVal = callOnChange(); setIntermediateValue(newVal); let newState = new StateUnknown(); newState.value = newVal; setState(newState); } } const [state, setState] = useState(() => { let newState = new StateUnknown(); newState.value = intermediateValue; return newState; }); const stateRef = useUpdatedRef(state); stateRef.current.value = intermediateValue; return state; } function Ui89InputText({ value, placeholder, autoTrim = true, onChange, onTyping, onFocus, onBlur, }) { const delayedState = useDelayedOnChange({ defaultValue: "", value, onChange, filter(value) { if (autoTrim) { if (typeof value === "string") { value = value.replace(/\s+/g, " ").trim(); } } return value; }, }); return (React__default.createElement("div", null, React__default.createElement("input", { className: `ui89-input-box ui89-typo-special`, type: "text", value: delayedState.value, onChange: (e) => delayedState.onChange(e.target.value), onBlur: delayedState.onBlur, onFocus: delayedState.onFocus, placeholder: placeholder }))); } function stringRemoveAllWhitespace(str) { return str.replace(/\s+/g, ""); } function isTextNumber(text) { return /\b\d+(\.\d+)?\b/.test(text); } function displayText(value, emptyValue) { if (value === undefined) { // No idea how to display this. return ""; } else if (value === emptyValue) { // Display emptiness. return ""; } else if (isNaN(value)) { // No idea what to display. return ""; } return value.toString(); } function Ui89InputTextNumber({ /** * The value that is emitted when the input is emptied. */ emptyValue = null, value, min, max, onChange, precision, }) { const wrappedValue = useMemo(() => { return displayText(value, emptyValue); }, [value, emptyValue]); function implOnChange(value) { if (onChange === undefined) { return; } if (value === "") { // Use empty value. onChange(emptyValue); return; } value = stringRemoveAllWhitespace(value); if (!isTextNumber(value)) { // We end here. return; } const numberValue = Number(value); if (min !== undefined) { if (numberValue <= min) { value = String(min); } } if (max !== undefined) { if (numberValue >= max) { value = String(max); } } onChange(value); } return React__default.createElement(Ui89InputText, { value: wrappedValue, onChange: implOnChange }); } function Ui89InputNumber(props) { const wrappedValue = useMemo(() => { if (props.value !== undefined && props.value !== null) { return Number(props.value); } else { return undefined; } }, [props.value]); function wrappedOnChange(value) { if (props.onChange === undefined) { return; } if (value === props.emptyValue) { // Pass along. props.onChange(value); } else { if (value !== undefined && value !== null) { props.onChange(Number(value)); } else { props.onChange(null); } } } return (React__default.createElement(Ui89InputTextNumber, { emptyValue: props.emptyValue, value: wrappedValue, onChange: wrappedOnChange, min: props.min, max: props.max, precision: props.precision })); } function Ui89InputPassword({ value, placeholder, onChange, }) { const [intermediateValue, setIntermediateValue] = useState(value ?? ""); const implOnChange = (e) => { const newValue = e.target.value; setIntermediateValue(newValue); if (onChange) { onChange(newValue); } }; return (React__default.createElement("div", null, React__default.createElement("input", { type: "password", className: `ui89-input-box ui89-typo-special`, role: "textbox", value: intermediateValue, onChange: implOnChange, placeholder: placeholder }))); } function Ui89LinkBase(props) { const overrides = useUi89Overrides(); const [clicking, setClicking] = useState(false); let localDisabled = props.disabled || ((props.autoDisableOnClick ?? true) && clicking); async function onClick(e) { if (localDisabled) { // The anchor tag does not support the disabled attribute so we do this. return; } try { setClicking(true); if (props.onClick !== undefined) { // A function takes over control. e.preventDefault(); await props.onClick(); } else if (props.href !== undefined) { if (props.href.startsWith("/")) { if (overrides.routerPush !== undefined) { e.preventDefault(); overrides.routerPush(props.href); } } } else { // Do nothing. e.preventDefault(); } } finally { setClicking(false); } } return (React__default.createElement("a", { className: `ui-89-reset-a ${props.className}`, role: "link", href: props.href, onClick: onClick }, props.children)); } function Ui89LinkStealth(props) { return React__default.createElement(Ui89LinkBase, { className: "ui89-link-stealth", ...props }); } function Ui89LinkUnderline(props) { return React__default.createElement(Ui89LinkBase, { className: "ui89-link-underline", ...props }); } function Ui89MenuBar({ items }) { return (React__default.createElement("div", { className: `ui89-menu-bar ui89-typo-special ui89-scrollbar` }, items.map((item, index) => { function onNativeClick() { if (item.onClick !== undefined) { item.onClick(); } } return (React__default.createElement("div", { key: index, className: "ui89-menu-bar__item", onClick: onNativeClick }, item.label)); }))); } function GridExpandTrick({ children, }) { return React__default.createElement("span", { className: "ui89-grid-expand-trick" }, children); } function ScrollContainer({ children, }) { return React__default.createElement("span", { className: "ui89-scroll-container" }, children); } const portalRoot = typeof document !== "undefined" ? document.body : null; function Ui89ModalDialog({ open, size = "medium", children, topCenter, onRequestClose, }) { const dialogClass = useMemo(() => { return ["ui89-modal-dialog", open ? "ui89-modal-dialog--open" : ""].join(" "); }, [size, open]); const dialogBoxClass = useMemo(() => { return [ "ui89-modal-dialog__box", `ui89-modal-dialog__box--size-${size}`, ].join(" "); }, [size, open]); function onClickBackdrop() { if (onRequestClose !== undefined) { onRequestClose(); } } const vdom = (React__default.createElement("div", { className: dialogClass, role: "dialog", style: { zIndex: 1 } }, React__default.createElement("div", { className: "ui89-modal-dialog__backdrop", role: "presentation", onClick: onClickBackdrop }), React__default.createElement("div", { className: dialogBoxClass }, React__default.createElement("div", { className: "ui89-modal-dialog__spacer" }), React__default.createElement(HoverShadow, null, React__default.createElement(GridExpandTrick, null, React__default.createElement(Ui89Scene, null, React__default.createElement(Ui89Card, { topCenter: topCenter }, React__default.createElement(ScrollContainer, null, children)))))))); return portalRoot !== null ? createPortal(vdom, portalRoot) : vdom; } function Ui89NameValuePair({ name, value, leftMaxWidth, }) { return (React__default.createElement("div", { className: "ui-89-name-value-pair" }, React__default.createElement("div", { className: "ui-89-name-value-pair__name-wrapper", style: { maxWidth: `${leftMaxWidth}px` } }, React__default.createElement("div", { className: "ui-89-name-value-pair__name" }, name), React__default.createElement("div", { className: "ui-89-name-value-pair__dots" })), React__default.createElement("div", { className: "ui-89-name-value-pair__value" }, value))); } const SvgShortcut = (props) => /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: 100, height: 100, viewBox: "0 0 100 100", ...props }, /* @__PURE__ */ React.createElement("rect", { x: 0, y: 0, width: 100, height: 100, fill: "white" }), /* @__PURE__ */ React.createElement("line", { x1: 80, y1: 80, x2: 20, y2: 20, stroke: "black", strokeWidth: 15 }), /* @__PURE__ */ React.createElement("line", { x1: 20, y1: 20, x2: 50, y2: 20, stroke: "black", strokeWidth: 15, strokeLinecap: "round" }), /* @__PURE__ */ React.createElement("line", { x1: 20, y1: 20, x2: 20, y2: 50, stroke: "black", strokeWidth: 15, strokeLinecap: "round" })); function Ui89SpaceVertical({ gap = 1 }) { const style = { paddingTop: `calc(var(--ui89-safe-space) * ${gap})`, }; return React__default.createElement("div", { style: style }); } function Ui89Shortcut({ imageUrl, label, onClick = () => { }, }) { function onNativeClick() { onClick(); } return (React__default.createElement("div", { className: "ui89-shortcut", onClick: onNativeClick }, React__default.createElement("div", { className: "ui89-shortcut__image-container" }, React__default.createElement("img", { className: "ui89-shortcut__image", src: imageUrl }), React__default.createElement("div", { className: "ui89-shortcut__shortcut-icon-container" }, React__default.createElement(SvgShortcut, { className: "ui89-shortcut__shortcut-icon", width: 16, height: 16 }))), React__default.createElement(Ui89SpaceVertical, { gap: 1 }), React__default.createElement("div", { className: `ui89-shortcut__label ui89-typo-small-bold`, onClick: onNativeClick }, label))); } function Ui89SpacePadding(props) { const gap = props.gap ?? 1; const style = { paddingTop: `calc(var(--ui89-safe-space) * ${props.gapTop ?? props.gapVertical ?? gap})`, paddingRight: `calc(var(--ui89-safe-space) * ${props.gapRight ?? props.gapHorizontal ?? gap})`, paddingBottom: `calc(var(--ui89-safe-space) * ${props.gapBottom ?? props.gapVertical ?? gap})`, paddingLeft: `calc(var(--ui89-safe-space) * ${props.gapLeft ?? props.gapHorizontal ?? gap})`, }; return React__default.createElement("div", { style: style }, props.children); } function Ui89Tabs({ selected, onChange = () => { }, options = [], stretch, }) { function handleOnChange(value) { onChange(value); } return (React__default.createElement("div", { className: ["ui89-tabs", stretch ? "ui89-tabs--stretch" : ""].join(" ") }, options.map((option) => (React__default.createElement("div", { className: [ "ui89-tabs__item", "ui89-typo-small-bold", selected === option.value ? "ui89-tabs__item--selected" : "", ].join(" "), key: option.value, onClick: () => handleOnChange(option.value) }, option.label))))); } function Ui89TabbedCard({ selected, onChange, options = [], }) { const selectedItem = useMemo(() => { return options.find((item) => item.value === selected) ?? null; }, [selected, options]); const render = useMemo(() => { return selectedItem !== null ? selectedItem.render : () => React__default.createElement(React__default.Fragment, null); }, [selectedItem]); const renderKey = useMemo(() => { return selectedItem !== null ? selectedItem.value : undefined; }, [selectedItem]); return (React__default.createElement(Ui89Card, { topCenter: React__default.createElement(Ui89Tabs, { selected: selected, options: options, onChange: onChange }) }, React__default.createElement(React__default.Fragment, { key: renderKey }, render()))); } function Ui89TagBox({ theme, children }) { return (React__default.createElement("div", { className: `ui89-tag-box ui89-typo-special ui89-chosen-theme-${theme}` }, children)); } function Ui89ThemeBackground({ theme = Ui89Theme.primary, children, }) { return (React__default.createElement("div", { className: `ui89-theme-background ui89-chosen-theme-${theme}` }, children)); } function Ui89TitleBox({ children }) { return (React__default.createElement("div", { className: `ui89-title-box ui89-typo-special` }, React__default.createElement("div", { className: `ui89-title-box__inside ui89-text-single-line` }, children))); } function Ui89TitleUnderline({ children }) { return (React__default.createElement("div", { className: `ui89-title-underline ui89-typo-special` }, React__default.createElement("div", { className: `ui89-title-underline__inside ui89-text-single-line` }, children))); } function useUi89Toaster() { return { toast(content, options = { theme: Ui89Theme.primary }) { const classNames = ["ui89-toaster", "ui89-typo-normal"]; if (options.theme !== undefined) { classNames.push(`ui89-chosen-theme-${options.theme}`); } let autoClose = 5000; if (options.duration !== undefined) { autoClose = options.duration; } if (options.autoClose !== undefined) { if (!options.autoClose) { autoClose = false; } } return toast(() => content, { className: classNames.join(" "), type: "default", autoClose, closeButton: false, hideProgressBar: true, closeOnClick: true, }); }, }; } function Ui89Toaster() { return React__default.createElement(ToastContainer, null); } var Ui89VirtualTablePropsColumnAlign; (function (Ui89VirtualTablePropsColumnAlign) { Ui89VirtualTablePropsColumnAlign["left"] = "left"; Ui89VirtualTablePropsColumnAlign["right"] = "right"; Ui89VirtualTablePropsColumnAlign["center"] = "center"; })(Ui89VirtualTablePropsColumnAlign || (Ui89VirtualTablePropsColumnAlign = {})); const Ui89VirtualTable = React__default.memo((props) => { const rows = useMemo(() => { let rows = props.rows !== undefined ? props.rows.slice() : []; rows.unshift(undefined); return rows; }, [props.rows]); const columns = useMemo(() => { return props.columns !== undefined ? props.columns : []; }, [props.columns]); const rowHeight = props.rowHeight ?? 20; function getColumnWidth(index) { return columns[index].width ?? 100; } function getColumnHorizontalOffset(columnIndex) { let offset = 0; for (let i = 0; i < columnIndex; i++) { offset += getColumnWidth(i); } return offset; } function isLastColumn(columnIndex) { return columnIndex === columns.length - 1; } function getRowClass(rowIndex) { const classes = ["ui89-virtual-table__row"]; if (rowIndex === 0) { classes.push("ui89-virtual-table__row--first"); classes.push("ui89-typo-normal-bold"); } if (rowIndex === rows.length) { classes.push("ui89-virtual-table__row--last"); } return classes.join(" "); } function getColumnClass(columnIndex) { const halign = columns[columnIndex].halign ?? "left"; const classes = [ "ui89-virtual-table__cell", `ui89-virtual-table__cell--halign-${halign}`, ]; if (columnIndex === 0) { classes.push("ui89-virtual-table__cell--column-first"); } if (isLastColumn(columnIndex)) { classes.push("ui89-virtual-table__cell--column-last"); } return classes.join(" "); } /** * The width of an entire row. */ function rowWidth() { return getColumnHorizontalOffset(columns.length); } const renderRow = useCallback(({ index, row }) => { return (React__default.createElement("div", { className: getRowClass(index), style: { minWidth: rowWidth() + "px", height: "100%" } }, columns.map((column, columnIndex) => { return (React__default.createElement("div", { key: columnIndex, className: getColumnClass(columnIndex), style: { top: 0, height: "100%", width: getColumnWidth(columnIndex) + "px", left: getColumnHorizontalOffset(columnIndex) + "px", } }, index === 0 ? columns[columnIndex].renderHeader ? columns[columnIndex].renderHeader({ index: columnIndex, column: column, }) : "" : columns[columnIndex].renderBody({ index: index - 1, row: row, }))); }))); }, [columns, rowHeight]); return (React__default.createElement(React__default.Fragment, null, rows.length > 1 ? (React__default.createElement(Ui89VirtualList, { maxHeight: props.maxHeight, rows: rows, rowHeight: rowHeight, renderRow: renderRow })) : (React__default.createElement("div", { className: "ui89-virtual-table__empty" }, React__default.createElement(Ui89TagBox, { theme: "warning" }, "Empty"))))); }); export { Ui89Breadcrumbs, Ui89Button, Ui89Card, Ui89CardHorizontalConnection, Ui89DateTimePicker, Ui89DigitalClock, Ui89HighlightText, Ui89Hr, Ui89InputCheckBox, Ui89InputCheckText, Ui89InputFileUpload, Ui89InputNumber, Ui89InputPassword, Ui89InputSelect, Ui89InputText, Ui89InputTextNumber, Ui89LinkStealth, Ui89LinkUnderline, Ui89Look, Ui89MenuBar, Ui89ModalDialog, Ui89NameValuePair, Ui89OverrideProvider, Ui89Scene, Ui89Shortcut, Ui89SpacePadding, Ui89SpaceVertical, Ui89TabbedCard, Ui89Tabs, Ui89TagBox, Ui89Theme, Ui89ThemeBackground, Ui89TitleBox, Ui89TitleUnderline, Ui89Toaster, Ui89VirtualList, Ui89VirtualTable, useUi89Toaster }; //# sourceMappingURL=index.js.map