twico-ui 1.0.1 → 1.1.0

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.cjs CHANGED
@@ -256,7 +256,7 @@ var MENU_CSS = `
256
256
  @media (prefers-reduced-motion: reduce) { .twc-menu[data-state] { animation-duration: 1ms; } }
257
257
  .twc-menu[data-flip="true"] { transform-origin: bottom; }
258
258
  .twc-menu__header { display: flex; align-items: center; gap: var(--space-2-5); padding: 8px 10px 10px; margin-bottom: var(--space-1-5); border-bottom: var(--border-thin) solid var(--color-divider); }
259
- .twc-menu__header-main { min-width: 0; }
259
+ .twc-menu__header-main { min-width: 0; display: flex; flex-direction: column; }
260
260
  .twc-menu__header-title { font-size: var(--text-sm); font-weight: var(--font-bold); color: var(--color-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
261
261
  .twc-menu__header-sub { font-size: var(--text-xs); color: var(--color-text-subtle); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
262
262
  .twc-menu__label { padding: 6px 10px 4px; font-size: var(--text-xs); font-weight: var(--font-semibold); letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--color-text-subtle); }
@@ -8691,6 +8691,135 @@ function Rating({
8691
8691
  }
8692
8692
  );
8693
8693
  }
8694
+ var TOOLTIP_CSS = `
8695
+ .twc-tooltip-wrap { display: inline-flex; }
8696
+ .twc-tooltip {
8697
+ position: fixed; z-index: var(--z-tooltip); pointer-events: none;
8698
+ padding: 6px 10px; border-radius: var(--radius-md);
8699
+ background: var(--color-text); color: var(--color-surface);
8700
+ font-family: var(--font-sans); font-size: var(--text-xs); font-weight: var(--font-medium);
8701
+ line-height: 1.3; white-space: nowrap; box-shadow: var(--shadow-md);
8702
+ opacity: 0; transform: scale(0.9); transition: opacity var(--duration-fast) var(--ease-out), transform var(--duration-fast) var(--ease-spring);
8703
+ }
8704
+ .twc-tooltip[data-show="true"] { opacity: 1; transform: scale(1); }
8705
+ .twc-tooltip[data-place="top"] { transform-origin: bottom center; translate: -50% 0; }
8706
+ .twc-tooltip[data-place="bottom"] { transform-origin: top center; translate: -50% 0; }
8707
+ .twc-tooltip[data-place="left"] { transform-origin: right center; translate: 0 -50%; }
8708
+ .twc-tooltip[data-place="right"] { transform-origin: left center; translate: 0 -50%; }
8709
+ .twc-tooltip[data-show="true"][data-place="top"], .twc-tooltip[data-show="true"][data-place="bottom"] { transform: scale(1); }
8710
+ .twc-tooltip__arrow { position: absolute; width: 7px; height: 7px; background: var(--color-text); transform: rotate(45deg); }
8711
+ .twc-tooltip[data-place="top"] .twc-tooltip__arrow { bottom: -3px; left: 50%; margin-left: -3.5px; }
8712
+ .twc-tooltip[data-place="bottom"] .twc-tooltip__arrow { top: -3px; left: 50%; margin-left: -3.5px; }
8713
+ .twc-tooltip[data-place="left"] .twc-tooltip__arrow { right: -3px; top: 50%; margin-top: -3.5px; }
8714
+ .twc-tooltip[data-place="right"] .twc-tooltip__arrow { left: -3px; top: 50%; margin-top: -3.5px; }
8715
+ @media (prefers-reduced-motion: reduce) { .twc-tooltip { transition: opacity var(--duration-fast) linear; transform: none; } }
8716
+ `;
8717
+ function Tooltip({
8718
+ children,
8719
+ label,
8720
+ placement = "top",
8721
+ delay = 120,
8722
+ className = "",
8723
+ ...rest
8724
+ }) {
8725
+ const __twcStyles = useScopedStyles("twc-tooltip-styles", TOOLTIP_CSS);
8726
+ const id = React23__namespace.default.useId();
8727
+ const [show, setShow] = React23__namespace.default.useState(false);
8728
+ const [coords, setCoords] = React23__namespace.default.useState(null);
8729
+ const timer = React23__namespace.default.useRef(null);
8730
+ const wrapRef = React23__namespace.default.useRef(null);
8731
+ const tipRef = React23__namespace.default.useRef(null);
8732
+ const open = () => {
8733
+ clearTimeout(timer.current);
8734
+ timer.current = setTimeout(() => setShow(true), delay);
8735
+ };
8736
+ const close = () => {
8737
+ clearTimeout(timer.current);
8738
+ setShow(false);
8739
+ };
8740
+ React23__namespace.default.useEffect(() => () => clearTimeout(timer.current), []);
8741
+ const place = React23__namespace.default.useCallback(() => {
8742
+ const el = wrapRef.current;
8743
+ if (!el) return;
8744
+ const r = el.getBoundingClientRect();
8745
+ const cx = Math.round(r.left + r.width / 2), cy = Math.round(r.top + r.height / 2);
8746
+ const vw = window.innerWidth, vh = window.innerHeight, gap = 8, margin = 8;
8747
+ const tip = tipRef.current;
8748
+ const tw = tip ? tip.offsetWidth : 0, th = tip ? tip.offsetHeight : 0;
8749
+ const clampX = (c) => tw ? Math.round(Math.min(Math.max(c, margin + tw / 2), vw - margin - tw / 2)) : c;
8750
+ const clampY = (c) => th ? Math.round(Math.min(Math.max(c, margin + th / 2), vh - margin - th / 2)) : c;
8751
+ const base = { left: "auto", right: "auto", top: "auto", bottom: "auto" };
8752
+ if (placement === "bottom") setCoords({ ...base, left: clampX(cx), top: Math.round(r.bottom + gap) });
8753
+ else if (placement === "left") setCoords({ ...base, top: clampY(cy), right: Math.round(vw - r.left + gap) });
8754
+ else if (placement === "right") setCoords({ ...base, top: clampY(cy), left: Math.round(r.right + gap) });
8755
+ else setCoords({ ...base, left: clampX(cx), bottom: Math.round(vh - r.top + gap) });
8756
+ }, [placement]);
8757
+ React23__namespace.default.useEffect(() => {
8758
+ place();
8759
+ }, [place]);
8760
+ React23__namespace.default.useEffect(() => {
8761
+ if (!show) return void 0;
8762
+ place();
8763
+ const onMove = () => place();
8764
+ window.addEventListener("scroll", onMove, true);
8765
+ window.addEventListener("resize", onMove);
8766
+ return () => {
8767
+ window.removeEventListener("scroll", onMove, true);
8768
+ window.removeEventListener("resize", onMove);
8769
+ };
8770
+ }, [show, place]);
8771
+ React23__namespace.default.useEffect(() => {
8772
+ if (!show) return;
8773
+ const onKeyDown = (e) => {
8774
+ if (e.key === "Escape") {
8775
+ clearTimeout(timer.current);
8776
+ setShow(false);
8777
+ }
8778
+ };
8779
+ document.addEventListener("keydown", onKeyDown);
8780
+ return () => document.removeEventListener("keydown", onKeyDown);
8781
+ }, [show]);
8782
+ const describedBy = show ? id : void 0;
8783
+ const trigger = React23__namespace.default.isValidElement(children) && children.type !== React23__namespace.default.Fragment ? React23__namespace.default.cloneElement(children, {
8784
+ "aria-describedby": [children.props["aria-describedby"], describedBy].filter(Boolean).join(" ") || void 0
8785
+ }) : /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-describedby": describedBy, children });
8786
+ return /* @__PURE__ */ jsxRuntime.jsxs(
8787
+ "span",
8788
+ {
8789
+ ref: wrapRef,
8790
+ className: `twc-tooltip-wrap ${className}`,
8791
+ onMouseEnter: open,
8792
+ onMouseLeave: close,
8793
+ onFocus: open,
8794
+ onBlur: close,
8795
+ ...rest,
8796
+ children: [
8797
+ __twcStyles,
8798
+ trigger,
8799
+ coords ? reactDom.createPortal(
8800
+ /* @__PURE__ */ jsxRuntime.jsxs(
8801
+ "span",
8802
+ {
8803
+ ref: tipRef,
8804
+ id,
8805
+ className: "twc-tooltip",
8806
+ "data-place": placement,
8807
+ "data-show": show || void 0,
8808
+ role: "tooltip",
8809
+ "aria-hidden": show ? void 0 : "true",
8810
+ style: { position: "fixed", left: coords.left, right: coords.right, top: coords.top, bottom: coords.bottom },
8811
+ children: [
8812
+ label,
8813
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-tooltip__arrow", "aria-hidden": "true" })
8814
+ ]
8815
+ }
8816
+ ),
8817
+ document.body
8818
+ ) : null
8819
+ ]
8820
+ }
8821
+ );
8822
+ }
8694
8823
  var SIDEBAR_CSS = `
