@woobee/ui 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import React15, { forwardRef, useRef, useEffect, createContext, useState, useImperativeHandle, useContext, useReducer, useCallback } from 'react';
2
+ import React16, { forwardRef, useRef, useEffect, createContext, useState, useImperativeHandle, useContext, useReducer, useCallback, useMemo } from 'react';
3
3
  import { createPortal } from 'react-dom';
4
4
  import { CSSTransition } from 'react-transition-group';
5
5
 
@@ -29,6 +29,7 @@ function Button({
29
29
  leftIcon = null,
30
30
  rightIcon = null,
31
31
  compact = false,
32
+ darkMode = false,
32
33
  ...props
33
34
  }) {
34
35
  return /* @__PURE__ */ jsxs(
@@ -45,11 +46,20 @@ function Button({
45
46
  "px-5 py-3 text-base": size === "big",
46
47
  "px-5 py-3 text-lg font-medium": size === "huge",
47
48
  "!px-1 !py-0.5": compact,
48
- "border bg-brown-500 dark:bg-primary-500 dark:hover:bg-primary-600 border-transparent text-white dark:text-black": variant === "primary",
49
- "text-gray-700 bg-white dark:bg-charcoal-800 dark:hover:bg-charcoal-700 dark:text-charcoal-100 border border-gray-300 dark:border-charcoal-700 hover:text-gray-500": variant === "secondary",
50
- "!rounded-md text-left w-full text-gray-700 bg-white dark:bg-charcoal-800 dark:hover:bg-charcoal-700 dark:text-charcoal-100 border border-gray-200 dark:border-charcoal-700 hover:bg-gray-50 hover:text-gray-500": variant === "form-input",
51
- "hover:text-brown-500 dark:text-charcoal-100 dark:hover:text-primary-500 !shadow-none": variant === "flat",
52
- "!text-gray-300 hover:!text-gray-400 dark:!text-charcoal-400": !children,
49
+ "border bg-brown-500 border-transparent text-white": variant === "primary" && !darkMode,
50
+ "dark:bg-primary-500 dark:hover:bg-primary-600 dark:text-black": variant === "primary" && !darkMode,
51
+ "border bg-primary-500 hover:bg-primary-600 border-transparent text-black": variant === "primary" && !!darkMode,
52
+ "text-gray-700 bg-white border border-gray-300 hover:text-gray-500": variant === "secondary" && !darkMode,
53
+ "text-charcoal-100 bg-charcoal-800 hover:bg-charcoal-700 border border-charcoal-700": variant === "secondary" && !!darkMode,
54
+ "!rounded-md text-left w-full text-gray-700 bg-white border border-gray-200 hover:bg-gray-50 hover:text-gray-500": variant === "form-input" && !darkMode,
55
+ "!rounded-md text-left w-full text-charcoal-100 bg-charcoal-800 hover:bg-charcoal-700 border border-charcoal-700": variant === "form-input" && !!darkMode,
56
+ "dark:bg-charcoal-800 dark:hover:bg-charcoal-700 dark:text-charcoal-100 dark:border-charcoal-700": (variant === "secondary" || variant === "form-input") && !darkMode,
57
+ "hover:text-brown-500 !shadow-none": variant === "flat" && !darkMode,
58
+ "dark:text-charcoal-100 dark:hover:text-primary-500": variant === "flat" && !darkMode,
59
+ "text-charcoal-100 hover:text-primary-500 !shadow-none": variant === "flat" && !!darkMode,
60
+ "!text-gray-300 hover:!text-gray-400": !children && !darkMode,
61
+ "dark:!text-charcoal-400": !children && !darkMode,
62
+ "!text-charcoal-400": !children && !!darkMode,
53
63
  ...classNameObject(className)
54
64
  }),
55
65
  ...props,
@@ -255,6 +265,10 @@ function CheckboxList({
255
265
  loading = false,
256
266
  placeholder = "No items found."
257
267
  }) {
268
+ const hasLoadedRef = useRef(false);
269
+ if (!loading) {
270
+ hasLoadedRef.current = true;
271
+ }
258
272
  const getFinalKey = (item, index) => {
259
273
  const key = typeof itemKey === "function" ? itemKey(item) : item[itemKey];
260
274
  return key !== void 0 && key !== null ? key : index;
@@ -265,7 +279,7 @@ function CheckboxList({
265
279
  }
266
280
  return placeholder;
267
281
  };
268
- return /* @__PURE__ */ jsx("div", { className: "w-full border border-gray-300 dark:border-charcoal-700 rounded-lg shadow-sm bg-white dark:bg-charcoal-800 overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "max-h-60 overflow-y-auto divide-y divide-gray-100 dark:divide-charcoal-700", children: loading && items.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-4 flex items-center justify-center min-h-36", children: /* @__PURE__ */ jsx(Loading, { spinSizeClassName: "w-6 h-6", className: "inline-block" }) }) : /* @__PURE__ */ jsx(Fragment, { children: items.length > 0 ? items.map((item, index) => {
282
+ return /* @__PURE__ */ jsx("div", { className: "w-full border border-gray-300 dark:border-charcoal-700 rounded-lg shadow-sm bg-white dark:bg-charcoal-800 overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "max-h-60 overflow-y-auto divide-y divide-gray-100 dark:divide-charcoal-700", children: loading && !hasLoadedRef.current ? /* @__PURE__ */ jsx("div", { className: "p-4 flex items-center justify-center min-h-36", children: /* @__PURE__ */ jsx(Loading, { spinSizeClassName: "w-6 h-6", className: "inline-block" }) }) : /* @__PURE__ */ jsx(Fragment, { children: items.length > 0 ? items.map((item, index) => {
269
283
  const finalKey = getFinalKey(item, index);
270
284
  const label = typeof itemLabel === "function" ? itemLabel(item) : item[itemLabel];
271
285
  const checked = typeof isChecked === "function" ? isChecked(item, selected) : selected.some(
@@ -322,7 +336,7 @@ function useModalContext() {
322
336
  }
323
337
  function ModalProvider({ children }) {
324
338
  const [currentState, setCurrentState] = useState(0);
325
- function reducer(state, action) {
339
+ function reducer2(state, action) {
326
340
  const updatedValues = { ...state };
327
341
  if ("modalShowing" in action) {
328
342
  updatedValues.modalShowing = action.modalShowing;
@@ -335,7 +349,7 @@ function ModalProvider({ children }) {
335
349
  }
336
350
  return updatedValues;
337
351
  }
338
- const [values, dispatch] = useReducer(reducer, {
352
+ const [values, dispatch] = useReducer(reducer2, {
339
353
  modalShowing: false,
340
354
  drawerShowing: false
341
355
  });
@@ -452,7 +466,7 @@ function CSSTransitionComponent({
452
466
  removeClasses(nodeRef.current, [...leaveToClasses, ...leaveClasses]);
453
467
  onExited(nodeRef.current);
454
468
  },
455
- children: React15.cloneElement(children, {
469
+ children: React16.cloneElement(children, {
456
470
  ref: (node) => {
457
471
  nodeRef.current = node;
458
472
  const childRef = children.ref;
@@ -1541,7 +1555,7 @@ function TagInput({
1541
1555
  }) {
1542
1556
  const [currentState, setCurrentState] = useState(0);
1543
1557
  const tagInputRef = useRef(null);
1544
- function reducer(state, action) {
1558
+ function reducer2(state, action) {
1545
1559
  const updatedValues = { ...state };
1546
1560
  if ("tags" in action) {
1547
1561
  updatedValues.tags = Array.from(new Set(action.tags));
@@ -1554,7 +1568,7 @@ function TagInput({
1554
1568
  }
1555
1569
  return updatedValues;
1556
1570
  }
1557
- const [values, dispatch] = useReducer(reducer, {
1571
+ const [values, dispatch] = useReducer(reducer2, {
1558
1572
  tags: []
1559
1573
  });
1560
1574
  const { tags } = values;
@@ -1646,34 +1660,114 @@ function NoData({ className }) {
1646
1660
  ] });
1647
1661
  }
1648
1662
  var ThemeContext = createContext(void 0);
1663
+ var currentIsDark = false;
1664
+ var matchMediaListeners = /* @__PURE__ */ new Set();
1665
+ var originalMatchMedia = null;
1666
+ if (typeof window !== "undefined") {
1667
+ originalMatchMedia = window.matchMedia;
1668
+ try {
1669
+ const saved = localStorage.getItem("theme");
1670
+ const prefersDark = originalMatchMedia.call(window, "(prefers-color-scheme: dark)").matches;
1671
+ const isSystemOrNone = !saved || saved === "system";
1672
+ currentIsDark = saved === "dark" || isSystemOrNone && prefersDark;
1673
+ } catch (e) {
1674
+ }
1675
+ window.matchMedia = function(query) {
1676
+ const normalized = query.replace(/\s+/g, "").toLowerCase();
1677
+ const isDarkQuery = normalized.includes("prefers-color-scheme:dark");
1678
+ const isLightQuery = normalized.includes("prefers-color-scheme:light");
1679
+ if (isDarkQuery || isLightQuery) {
1680
+ const matches = isDarkQuery ? currentIsDark : !currentIsDark;
1681
+ return {
1682
+ matches,
1683
+ media: query,
1684
+ onchange: null,
1685
+ addListener(listener) {
1686
+ matchMediaListeners.add({ listener, query });
1687
+ },
1688
+ removeListener(listener) {
1689
+ for (const item of matchMediaListeners) {
1690
+ if (item.listener === listener) {
1691
+ matchMediaListeners.delete(item);
1692
+ }
1693
+ }
1694
+ },
1695
+ addEventListener(type, listener) {
1696
+ if (type === "change") {
1697
+ matchMediaListeners.add({ listener, query });
1698
+ }
1699
+ },
1700
+ removeEventListener(type, listener) {
1701
+ if (type === "change") {
1702
+ for (const item of matchMediaListeners) {
1703
+ if (item.listener === listener) {
1704
+ matchMediaListeners.delete(item);
1705
+ }
1706
+ }
1707
+ }
1708
+ },
1709
+ dispatchEvent(event) {
1710
+ return true;
1711
+ }
1712
+ };
1713
+ }
1714
+ return originalMatchMedia.call(window, query);
1715
+ };
1716
+ }
1649
1717
  function ThemeProvider({ children, defaultTheme = "system" }) {
1650
1718
  const [mode, setMode] = useState(defaultTheme);
1651
1719
  const [isDark, setIsDark] = useState(false);
1652
- const stateRef = useRef(mode);
1653
- useEffect(() => {
1654
- const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
1655
- const isDark2 = mode === "dark" || mode === "system" && prefersDark;
1656
- document.documentElement.classList.toggle("dark", isDark2);
1657
- stateRef.current = mode;
1658
- setIsDark(isDark2);
1659
- }, [mode]);
1720
+ const [mounted, setMounted] = useState(false);
1660
1721
  useEffect(() => {
1661
1722
  const saved = localStorage.getItem("theme") || defaultTheme;
1662
1723
  setMode(saved);
1724
+ setMounted(true);
1663
1725
  }, [defaultTheme]);
1664
1726
  useEffect(() => {
1665
- const media = window.matchMedia("(prefers-color-scheme: dark)");
1666
- const listener = () => {
1667
- const prevMode = stateRef.current;
1668
- if (prevMode !== "system") {
1669
- setTimeout(() => {
1670
- document.documentElement.classList.toggle("dark", prevMode === "dark");
1671
- }, 100);
1727
+ if (!mounted) return;
1728
+ const media = originalMatchMedia ? originalMatchMedia.call(window, "(prefers-color-scheme: dark)") : null;
1729
+ const updateTheme = () => {
1730
+ const prefersDark = media ? media.matches : false;
1731
+ const currentIsDarkVal = mode === "dark" || mode === "system" && prefersDark;
1732
+ if (currentIsDarkVal) {
1733
+ document.documentElement.classList.add("dark");
1734
+ document.documentElement.classList.remove("light");
1735
+ } else {
1736
+ document.documentElement.classList.remove("dark");
1737
+ document.documentElement.classList.add("light");
1672
1738
  }
1739
+ currentIsDark = currentIsDarkVal;
1740
+ setIsDark(currentIsDarkVal);
1741
+ matchMediaListeners.forEach(({ listener, query }) => {
1742
+ try {
1743
+ const normalized = query.replace(/\s+/g, "").toLowerCase();
1744
+ const isDarkQuery = normalized.includes("prefers-color-scheme:dark");
1745
+ const matches = isDarkQuery ? currentIsDarkVal : !currentIsDarkVal;
1746
+ const event = { matches, media: query };
1747
+ if (typeof listener === "function") {
1748
+ listener(event);
1749
+ } else if (listener && typeof listener.handleEvent === "function") {
1750
+ listener.handleEvent(event);
1751
+ }
1752
+ } catch (e) {
1753
+ console.error(e);
1754
+ }
1755
+ });
1673
1756
  };
1674
- media.addEventListener("change", listener);
1675
- return () => media.removeEventListener("change", listener);
1676
- }, []);
1757
+ const timeoutId = setTimeout(updateTheme, 0);
1758
+ const handleMediaChange = () => {
1759
+ updateTheme();
1760
+ };
1761
+ if (media) {
1762
+ media.addEventListener("change", handleMediaChange);
1763
+ }
1764
+ return () => {
1765
+ clearTimeout(timeoutId);
1766
+ if (media) {
1767
+ media.removeEventListener("change", handleMediaChange);
1768
+ }
1769
+ };
1770
+ }, [mode, mounted]);
1677
1771
  function handleToggleTheme(triggerMode) {
1678
1772
  const triggeredMode = triggerMode === mode ? "system" : triggerMode;
1679
1773
  setMode(triggeredMode);
@@ -1927,71 +2021,65 @@ var resetValues = {
1927
2021
  description: null,
1928
2022
  actions: null
1929
2023
  };
1930
- function PopoverProvider({ children }) {
1931
- const [currentState, setCurrentState] = useState(0);
1932
- const [values, dispatch] = useReducer(reducer, resetValues);
1933
- const { show, style, tooltipStyle, position, description, actions } = values;
1934
- function reducer(state, action) {
1935
- const updatedValues = { ...state };
1936
- if ("show" in action) {
1937
- updatedValues.show = action.show;
1938
- }
1939
- if ("style" in action) {
1940
- updatedValues.style = action.style;
1941
- }
1942
- if ("tooltipStyle" in action) {
1943
- updatedValues.tooltipStyle = action.tooltipStyle;
1944
- }
1945
- if ("position" in action) {
1946
- updatedValues.position = action.position;
1947
- }
1948
- if ("description" in action) {
1949
- updatedValues.description = action.description;
2024
+ function reducer(state, action) {
2025
+ let changed = false;
2026
+ for (const k in action) {
2027
+ const key = k;
2028
+ if (action[key] !== state[key]) {
2029
+ changed = true;
2030
+ break;
1950
2031
  }
1951
- if ("actions" in action) {
1952
- updatedValues.actions = action.actions;
1953
- }
1954
- if ("render" in action && !!action.render) {
1955
- setCurrentState((prev) => 1 - prev);
1956
- }
1957
- return updatedValues;
1958
2032
  }
1959
- function handleShowPopover(params) {
1960
- dispatch({
1961
- ...params,
1962
- render: true
1963
- });
2033
+ if (!changed) return state;
2034
+ const updatedValues = { ...state };
2035
+ if ("show" in action) {
2036
+ updatedValues.show = action.show;
1964
2037
  }
1965
- function handleClosePopover() {
1966
- dispatch({
1967
- ...resetValues,
1968
- render: true
1969
- });
2038
+ if ("style" in action) {
2039
+ updatedValues.style = action.style;
1970
2040
  }
1971
- return /* @__PURE__ */ jsxs(
1972
- PopoverContext.Provider,
1973
- {
1974
- value: {
1975
- showPopover: show,
1976
- setPopover: handleShowPopover
1977
- },
1978
- children: [
1979
- children,
1980
- !!show && /* @__PURE__ */ jsx(
1981
- PopoverCard,
1982
- {
1983
- show,
1984
- style,
1985
- tooltipStyle,
1986
- position,
1987
- description,
1988
- actions,
1989
- onClose: handleClosePopover
1990
- }
1991
- )
1992
- ]
1993
- }
1994
- );
2041
+ if ("tooltipStyle" in action) {
2042
+ updatedValues.tooltipStyle = action.tooltipStyle;
2043
+ }
2044
+ if ("position" in action) {
2045
+ updatedValues.position = action.position;
2046
+ }
2047
+ if ("description" in action) {
2048
+ updatedValues.description = action.description;
2049
+ }
2050
+ if ("actions" in action) {
2051
+ updatedValues.actions = action.actions;
2052
+ }
2053
+ return updatedValues;
2054
+ }
2055
+ function PopoverProvider({ children }) {
2056
+ const [values, dispatch] = useReducer(reducer, resetValues);
2057
+ const { show, style, tooltipStyle, position, description, actions } = values;
2058
+ const handleShowPopover = useCallback((params) => {
2059
+ dispatch(params);
2060
+ }, []);
2061
+ const handleClosePopover = useCallback(() => {
2062
+ dispatch(resetValues);
2063
+ }, []);
2064
+ const contextValue = useMemo(() => ({
2065
+ showPopover: show,
2066
+ setPopover: handleShowPopover
2067
+ }), [show, handleShowPopover]);
2068
+ return /* @__PURE__ */ jsxs(PopoverContext.Provider, { value: contextValue, children: [
2069
+ children,
2070
+ !!show && /* @__PURE__ */ jsx(
2071
+ PopoverCard,
2072
+ {
2073
+ show,
2074
+ style,
2075
+ tooltipStyle,
2076
+ position,
2077
+ description,
2078
+ actions,
2079
+ onClose: handleClosePopover
2080
+ }
2081
+ )
2082
+ ] });
1995
2083
  }
1996
2084
  function usePopoverContext() {
1997
2085
  const context = useContext(PopoverContext);
@@ -2006,8 +2094,15 @@ function Popover({ className, description, actions, children, opts }) {
2006
2094
  const { screenWidth, screenHeight } = useResizeListener();
2007
2095
  const [menuData, setMenuData] = useState({ showMenu: false, x: 0, y: 0, left: 0, right: 0, top: 0, bottom: 0 });
2008
2096
  const parentRef = useRef(null);
2097
+ const prevShowMenuRef = useRef(false);
2098
+ const prevShowPopoverRef = useRef(false);
2009
2099
  (actions || []).some((action) => (action.items || []).length > 0);
2010
2100
  useEffect(() => {
2101
+ const wasOpen = prevShowMenuRef.current;
2102
+ prevShowMenuRef.current = menuData.showMenu;
2103
+ if (!menuData.showMenu && !wasOpen) {
2104
+ return;
2105
+ }
2011
2106
  const stickyTop = menuData.showMenu && screenHeight - (menuData.y + 14) < 300;
2012
2107
  setPopover({
2013
2108
  show: menuData.showMenu,
@@ -2031,13 +2126,14 @@ function Popover({ className, description, actions, children, opts }) {
2031
2126
  });
2032
2127
  }, [menuData, screenWidth, screenHeight, description, actions, setPopover]);
2033
2128
  useEffect(() => {
2034
- if (!showPopover && !!menuData.showMenu) {
2129
+ if (prevShowPopoverRef.current && !showPopover) {
2035
2130
  setMenuData((prev) => ({
2036
2131
  ...prev,
2037
2132
  showMenu: false
2038
2133
  }));
2039
2134
  }
2040
- }, [showPopover, menuData.showMenu]);
2135
+ prevShowPopoverRef.current = showPopover;
2136
+ }, [showPopover]);
2041
2137
  function handleToggleMenu(e) {
2042
2138
  e.stopPropagation();
2043
2139
  if (menuData.showMenu) {
@@ -2160,7 +2256,7 @@ function Dragger({
2160
2256
  onChange
2161
2257
  }) {
2162
2258
  const [, setCurrentState] = useState(0);
2163
- const [values, dispatch] = useReducer(reducer, {
2259
+ const [values, dispatch] = useReducer(reducer2, {
2164
2260
  items: null,
2165
2261
  displayItems: null,
2166
2262
  dragging: false,
@@ -2199,7 +2295,7 @@ function Dragger({
2199
2295
  }
2200
2296
  }
2201
2297
  }, [grandChildren, updated]);
2202
- function reducer(state, action) {
2298
+ function reducer2(state, action) {
2203
2299
  const updatedValues = { ...state };
2204
2300
  if ("items" in action) {
2205
2301
  updatedValues.items = action.items;
@@ -2420,9 +2516,9 @@ function Dragger({
2420
2516
  }
2421
2517
  dispatch({ dragging: false });
2422
2518
  }
2423
- const derivedItems = !!items && React15.cloneElement(children, {
2519
+ const derivedItems = !!items && React16.cloneElement(children, {
2424
2520
  children: items.map((grandChild, index) => {
2425
- return React15.cloneElement(grandChild, {
2521
+ return React16.cloneElement(grandChild, {
2426
2522
  id: (records[index] || {}).id,
2427
2523
  draggable: true,
2428
2524
  className: classNames({
@@ -2439,9 +2535,9 @@ function Dragger({
2439
2535
  });
2440
2536
  })
2441
2537
  });
2442
- const derivedDisplayItems = !!displayItems && React15.cloneElement(children, {
2538
+ const derivedDisplayItems = !!displayItems && React16.cloneElement(children, {
2443
2539
  children: displayItems.map((grandChild, index) => {
2444
- return React15.cloneElement(grandChild, {
2540
+ return React16.cloneElement(grandChild, {
2445
2541
  draggable: true,
2446
2542
  className: classNames({
2447
2543
  ...classNameObject(grandChild.props.className),
@@ -3040,7 +3136,7 @@ function DataTableRow({ index, selected, label, description, additionalDescripti
3040
3136
  "!text-left": dataItem.align === "left",
3041
3137
  "align-middle": vertical === "middle"
3042
3138
  }),
3043
- children: typeof dataItem.data[index] === "string" || React15.isValidElement(dataItem.data[index]) ? dataItem.data[index] : numberFormatter(dataItem.data[index], dataItem.decimal != null ? dataItem.decimal : 1)
3139
+ children: typeof dataItem.data[index] === "string" || React16.isValidElement(dataItem.data[index]) ? dataItem.data[index] : numberFormatter(dataItem.data[index], dataItem.decimal != null ? dataItem.decimal : 1)
3044
3140
  },
3045
3141
  `${dataIndex}-${index}`
3046
3142
  ))
@@ -3177,7 +3273,7 @@ function DataTable({
3177
3273
  backgroundColor: `${colors[dataIndex]}`,
3178
3274
  color: bestTextColorForBg(colors[dataIndex])
3179
3275
  } : void 0,
3180
- children: typeof total[dataIndex] === "string" || React15.isValidElement(total[dataIndex]) ? total[dataIndex] : showCurrency ? /* @__PURE__ */ jsx(CurrencyDisplay, { amount: total[dataIndex] }) : numberFormatter(total[dataIndex], 1)
3276
+ children: typeof total[dataIndex] === "string" || React16.isValidElement(total[dataIndex]) ? total[dataIndex] : showCurrency ? /* @__PURE__ */ jsx(CurrencyDisplay, { amount: total[dataIndex] }) : numberFormatter(total[dataIndex], 1)
3181
3277
  },
3182
3278
  `${dataIndex}-total`
3183
3279
  ))