react-ui89
Version:
A collection of React components that mimic a common style of user interfaces from the late 80s and early 90s.
1,059 lines (1,008 loc) • 44.6 kB
JavaScript
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));
}
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() { }
onConfirm() {
callOnChange();
}
}
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);
}
onConfirm() {
callOnChange();
}
}
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, onKeyDown: (e) => {
if (e.key === "Enter") {
delayedState.onConfirm();
}
}, placeholder: placeholder })));
}
/**
* This is a very performant and customizable dropdown selector that
* allows you to choose from a list of options.
*/
function Ui89InputSelect(props) {
const [search, setSearch] = useState("");
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: `${availableWidth}px`,
maxWidth: `${width}px`,
maxHeight: `${Math.max(0, availableHeight)}px`,
});
},
}),
],
whileElementsMounted: autoUpdate,
placement: "bottom-start",
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]);
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]);
useEffect(() => {
if (isOpen) {
setSearch("");
}
}, [isOpen]);
useEffect(() => {
if (isOpen) {
if (props.onSearch !== undefined) {
props.onSearch(search);
}
}
}, [search]);
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,
props.search && (React__default.createElement(Ui89InputText, { placeholder: "Search...", value: search, onChange: setSearch })),
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>")))))))));
}
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