8695
8824
  .twc-sidebar { display: flex; flex-direction: column; height: 100%; font-family: var(--font-sans);
8696
8825
  background: var(--color-surface); border-inline-end: var(--border-thin) solid var(--color-border);
@@ -8774,10 +8903,10 @@ function Sidebar({
8774
8903
  "data-active": it.active || void 0,
8775
8904
  "aria-current": it.active ? "page" : void 0,
8776
8905
  onClick: it.onClick,
8777
- title: collapsed && labelStr != null ? labelStr : void 0,
8778
8906
  "aria-label": collapsed && labelStr != null ? labelStr : void 0
8779
8907
  };
8780
- return href != null ? /* @__PURE__ */ jsxRuntime.jsx("a", { href, ...common, children: inner }, i) : /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", ...common, children: inner }, i);
8908
+ const node = href != null ? /* @__PURE__ */ jsxRuntime.jsx("a", { href, ...common, children: inner }, i) : /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", ...common, children: inner }, i);
8909
+ return collapsed && labelStr != null ? /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { label: labelStr, placement: "right", style: { display: "block" }, children: node }, i) : node;
8781
8910
  })()
8782
8911
  ) }),
8783
8912
  footer || collapsible ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-sidebar__foot", children: [
@@ -9919,135 +10048,6 @@ function ToastProvider({ children, limit = 4, duration = 4500, ...viewportProps
9919
10048
  )) })
9920
10049
  ] });
9921
10050
  }
9922
- var TOOLTIP_CSS = `
9923
- .twc-tooltip-wrap { display: inline-flex; }
9924
- .twc-tooltip {
9925
- position: fixed; z-index: var(--z-tooltip); pointer-events: none;
9926
- padding: 6px 10px; border-radius: var(--radius-md);
9927
- background: var(--color-text); color: var(--color-surface);
9928
- font-family: var(--font-sans); font-size: var(--text-xs); font-weight: var(--font-medium);
9929
- line-height: 1.3; white-space: nowrap; box-shadow: var(--shadow-md);
9930
- opacity: 0; transform: scale(0.9); transition: opacity var(--duration-fast) var(--ease-out), transform var(--duration-fast) var(--ease-spring);
9931
- }
9932
- .twc-tooltip[data-show="true"] { opacity: 1; transform: scale(1); }
9933
- .twc-tooltip[data-place="top"] { transform-origin: bottom center; translate: -50% 0; }
9934
- .twc-tooltip[data-place="bottom"] { transform-origin: top center; translate: -50% 0; }
9935
- .twc-tooltip[data-place="left"] { transform-origin: right center; translate: 0 -50%; }
9936
- .twc-tooltip[data-place="right"] { transform-origin: left center; translate: 0 -50%; }
9937
- .twc-tooltip[data-show="true"][data-place="top"], .twc-tooltip[data-show="true"][data-place="bottom"] { transform: scale(1); }
9938
- .twc-tooltip__arrow { position: absolute; width: 7px; height: 7px; background: var(--color-text); transform: rotate(45deg); }
9939
- .twc-tooltip[data-place="top"] .twc-tooltip__arrow { bottom: -3px; left: 50%; margin-left: -3.5px; }
9940
- .twc-tooltip[data-place="bottom"] .twc-tooltip__arrow { top: -3px; left: 50%; margin-left: -3.5px; }
9941
- .twc-tooltip[data-place="left"] .twc-tooltip__arrow { right: -3px; top: 50%; margin-top: -3.5px; }
9942
- .twc-tooltip[data-place="right"] .twc-tooltip__arrow { left: -3px; top: 50%; margin-top: -3.5px; }
9943
- @media (prefers-reduced-motion: reduce) { .twc-tooltip { transition: opacity var(--duration-fast) linear; transform: none; } }
9944
- `;
9945
- function Tooltip({
9946
- children,
9947
- label,
9948
- placement = "top",
9949
- delay = 120,
9950
- className = "",
9951
- ...rest
9952
- }) {
9953
- const __twcStyles = useScopedStyles("twc-tooltip-styles", TOOLTIP_CSS);
9954
- const id = React23__namespace.default.useId();
9955
- const [show, setShow] = React23__namespace.default.useState(false);
9956
- const [coords, setCoords] = React23__namespace.default.useState(null);
9957
- const timer = React23__namespace.default.useRef(null);
9958
- const wrapRef = React23__namespace.default.useRef(null);
9959
- const tipRef = React23__namespace.default.useRef(null);
9960
- const open = () => {
9961
- clearTimeout(timer.current);
9962
- timer.current = setTimeout(() => setShow(true), delay);
9963
- };
9964
- const close = () => {
9965
- clearTimeout(timer.current);
9966
- setShow(false);
9967
- };
9968
- React23__namespace.default.useEffect(() => () => clearTimeout(timer.current), []);
9969
- const place = React23__namespace.default.useCallback(() => {
9970
- const el = wrapRef.current;
9971
- if (!el) return;
9972
- const r = el.getBoundingClientRect();
9973
- const cx = Math.round(r.left + r.width / 2), cy = Math.round(r.top + r.height / 2);
9974
- const vw = window.innerWidth, vh = window.innerHeight, gap = 8, margin = 8;
9975
- const tip = tipRef.current;
9976
- const tw = tip ? tip.offsetWidth : 0, th = tip ? tip.offsetHeight : 0;
9977
- const clampX = (c) => tw ? Math.round(Math.min(Math.max(c, margin + tw / 2), vw - margin - tw / 2)) : c;
9978
- const clampY = (c) => th ? Math.round(Math.min(Math.max(c, margin + th / 2), vh - margin - th / 2)) : c;
9979
- const base = { left: "auto", right: "auto", top: "auto", bottom: "auto" };
9980
- if (placement === "bottom") setCoords({ ...base, left: clampX(cx), top: Math.round(r.bottom + gap) });
9981
- else if (placement === "left") setCoords({ ...base, top: clampY(cy), right: Math.round(vw - r.left + gap) });
9982
- else if (placement === "right") setCoords({ ...base, top: clampY(cy), left: Math.round(r.right + gap) });
9983
- else setCoords({ ...base, left: clampX(cx), bottom: Math.round(vh - r.top + gap) });
9984
- }, [placement]);
9985
- React23__namespace.default.useEffect(() => {
9986
- place();
9987
- }, [place]);
9988
- React23__namespace.default.useEffect(() => {
9989
- if (!show) return void 0;
9990
- place();
9991
- const onMove = () => place();
9992
- window.addEventListener("scroll", onMove, true);
9993
- window.addEventListener("resize", onMove);
9994
- return () => {
9995
- window.removeEventListener("scroll", onMove, true);
9996
- window.removeEventListener("resize", onMove);
9997
- };
9998
- }, [show, place]);
9999
- React23__namespace.default.useEffect(() => {
10000
- if (!show) return;
10001
- const onKeyDown = (e) => {
10002
- if (e.key === "Escape") {
10003
- clearTimeout(timer.current);
10004
- setShow(false);
10005
- }
10006
- };
10007
- document.addEventListener("keydown", onKeyDown);
10008
- return () => document.removeEventListener("keydown", onKeyDown);
10009
- }, [show]);
10010
- const describedBy = show ? id : void 0;
10011
- const trigger = React23__namespace.default.isValidElement(children) && children.type !== React23__namespace.default.Fragment ? React23__namespace.default.cloneElement(children, {
10012
- "aria-describedby": [children.props["aria-describedby"], describedBy].filter(Boolean).join(" ") || void 0
10013
- }) : /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-describedby": describedBy, children });
10014
- return /* @__PURE__ */ jsxRuntime.jsxs(
10015
- "span",
10016
- {
10017
- ref: wrapRef,
10018
- className: `twc-tooltip-wrap ${className}`,
10019
- onMouseEnter: open,
10020
- onMouseLeave: close,
10021
- onFocus: open,
10022
- onBlur: close,
10023
- ...rest,
10024
- children: [
10025
- __twcStyles,
10026
- trigger,
10027
- coords ? reactDom.createPortal(
10028
- /* @__PURE__ */ jsxRuntime.jsxs(
10029
- "span",
10030
- {
10031
- ref: tipRef,
10032
- id,
10033
- className: "twc-tooltip",
10034
- "data-place": placement,
10035
- "data-show": show || void 0,
10036
- role: "tooltip",
10037
- "aria-hidden": show ? void 0 : "true",
10038
- style: { position: "fixed", left: coords.left, right: coords.right, top: coords.top, bottom: coords.bottom },
10039
- children: [
10040
- label,
10041
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-tooltip__arrow", "aria-hidden": "true" })
10042
- ]
10043
- }
10044
- ),
10045
- document.body
10046
- ) : null
10047
- ]
10048
- }
10049
- );
10050
- }
10051
10051
  var TREE_CSS = `
10052
10052
  .twc-tree { font-family: var(--font-sans); font-size: var(--text-sm); user-select: none; }
10053
10053
  .twc-tree__group { list-style: none; margin: 0; padding: 0; }
@@ -10218,6 +10218,34 @@ function TreeView({
10218
10218
  )) })
10219
10219
  ] });
10220
10220
  }
10221
+ var SHELL_CSS = `
10222
+ .twc-shell { display: flex; overflow: hidden; background: var(--color-bg); color: var(--color-text); font-family: var(--font-sans); }
10223
+ .twc-shell__main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; height: 100%; }
10224
+ .twc-shell__header { flex: none; display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
10225
+ min-height: 60px; padding-inline: var(--space-5); border-bottom: var(--border-thin) solid var(--color-border); background: var(--color-surface); }
10226
+ .twc-shell__content { flex: 1 1 auto; min-height: 0; overflow: auto; }
10227
+ .twc-shell__content[data-padded="true"] { padding: var(--space-6) var(--space-5); }
10228
+ `;
10229
+ function AppShell({
10230
+ sidebar,
10231
+ header,
10232
+ children,
10233
+ height = "100dvh",
10234
+ padded = true,
10235
+ className = "",
10236
+ style,
10237
+ ...rest
10238
+ }) {
10239
+ const __twcStyles = useScopedStyles("twc-shell-styles", SHELL_CSS);
10240
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-shell ${className}`.trim(), style: { height, ...style }, ...rest, children: [
10241
+ __twcStyles,
10242
+ sidebar,
10243
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-shell__main", children: [
10244
+ header != null ? /* @__PURE__ */ jsxRuntime.jsx("header", { className: "twc-shell__header", children: header }) : null,
10245
+ /* @__PURE__ */ jsxRuntime.jsx("main", { className: "twc-shell__content", "data-padded": padded ? "true" : void 0, children })
10246
+ ] })
10247
+ ] });
10248
+ }
10221
10249
  function sp(v) {
10222
10250
  if (v == null) return void 0;
10223
10251
  return typeof v === "number" ? `var(--space-${String(v).replace(".", "-")})` : v;
@@ -10856,6 +10884,7 @@ function useId(prefix = "twc") {
10856
10884
 
10857
10885
  exports.Accordion = Accordion;
10858
10886
  exports.Alert = Alert;
10887
+ exports.AppShell = AppShell;
10859
10888
  exports.Avatar = Avatar;
10860
10889
  exports.AvatarMenu = AvatarMenu;
10861
10890
  exports.Badge = Badge;