@kjun-ui/native 0.14.0 → 0.16.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
@@ -1818,13 +1818,25 @@ function KjunFormLayout({
1818
1818
  ) });
1819
1819
  }
1820
1820
 
1821
- // shared/package-runtime/content-presence.ts
1821
+ // shared/package-runtime/use-after-close.ts
1822
1822
  var import_react18 = require("react");
1823
+ function useAfterClose(present, onAfterClose, enabled = true) {
1824
+ const was = (0, import_react18.useRef)(present), callback = (0, import_react18.useRef)(onAfterClose);
1825
+ callback.current = onAfterClose;
1826
+ (0, import_react18.useEffect)(() => {
1827
+ var _a;
1828
+ if (was.current && !present && enabled) (_a = callback.current) == null ? void 0 : _a.call(callback);
1829
+ was.current = present;
1830
+ }, [present, enabled]);
1831
+ }
1832
+
1833
+ // shared/package-runtime/content-presence.ts
1834
+ var import_react19 = require("react");
1823
1835
  function hasContent(node) {
1824
1836
  if (node == null || typeof node === "boolean") return false;
1825
1837
  if (typeof node === "string") return node.trim().length > 0;
1826
1838
  if (Array.isArray(node)) return node.some(hasContent);
1827
- if ((0, import_react18.isValidElement)(node) && node.type === import_react18.Fragment)
1839
+ if ((0, import_react19.isValidElement)(node) && node.type === import_react19.Fragment)
1828
1840
  return hasContent(node.props.children);
1829
1841
  return true;
1830
1842
  }
@@ -1837,7 +1849,7 @@ function LayerBackdrop({ onPress, style }) {
1837
1849
  }
1838
1850
 
1839
1851
  // packages/native/src/modal.tsx
1840
- var import_react20 = require("react");
1852
+ var import_react21 = require("react");
1841
1853
 
1842
1854
  // shared/package-runtime/form-actions.ts
1843
1855
  function formActionsNeedStack(width, widths, gap) {
@@ -1850,7 +1862,7 @@ var import_tokens11 = require("@kjun-ui/tokens");
1850
1862
  var import_react_native14 = require("react-native");
1851
1863
 
1852
1864
  // packages/native/src/overlay-host.tsx
1853
- var import_react19 = require("react");
1865
+ var import_react20 = require("react");
1854
1866
  var import_react_native12 = require("react-native");
1855
1867
 
1856
1868
  // packages/native/src/modal-window.tsx
@@ -1866,17 +1878,18 @@ var import_jsx_runtime12 = (
1866
1878
  // a new window even when an interrupted exit retained the old surface.
1867
1879
  require("react/jsx-runtime")
1868
1880
  );
1869
- var FeedbackOverlayContext = (0, import_react19.createContext)(
1881
+ var FeedbackOverlayContext = (0, import_react20.createContext)(
1870
1882
  null
1871
1883
  );
1872
1884
  function ScopedNativeModal({ children, visible, ...props }) {
1873
- var _a;
1874
- const layers = useLayerState(), scope = (0, import_react19.useContext)(LayerScope);
1885
+ const layers = useLayerState(), scope = (0, import_react20.useContext)(LayerScope);
1875
1886
  const entry = scope ? layers.entries.get(scope) : void 0;
1876
- const generation = (_a = entry == null ? void 0 : entry.order) != null ? _a : 0;
1887
+ const last = (0, import_react20.useRef)(0);
1888
+ if (entry) last.current = entry.order;
1889
+ const generation = last.current;
1877
1890
  const inactive = !!scope && !layers.isActive(scope);
1878
- const host = (0, import_react19.useContext)(FeedbackOverlayContext), id = "kjun-" + (0, import_react19.useId)(), register = host == null ? void 0 : host.register;
1879
- (0, import_react19.useEffect)(
1891
+ const host = (0, import_react20.useContext)(FeedbackOverlayContext), id = "kjun-" + (0, import_react20.useId)(), register = host == null ? void 0 : host.register;
1892
+ (0, import_react20.useEffect)(
1880
1893
  () => visible && register ? register(id) : void 0,
1881
1894
  [visible, register, id, generation]
1882
1895
  );
@@ -1925,7 +1938,7 @@ function KjunFormActions({
1925
1938
  cancelText != null ? cancelText : cancelText = m.cancel;
1926
1939
  confirmText != null ? confirmText : confirmText = m.save;
1927
1940
  const { fontFamily } = useKjunStyles();
1928
- const [width, setWidth] = (0, import_react20.useState)(0), [measurements, setMeasurements] = (0, import_react20.useState)({});
1941
+ const [width, setWidth] = (0, import_react21.useState)(0), [measurements, setMeasurements] = (0, import_react21.useState)({});
1929
1942
  const labels = [showCancel && { text: cancelText }, showConfirm && { text: confirmText }].filter(Boolean);
1930
1943
  const keyFor = (text) => `${fontFamily}:${size}:${text}`;
1931
1944
  const widths = labels.map((label) => measurements[keyFor(label.text)] === void 0 ? 0 : Math.max(import_tokens11.tokens.button.minWidths[size], measurements[keyFor(label.text)] + import_tokens11.tokens.button.paddingX[size] * 2));
@@ -1997,6 +2010,7 @@ function KjunModal({
1997
2010
  open,
1998
2011
  onOpenChange,
1999
2012
  onClose,
2013
+ onAfterClose,
2000
2014
  onCancel,
2001
2015
  onConfirm,
2002
2016
  title = "",
@@ -2029,7 +2043,7 @@ function KjunModal({
2029
2043
  const motion = useNativePresence(open, import_tokens11.tokens.motion.layerEnter, import_tokens11.tokens.motion.layerExit, true, 0, { fade: import_tokens11.tokens.motion.backdropExit });
2030
2044
  const offset = useMotionValue(open ? 0 : import_tokens11.tokens.motionDistance.modalExit, open ? import_tokens11.tokens.motion.layerEnter : import_tokens11.tokens.motion.layerExit, import_tokens11.tokens.motionDistance.modalEnter, open ? motionOut : motionIn, true);
2031
2045
  const backdrop = useNativePresence(open, import_tokens11.tokens.motion.backdrop, import_tokens11.tokens.motion.backdropExit, true, 0, { fade: import_tokens11.tokens.motion.backdropExit });
2032
- (0, import_react20.useLayoutEffect)(() => {
2046
+ (0, import_react21.useLayoutEffect)(() => {
2033
2047
  if (!motion.present) {
2034
2048
  offset.stopAnimation();
2035
2049
  offset.setValue(import_tokens11.tokens.motionDistance.modalEnter);
@@ -2044,10 +2058,15 @@ function KjunModal({
2044
2058
  const layer = useLayer(open, motion.present, "window", () => {
2045
2059
  if (closeOnEsc) close();
2046
2060
  });
2061
+ const iosDismiss = import_react_native14.Platform.OS === "ios";
2062
+ useAfterClose(motion.present, onAfterClose, !iosDismiss);
2047
2063
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(LayerScope.Provider, { value: layer.id, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2048
2064
  ScopedNativeModal,
2049
2065
  {
2050
2066
  visible: motion.present,
2067
+ onDismiss: iosDismiss ? () => {
2068
+ if (!open) onAfterClose == null ? void 0 : onAfterClose();
2069
+ } : void 0,
2051
2070
  transparent: true,
2052
2071
  animationType: "none",
2053
2072
  onRequestClose: () => {
@@ -2205,7 +2224,7 @@ function KjunModal({
2205
2224
 
2206
2225
  // packages/native/src/card.tsx
2207
2226
  var import_tokens12 = require("@kjun-ui/tokens");
2208
- var import_react21 = require("react");
2227
+ var import_react22 = require("react");
2209
2228
  var import_react_native15 = require("react-native");
2210
2229
  var import_react_native_svg2 = __toESM(require("react-native-svg"), 1);
2211
2230
  var import_jsx_runtime14 = require("react/jsx-runtime");
@@ -2231,7 +2250,7 @@ function KjunCard({
2231
2250
  onPress
2232
2251
  }) {
2233
2252
  const { colors } = useKjunStyles();
2234
- const id = "kjun-" + (0, import_react21.useId)().replace(/[^a-z0-9]/gi, "");
2253
+ const id = "kjun-" + (0, import_react22.useId)().replace(/[^a-z0-9]/gi, "");
2235
2254
  const inset = import_tokens12.tokens.card.padding[padding], bodyInset = import_tokens12.tokens.card.padding[bodyPadding];
2236
2255
  const corner = import_tokens12.tokens.card.radii[radius];
2237
2256
  const paint = import_tokens12.tokens.cardSurfaces[surface];
@@ -2315,7 +2334,7 @@ function alertRole(tone, live) {
2315
2334
 
2316
2335
  // packages/native/src/display-primitives.tsx
2317
2336
  var import_tokens14 = require("@kjun-ui/tokens");
2318
- var import_react22 = require("react");
2337
+ var import_react23 = require("react");
2319
2338
  var import_react_native16 = require("react-native");
2320
2339
  var import_react_native_svg3 = __toESM(require("react-native-svg"), 1);
2321
2340
 
@@ -2439,7 +2458,7 @@ function KjunEmpty({
2439
2458
  text != null ? text : text = m.noData;
2440
2459
  const { colors } = useKjunStyles();
2441
2460
  const spec = import_tokens14.tokens.extensions.empty;
2442
- const stateContent = (0, import_react22.useContext)(DataStateContentContext);
2461
+ const stateContent = (0, import_react23.useContext)(DataStateContentContext);
2443
2462
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
2444
2463
  import_react_native16.View,
2445
2464
  {
@@ -2473,8 +2492,8 @@ function KjunEmpty({
2473
2492
  }
2474
2493
  function KjunSpinner({ size = "md", text }) {
2475
2494
  const m = useKjunMessages();
2476
- const { colors } = useKjunStyles(), reduced = useReducedMotion(), rotation = (0, import_react22.useRef)(new import_react_native16.Animated.Value(0)).current;
2477
- (0, import_react22.useEffect)(() => {
2495
+ const { colors } = useKjunStyles(), reduced = useReducedMotion(), rotation = (0, import_react23.useRef)(new import_react_native16.Animated.Value(0)).current;
2496
+ (0, import_react23.useEffect)(() => {
2478
2497
  if (reduced) return;
2479
2498
  const loop = import_react_native16.Animated.loop(
2480
2499
  import_react_native16.Animated.timing(rotation, {
@@ -2563,7 +2582,7 @@ function KjunAlert({
2563
2582
  }) {
2564
2583
  const m = useKjunMessages();
2565
2584
  const { colors } = useKjunStyles();
2566
- const [visible, setVisible] = (0, import_react22.useState)(true);
2585
+ const [visible, setVisible] = (0, import_react23.useState)(true);
2567
2586
  if (!visible) return null;
2568
2587
  const tone = variant || (type === "error" ? "danger" : type), small = size === "sm";
2569
2588
  const geometry2 = import_tokens14.tokens.extensions.alert;
@@ -2767,7 +2786,7 @@ function KjunProgress({
2767
2786
 
2768
2787
  // packages/native/src/skeleton-base.tsx
2769
2788
  var import_tokens15 = require("@kjun-ui/tokens");
2770
- var import_react23 = require("react");
2789
+ var import_react24 = require("react");
2771
2790
  var import_react_native17 = require("react-native");
2772
2791
  var import_jsx_runtime16 = require("react/jsx-runtime");
2773
2792
  function KjunSkeleton({
@@ -2779,8 +2798,8 @@ function KjunSkeleton({
2779
2798
  animated = true,
2780
2799
  rounded = true
2781
2800
  }) {
2782
- const { colors } = useKjunStyles(), reduced = useReducedMotion(), opacity = (0, import_react23.useRef)(new import_react_native17.Animated.Value(1)).current;
2783
- (0, import_react23.useEffect)(() => {
2801
+ const { colors } = useKjunStyles(), reduced = useReducedMotion(), opacity = (0, import_react24.useRef)(new import_react_native17.Animated.Value(1)).current;
2802
+ (0, import_react24.useEffect)(() => {
2784
2803
  if (!animated || reduced) return;
2785
2804
  const loop = import_react_native17.Animated.loop(
2786
2805
  import_react_native17.Animated.sequence([
@@ -2935,7 +2954,7 @@ function KjunSkeleton({
2935
2954
  }
2936
2955
 
2937
2956
  // packages/native/src/layout-primitives.tsx
2938
- var import_react24 = require("react");
2957
+ var import_react25 = require("react");
2939
2958
  var import_react_native18 = require("react-native");
2940
2959
  var import_tokens16 = require("@kjun-ui/tokens");
2941
2960
 
@@ -2958,13 +2977,15 @@ var toneRole = {
2958
2977
  inverse: "onBrand"
2959
2978
  };
2960
2979
  var textAlign = { start: "left", center: "center", end: "right" };
2961
- function KjunText({ variant = "body", tone = "default", align, maxLines, style, children }) {
2980
+ function KjunText({ variant = "body", tone = "default", align, maxLines, style, children, ...rest }) {
2981
+ var _a;
2962
2982
  const { colors, fontFamily, numericFontFamily } = useKjunStyles();
2963
2983
  const lines = maxLines && maxLines > 0 ? Math.floor(maxLines) : void 0;
2964
2984
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2965
2985
  KText,
2966
2986
  {
2967
- accessibilityRole: titleVariants.includes(variant) ? "header" : void 0,
2987
+ ...rest,
2988
+ accessibilityRole: (_a = rest.accessibilityRole) != null ? _a : titleVariants.includes(variant) ? "header" : void 0,
2968
2989
  numberOfLines: lines,
2969
2990
  ellipsizeMode: lines ? "tail" : void 0,
2970
2991
  style: [
@@ -2979,23 +3000,23 @@ function KjunText({ variant = "body", tone = "default", align, maxLines, style,
2979
3000
  );
2980
3001
  }
2981
3002
  function flatten(children, path = "") {
2982
- return import_react24.Children.toArray(children).flatMap((child, i) => {
3003
+ return import_react25.Children.toArray(children).flatMap((child, i) => {
2983
3004
  const key = `${path}${i}`;
2984
- return (0, import_react24.isValidElement)(child) && child.type === import_react24.Fragment ? flatten(child.props.children, `${key}.`) : [[key, child]];
3005
+ return (0, import_react25.isValidElement)(child) && child.type === import_react25.Fragment ? flatten(child.props.children, `${key}.`) : [[key, child]];
2985
3006
  });
2986
3007
  }
2987
3008
  function items(children, childAlign) {
2988
3009
  return flatten(children).map(([key, child]) => {
2989
3010
  if (typeof child === "string" || typeof child === "number") return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(KText, { children: child }, key);
2990
- if ((0, import_react24.isValidElement)(child) && child.type === KjunFormGroup) {
3011
+ if ((0, import_react25.isValidElement)(child) && child.type === KjunFormGroup) {
2991
3012
  const group = child;
2992
- return (0, import_react24.cloneElement)(group, { key, style: [{ marginBottom: 0 }, group.props.style] });
3013
+ return (0, import_react25.cloneElement)(group, { key, style: [{ marginBottom: 0 }, group.props.style] });
2993
3014
  }
2994
- if (childAlign && (0, import_react24.isValidElement)(child) && (child.type === KjunButton || child.type === KjunBadge)) {
3015
+ if (childAlign && (0, import_react25.isValidElement)(child) && (child.type === KjunButton || child.type === KjunBadge)) {
2995
3016
  const control = child;
2996
- return (0, import_react24.cloneElement)(control, { key, style: [{ alignSelf: childAlign }, control.props.style] });
3017
+ return (0, import_react25.cloneElement)(control, { key, style: [{ alignSelf: childAlign }, control.props.style] });
2997
3018
  }
2998
- return (0, import_react24.isValidElement)(child) ? (0, import_react24.cloneElement)(child, { key }) : child;
3019
+ return (0, import_react25.isValidElement)(child) ? (0, import_react25.cloneElement)(child, { key }) : child;
2999
3020
  });
3000
3021
  }
3001
3022
  var followed = (align) => align && align !== "stretch" ? crossAxis[align] : void 0;
@@ -3032,13 +3053,13 @@ function KjunInline({ gap = "sm", align, justify = "start", wrap = true, style,
3032
3053
  }
3033
3054
 
3034
3055
  // shared/package-runtime/feedback-dialog.ts
3035
- var import_react25 = require("react");
3056
+ var import_react26 = require("react");
3036
3057
  function useFeedbackDialog(controller, request, messages = kjunMessagesKo) {
3037
3058
  const options = request.options;
3038
- const [value, setValue] = (0, import_react25.useState)(options.initialValue || "");
3039
- const [error, setError] = (0, import_react25.useState)("");
3040
- const [busy, setBusy] = (0, import_react25.useState)(false);
3041
- const working = (0, import_react25.useRef)(false);
3059
+ const [value, setValue] = (0, import_react26.useState)(options.initialValue || "");
3060
+ const [error, setError] = (0, import_react26.useState)("");
3061
+ const [busy, setBusy] = (0, import_react26.useState)(false);
3062
+ const working = (0, import_react26.useRef)(false);
3042
3063
  const active = () => {
3043
3064
  var _a;
3044
3065
  return ((_a = controller.getSnapshot().current) == null ? void 0 : _a.id) === request.id;
@@ -3081,11 +3102,11 @@ function useFeedbackDialog(controller, request, messages = kjunMessagesKo) {
3081
3102
 
3082
3103
  // shared/package-runtime/feedback-motion.ts
3083
3104
  var import_tokens17 = require("@kjun-ui/tokens");
3084
- var import_react26 = require("react");
3105
+ var import_react27 = require("react");
3085
3106
  function usePresentedToasts(toasts, reduced) {
3086
- const [items2, setItems] = (0, import_react26.useState)([]);
3087
- const removals = (0, import_react26.useRef)(/* @__PURE__ */ new Map());
3088
- (0, import_react26.useLayoutEffect)(() => {
3107
+ const [items2, setItems] = (0, import_react27.useState)([]);
3108
+ const removals = (0, import_react27.useRef)(/* @__PURE__ */ new Map());
3109
+ (0, import_react27.useLayoutEffect)(() => {
3089
3110
  const ids = new Set(toasts.map((toast) => toast.id));
3090
3111
  setItems((previous) => {
3091
3112
  const old = new Map(previous.map((item) => [item.toast.id, item]));
@@ -3107,15 +3128,15 @@ function usePresentedToasts(toasts, reduced) {
3107
3128
  removals.current.clear();
3108
3129
  }
3109
3130
  }, [toasts, reduced]);
3110
- (0, import_react26.useLayoutEffect)(() => () => {
3131
+ (0, import_react27.useLayoutEffect)(() => () => {
3111
3132
  for (const timer of removals.current.values()) clearTimeout(timer);
3112
3133
  removals.current.clear();
3113
3134
  }, []);
3114
3135
  return items2;
3115
3136
  }
3116
3137
  function usePresentedRequest(current, reduced) {
3117
- const [request, setRequest] = (0, import_react26.useState)(current);
3118
- (0, import_react26.useLayoutEffect)(() => {
3138
+ const [request, setRequest] = (0, import_react27.useState)(current);
3139
+ (0, import_react27.useLayoutEffect)(() => {
3119
3140
  if (!request || request.id === (current == null ? void 0 : current.id) || reduced) {
3120
3141
  setRequest(current);
3121
3142
  return;
@@ -3127,14 +3148,14 @@ function usePresentedRequest(current, reduced) {
3127
3148
  }
3128
3149
 
3129
3150
  // packages/native/src/feedback-context.ts
3130
- var import_react27 = require("react");
3131
- var KjunFeedbackContext = (0, import_react27.createContext)(null);
3151
+ var import_react28 = require("react");
3152
+ var KjunFeedbackContext = (0, import_react28.createContext)(null);
3132
3153
  function useOptionalKjunFeedback() {
3133
- return (0, import_react27.useContext)(KjunFeedbackContext);
3154
+ return (0, import_react28.useContext)(KjunFeedbackContext);
3134
3155
  }
3135
3156
 
3136
3157
  // packages/native/src/toast-stack.tsx
3137
- var import_react29 = require("react");
3158
+ var import_react30 = require("react");
3138
3159
  var import_react_native20 = require("react-native");
3139
3160
  var import_tokens19 = require("@kjun-ui/tokens");
3140
3161
 
@@ -3152,7 +3173,7 @@ function shadowLayers(layers, colors) {
3152
3173
 
3153
3174
  // packages/native/src/toast.tsx
3154
3175
  var import_tokens18 = require("@kjun-ui/tokens");
3155
- var import_react28 = require("react");
3176
+ var import_react29 = require("react");
3156
3177
  var import_react_native19 = require("react-native");
3157
3178
  var import_jsx_runtime18 = require("react/jsx-runtime");
3158
3179
  function ToastView({
@@ -3160,14 +3181,14 @@ function ToastView({
3160
3181
  controller
3161
3182
  }) {
3162
3183
  const m = useKjunMessages();
3163
- const { colors } = useKjunStyles(), reduced = useReducedMotion(), progress = (0, import_react28.useRef)(new import_react_native19.Animated.Value(1)).current;
3164
- const reasons = (0, import_react28.useRef)({
3184
+ const { colors } = useKjunStyles(), reduced = useReducedMotion(), progress = (0, import_react29.useRef)(new import_react_native19.Animated.Value(1)).current;
3185
+ const reasons = (0, import_react29.useRef)({
3165
3186
  hover: /* @__PURE__ */ Symbol("hover"),
3166
3187
  actionHover: /* @__PURE__ */ Symbol("actionHover"),
3167
3188
  closeHover: /* @__PURE__ */ Symbol("closeHover"),
3168
3189
  focus: /* @__PURE__ */ Symbol("focus")
3169
3190
  }).current;
3170
- (0, import_react28.useEffect)(() => () => {
3191
+ (0, import_react29.useEffect)(() => () => {
3171
3192
  for (const reason of Object.values(reasons)) controller.resumeToast(toast.id, reason);
3172
3193
  }, [controller, toast.id, reasons]);
3173
3194
  const tone = toast.type === "error" ? "danger" : toast.type || "info", icon = {
@@ -3176,7 +3197,7 @@ function ToastView({
3176
3197
  warning: "alert-triangle",
3177
3198
  info: "info-circle"
3178
3199
  }[tone];
3179
- (0, import_react28.useEffect)(() => {
3200
+ (0, import_react29.useEffect)(() => {
3180
3201
  progress.stopAnimation();
3181
3202
  progress.setValue(toast.duration ? toast.remaining / toast.duration : 1);
3182
3203
  if (!toast.paused && !reduced) {
@@ -3296,17 +3317,17 @@ function useToastReflow(_root, _reduced) {
3296
3317
  // packages/native/src/toast-stack.tsx
3297
3318
  var import_jsx_runtime19 = require("react/jsx-runtime");
3298
3319
  function ToastSlot({ item, controller, boxes }) {
3299
- const born = (0, import_react29.useRef)(Math.min(import_tokens19.tokens.motion.toastEnter, Date.now() - item.born)).current;
3320
+ const born = (0, import_react30.useRef)(Math.min(import_tokens19.tokens.motion.toastEnter, Date.now() - item.born)).current;
3300
3321
  const motion = useNativePresence(!item.exiting, import_tokens19.tokens.motion.toastEnter - born, import_tokens19.tokens.motion.toastExit, true, born / import_tokens19.tokens.motion.toastEnter);
3301
- const offset = (0, import_react29.useRef)(new import_react_native20.Animated.Value(0)).current;
3322
+ const offset = (0, import_react30.useRef)(new import_react_native20.Animated.Value(0)).current;
3302
3323
  const box = boxes.get(item.toast.id);
3303
- (0, import_react29.useLayoutEffect)(() => {
3324
+ (0, import_react30.useLayoutEffect)(() => {
3304
3325
  if (motion.reduced) {
3305
3326
  offset.stopAnimation();
3306
3327
  offset.setValue(0);
3307
3328
  }
3308
3329
  }, [motion.reduced, offset]);
3309
- (0, import_react29.useLayoutEffect)(() => () => offset.stopAnimation(), [offset]);
3330
+ (0, import_react30.useLayoutEffect)(() => () => offset.stopAnimation(), [offset]);
3310
3331
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3311
3332
  import_react_native20.Animated.View,
3312
3333
  {
@@ -3345,9 +3366,9 @@ function ToastSlot({ item, controller, boxes }) {
3345
3366
  function ToastStack({ items: items2, controller }) {
3346
3367
  var _a;
3347
3368
  const layers = useLayerState();
3348
- const root = (0, import_react29.useRef)(null), reduced = useReducedMotion();
3369
+ const root = (0, import_react30.useRef)(null), reduced = useReducedMotion();
3349
3370
  useToastReflow(root, reduced);
3350
- const boxes = (0, import_react29.useRef)(/* @__PURE__ */ new Map()).current;
3371
+ const boxes = (0, import_react30.useRef)(/* @__PURE__ */ new Map()).current;
3351
3372
  for (const id of boxes.keys()) if (!items2.some((item) => item.toast.id === id)) boxes.delete(id);
3352
3373
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native20.View, { ref: root, pointerEvents: "box-none", style: {
3353
3374
  position: "absolute",
@@ -3362,7 +3383,7 @@ function ToastStack({ items: items2, controller }) {
3362
3383
 
3363
3384
  // packages/native/src/feedback.tsx
3364
3385
  var import_tokens20 = require("@kjun-ui/tokens");
3365
- var import_react30 = require("react");
3386
+ var import_react31 = require("react");
3366
3387
  var import_react_native21 = require("react-native");
3367
3388
  var import_tokens21 = require("@kjun-ui/tokens");
3368
3389
  var import_jsx_runtime20 = require("react/jsx-runtime");
@@ -3414,11 +3435,11 @@ function FeedbackDialog({
3414
3435
  );
3415
3436
  }
3416
3437
  function KjunFeedbackProvider({ children, style, controller: controllerProp }) {
3417
- const [owned] = (0, import_react30.useState)(() => controllerProp ? void 0 : (0, import_tokens20.createFeedbackController)());
3438
+ const [owned] = (0, import_react31.useState)(() => controllerProp ? void 0 : (0, import_tokens20.createFeedbackController)());
3418
3439
  const controller = controllerProp ? (0, import_tokens20.feedbackControllerOf)(controllerProp) : owned;
3419
- (0, import_react30.useEffect)(() => controller.attach(), [controller]);
3420
- const life = (0, import_react30.useRef)(0);
3421
- const snapshot = (0, import_react30.useSyncExternalStore)(
3440
+ (0, import_react31.useEffect)(() => controller.attach(), [controller]);
3441
+ const life = (0, import_react31.useRef)(0);
3442
+ const snapshot = (0, import_react31.useSyncExternalStore)(
3422
3443
  controller.subscribe,
3423
3444
  controller.getSnapshot,
3424
3445
  controller.getSnapshot
@@ -3426,7 +3447,7 @@ function KjunFeedbackProvider({ children, style, controller: controllerProp }) {
3426
3447
  const reduced = useReducedMotion();
3427
3448
  const toasts = usePresentedToasts(snapshot.toasts, reduced);
3428
3449
  const dialog = usePresentedRequest(snapshot.current, reduced);
3429
- (0, import_react30.useEffect)(() => {
3450
+ (0, import_react31.useEffect)(() => {
3430
3451
  const version = ++life.current;
3431
3452
  return () => {
3432
3453
  queueMicrotask(() => {
@@ -3434,8 +3455,8 @@ function KjunFeedbackProvider({ children, style, controller: controllerProp }) {
3434
3455
  });
3435
3456
  };
3436
3457
  }, [controller]);
3437
- const [layers, setLayers] = (0, import_react30.useState)([]);
3438
- const register = (0, import_react30.useCallback)((id) => {
3458
+ const [layers, setLayers] = (0, import_react31.useState)([]);
3459
+ const register = (0, import_react31.useCallback)((id) => {
3439
3460
  setLayers((previous) => [...previous, id]);
3440
3461
  return () => setLayers((previous) => previous.filter((item) => item !== id));
3441
3462
  }, []);
@@ -3466,7 +3487,7 @@ function KjunFeedbackProvider({ children, style, controller: controllerProp }) {
3466
3487
  // packages/native/src/choice-controls.tsx
3467
3488
  var import_react_native22 = require("react-native");
3468
3489
  var import_tokens22 = require("@kjun-ui/tokens");
3469
- var import_react31 = require("react");
3490
+ var import_react32 = require("react");
3470
3491
  var import_react_native23 = require("react-native");
3471
3492
  var import_jsx_runtime21 = require("react/jsx-runtime");
3472
3493
  function KjunCheckbox({
@@ -3537,7 +3558,7 @@ function KjunCheckbox({
3537
3558
  }
3538
3559
  function useControlFocus(disabled) {
3539
3560
  const { colors } = useKjunStyles();
3540
- const [focused, setFocused] = (0, import_react31.useState)(false);
3561
+ const [focused, setFocused] = (0, import_react32.useState)(false);
3541
3562
  return {
3542
3563
  handlers: {
3543
3564
  focusRing: false,
@@ -3566,7 +3587,7 @@ function KjunSwitch({
3566
3587
  const offset = width - height;
3567
3588
  const motion = useMotionValue(value ? 1 : 0);
3568
3589
  const focus = useControlFocus(disabled);
3569
- const [hovered, setHovered] = (0, import_react31.useState)(false);
3590
+ const [hovered, setHovered] = (0, import_react32.useState)(false);
3570
3591
  const trackColor = typeof colors.active === "string" && typeof colors.brand === "string" ? motion.interpolate({ inputRange: [0, 1], outputRange: [colors.active, colors.brand] }) : value ? colors.brand : colors.active;
3571
3592
  const offRole = disabled ? colors.borderSecondary : colors.borderStrong;
3572
3593
  const thumbColor = typeof offRole === "string" && typeof colors.switchThumbOn === "string" ? motion.interpolate({ inputRange: [0, 1], outputRange: [offRole, colors.switchThumbOn] }) : value ? colors.switchThumbOn : offRole;
@@ -3651,7 +3672,7 @@ function KjunSwitch({
3651
3672
  }
3652
3673
  );
3653
3674
  }
3654
- var RadioContext = (0, import_react31.createContext)(null);
3675
+ var RadioContext = (0, import_react32.createContext)(null);
3655
3676
  function KjunRadio({
3656
3677
  value,
3657
3678
  val,
@@ -3662,10 +3683,10 @@ function KjunRadio({
3662
3683
  onValueChange,
3663
3684
  onChange
3664
3685
  }) {
3665
- const group = (0, import_react31.useContext)(RadioContext), { colors } = useKjunStyles(), selected = (group ? group.value : value) === val;
3686
+ const group = (0, import_react32.useContext)(RadioContext), { colors } = useKjunStyles(), selected = (group ? group.value : value) === val;
3666
3687
  const geometry2 = import_tokens22.tokens.extensions.radio;
3667
- const [hovered, setHovered] = (0, import_react31.useState)(false);
3668
- (0, import_react31.useEffect)(() => {
3688
+ const [hovered, setHovered] = (0, import_react32.useState)(false);
3689
+ (0, import_react32.useEffect)(() => {
3669
3690
  if (disabled) setHovered(false);
3670
3691
  }, [disabled]);
3671
3692
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
@@ -3737,7 +3758,7 @@ function KjunRadioGroup({
3737
3758
  onValueChange,
3738
3759
  onChange
3739
3760
  }) {
3740
- const root = (0, import_react31.useRef)(null);
3761
+ const root = (0, import_react32.useRef)(null);
3741
3762
  useRadioKeyboard(root);
3742
3763
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3743
3764
  RadioContext.Provider,
@@ -3777,11 +3798,11 @@ function KjunRadioGroup({
3777
3798
 
3778
3799
  // packages/native/src/selection-groups.tsx
3779
3800
  var import_tokens24 = require("@kjun-ui/tokens");
3780
- var import_react33 = require("react");
3801
+ var import_react34 = require("react");
3781
3802
  var import_react_native25 = require("react-native");
3782
3803
 
3783
3804
  // packages/native/src/button-group-motion.tsx
3784
- var import_react32 = require("react");
3805
+ var import_react33 = require("react");
3785
3806
  var import_react_native24 = require("react-native");
3786
3807
  var import_tokens23 = require("@kjun-ui/tokens");
3787
3808
  var import_jsx_runtime22 = require("react/jsx-runtime");
@@ -3790,16 +3811,16 @@ var easing = import_react_native24.Easing.bezier(...curve);
3790
3811
  var sameBox = (a, b) => a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
3791
3812
  function useButtonGroupMotion(selected) {
3792
3813
  const reduced = useReducedMotion();
3793
- const [layouts, setLayouts] = (0, import_react32.useState)(/* @__PURE__ */ new Map());
3794
- const x = (0, import_react32.useRef)(new import_react_native24.Animated.Value(0)).current;
3795
- const width = (0, import_react32.useRef)(new import_react_native24.Animated.Value(0)).current;
3796
- const previous = (0, import_react32.useRef)(null);
3814
+ const [layouts, setLayouts] = (0, import_react33.useState)(/* @__PURE__ */ new Map());
3815
+ const x = (0, import_react33.useRef)(new import_react_native24.Animated.Value(0)).current;
3816
+ const width = (0, import_react33.useRef)(new import_react_native24.Animated.Value(0)).current;
3817
+ const previous = (0, import_react33.useRef)(null);
3797
3818
  const layout = selected && layouts.get(selected.value);
3798
- const updateLayout = (0, import_react32.useCallback)((value, box) => {
3819
+ const updateLayout = (0, import_react33.useCallback)((value, box) => {
3799
3820
  setLayouts((old) => old.has(value) && sameBox(old.get(value), box) ? old : new Map(old).set(value, box));
3800
3821
  }, []);
3801
- const measure = (0, import_react32.useCallback)((value, event) => updateLayout(value, event.nativeEvent.layout), [updateLayout]);
3802
- (0, import_react32.useLayoutEffect)(() => {
3822
+ const measure = (0, import_react33.useCallback)((value, event) => updateLayout(value, event.nativeEvent.layout), [updateLayout]);
3823
+ (0, import_react33.useLayoutEffect)(() => {
3803
3824
  const old = previous.current;
3804
3825
  if (!selected || !layout) {
3805
3826
  previous.current = null;
@@ -3821,7 +3842,7 @@ function useButtonGroupMotion(selected) {
3821
3842
  }
3822
3843
  previous.current = { value: selected.value, box: layout, reduced };
3823
3844
  }, [selected == null ? void 0 : selected.value, layout, reduced, x, width]);
3824
- (0, import_react32.useEffect)(() => () => {
3845
+ (0, import_react33.useEffect)(() => () => {
3825
3846
  x.stopAnimation();
3826
3847
  width.stopAnimation();
3827
3848
  }, [x, width]);
@@ -3829,9 +3850,9 @@ function useButtonGroupMotion(selected) {
3829
3850
  }
3830
3851
  function ButtonGroupLabel({ label, active, size, reduced }) {
3831
3852
  const { colors, fontFamily } = useKjunStyles();
3832
- const progress = (0, import_react32.useRef)(new import_react_native24.Animated.Value(active ? 1 : 0)).current;
3853
+ const progress = (0, import_react33.useRef)(new import_react_native24.Animated.Value(active ? 1 : 0)).current;
3833
3854
  const interpolate = typeof colors.textSecondary === "string" && typeof colors.text === "string";
3834
- (0, import_react32.useEffect)(() => {
3855
+ (0, import_react33.useEffect)(() => {
3835
3856
  progress.stopAnimation();
3836
3857
  if (reduced || !interpolate) progress.setValue(active ? 1 : 0);
3837
3858
  else import_react_native24.Animated.timing(progress, { toValue: active ? 1 : 0, duration: import_tokens23.tokens.motion.indicator, easing, useNativeDriver: false }).start();
@@ -3878,9 +3899,9 @@ function KjunButtonGroup({
3878
3899
  const motion = useButtonGroupMotion(selected);
3879
3900
  const padding = import_tokens24.tokens.buttonGroup.padding[size];
3880
3901
  const itemRadius = import_tokens24.tokens.buttonGroup.itemRadii[size];
3881
- const itemRefs = (0, import_react33.useRef)(/* @__PURE__ */ new Map());
3902
+ const itemRefs = (0, import_react34.useRef)(/* @__PURE__ */ new Map());
3882
3903
  const { updateLayout } = motion;
3883
- (0, import_react33.useLayoutEffect)(() => {
3904
+ (0, import_react34.useLayoutEffect)(() => {
3884
3905
  for (const [value2, node] of itemRefs.current) {
3885
3906
  node.measure((x, y, width, height) => updateLayout(value2, { x, y, width, height }));
3886
3907
  }
@@ -4049,7 +4070,7 @@ function KjunFilterGroup({
4049
4070
  paddingHorizontal: { xs: import_tokens24.tokens.dimension.value8, sm: import_tokens24.tokens.dimension.value12, md: import_tokens24.tokens.dimension.value12, lg: import_tokens24.tokens.dimension.value14, xl: import_tokens24.tokens.dimension.value16 }[size],
4050
4071
  // Corners follow the button scale with the height, so every size keeps the same shape.
4051
4072
  borderRadius: import_tokens24.tokens.button.radii[size],
4052
- backgroundColor: active ? colors.text : colors.secondary
4073
+ backgroundColor: active ? colors.filterSelectedBg : colors.secondary
4053
4074
  },
4054
4075
  children: [
4055
4076
  option.dot && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
@@ -4068,7 +4089,7 @@ function KjunFilterGroup({
4068
4089
  {
4069
4090
  name: option.icon,
4070
4091
  size: import_tokens24.tokens.extensions.selection.iconSize,
4071
- color: active ? colors.inverse : colors.textSecondary
4092
+ color: active ? colors.filterSelectedText : colors.textSecondary
4072
4093
  }
4073
4094
  ),
4074
4095
  /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react_native25.View, { children: [
@@ -4091,7 +4112,7 @@ function KjunFilterGroup({
4091
4112
  right: 0,
4092
4113
  top: 0,
4093
4114
  ...selectionTypeStyle(size, active),
4094
- color: active ? colors.inverse : colors.textSecondary
4115
+ color: active ? colors.filterSelectedText : colors.textSecondary
4095
4116
  },
4096
4117
  children: option.label
4097
4118
  }
@@ -4119,10 +4140,10 @@ function KjunFilterGroup({
4119
4140
 
4120
4141
  // packages/native/src/textarea.tsx
4121
4142
  var import_tokens25 = require("@kjun-ui/tokens");
4122
- var import_react34 = require("react");
4143
+ var import_react35 = require("react");
4123
4144
  var import_react_native26 = require("react-native");
4124
4145
  var import_jsx_runtime24 = require("react/jsx-runtime");
4125
- var KjunTextarea = (0, import_react34.forwardRef)(
4146
+ var KjunTextarea = (0, import_react35.forwardRef)(
4126
4147
  function KjunTextarea2({
4127
4148
  value,
4128
4149
  size = "md",
@@ -4192,7 +4213,7 @@ var KjunTextarea = (0, import_react34.forwardRef)(
4192
4213
 
4193
4214
  // packages/native/src/drawer.tsx
4194
4215
  var import_tokens26 = require("@kjun-ui/tokens");
4195
- var import_react35 = require("react");
4216
+ var import_react36 = require("react");
4196
4217
  var import_react_native27 = require("react-native");
4197
4218
 
4198
4219
  // shared/package-runtime/drawer-geometry.ts
@@ -4221,8 +4242,8 @@ function drawerFrame({ position, windowHeight, safeAreaTop, safeAreaBottom, gap,
4221
4242
  var import_react_native28 = require("react-native");
4222
4243
  var import_jsx_runtime25 = require("react/jsx-runtime");
4223
4244
  function useKeyboardOverlap(enabled, windowHeight) {
4224
- const [overlap, setOverlap] = (0, import_react35.useState)(0);
4225
- (0, import_react35.useEffect)(() => {
4245
+ const [overlap, setOverlap] = (0, import_react36.useState)(0);
4246
+ (0, import_react36.useEffect)(() => {
4226
4247
  var _a, _b;
4227
4248
  setOverlap(0);
4228
4249
  const events = keyboardEvents(import_react_native28.Platform.OS);
@@ -4252,6 +4273,7 @@ function KjunDrawer({
4252
4273
  footer,
4253
4274
  children,
4254
4275
  onClose,
4276
+ onAfterClose,
4255
4277
  ariaLabel,
4256
4278
  safeAreaTop = 0,
4257
4279
  safeAreaBottom = 0,
@@ -4260,7 +4282,7 @@ function KjunDrawer({
4260
4282
  }) {
4261
4283
  const m = useKjunMessages();
4262
4284
  const { colors } = useKjunStyles(), window2 = (0, import_react_native28.useWindowDimensions)(), vertical = position === "left" || position === "right";
4263
- const [rootHeight, setRootHeight] = (0, import_react35.useState)(0);
4285
+ const [rootHeight, setRootHeight] = (0, import_react36.useState)(0);
4264
4286
  const keyboard = useKeyboardOverlap(open && avoidKeyboard, window2.height);
4265
4287
  const frame = drawerFrame({
4266
4288
  position,
@@ -4271,7 +4293,7 @@ function KjunDrawer({
4271
4293
  keyboard,
4272
4294
  maxHeight
4273
4295
  });
4274
- const [panelHeight, setPanelHeight] = (0, import_react35.useState)(0);
4296
+ const [panelHeight, setPanelHeight] = (0, import_react36.useState)(0);
4275
4297
  const motion = useNativePresence(open, import_tokens26.tokens.motion.layerEnter, import_tokens26.tokens.motion.layerExit, vertical || panelHeight > 0, 0, { enterEasing: motionEmphasized });
4276
4298
  const backdrop = useNativePresence(open, import_tokens26.tokens.motion.backdrop, import_tokens26.tokens.motion.backdropExit, true, 0, { fade: import_tokens26.tokens.motion.backdropExit });
4277
4299
  const close = () => {
@@ -4282,10 +4304,15 @@ function KjunDrawer({
4282
4304
  const layer = useLayer(open, motion.present, "window", () => {
4283
4305
  if (closeOnEsc) close();
4284
4306
  });
4307
+ const iosDismiss = import_react_native28.Platform.OS === "ios";
4308
+ useAfterClose(motion.present, onAfterClose, !iosDismiss);
4285
4309
  return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(LayerScope.Provider, { value: layer.id, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
4286
4310
  ScopedNativeModal,
4287
4311
  {
4288
4312
  visible: motion.present,
4313
+ onDismiss: iosDismiss ? () => {
4314
+ if (!open) onAfterClose == null ? void 0 : onAfterClose();
4315
+ } : void 0,
4289
4316
  transparent: true,
4290
4317
  onRequestClose: () => {
4291
4318
  if (closeOnEsc) close();
@@ -4412,13 +4439,13 @@ function KjunDrawer({
4412
4439
 
4413
4440
  // packages/native/src/dropdown.tsx
4414
4441
  var import_tokens29 = require("@kjun-ui/tokens");
4415
- var import_react38 = require("react");
4442
+ var import_react39 = require("react");
4416
4443
  var import_react_native32 = require("react-native");
4417
4444
 
4418
4445
  // packages/native/src/floating-panel.tsx
4419
4446
  var import_react_native29 = require("react-native");
4420
4447
  var import_tokens27 = require("@kjun-ui/tokens");
4421
- var import_react36 = require("react");
4448
+ var import_react37 = require("react");
4422
4449
  var import_react_native30 = require("react-native");
4423
4450
  var import_react_native_svg4 = __toESM(require("react-native-svg"), 1);
4424
4451
  var import_jsx_runtime26 = require("react/jsx-runtime");
@@ -4427,9 +4454,9 @@ function LayerTrigger({
4427
4454
  onPress,
4428
4455
  disabled = false
4429
4456
  }) {
4430
- if ((0, import_react36.isValidElement)(trigger)) {
4457
+ if ((0, import_react37.isValidElement)(trigger)) {
4431
4458
  const child = trigger;
4432
- return (0, import_react36.cloneElement)(child, {
4459
+ return (0, import_react37.cloneElement)(child, {
4433
4460
  disabled: disabled || child.props.disabled,
4434
4461
  onPress: () => {
4435
4462
  var _a, _b;
@@ -4464,14 +4491,14 @@ function FloatingPanel({
4464
4491
  }) {
4465
4492
  const m = useKjunMessages();
4466
4493
  ariaLabel != null ? ariaLabel : ariaLabel = m.select;
4467
- const [headerHeight, setHeaderHeight] = (0, import_react36.useState)(0);
4468
- const panelRef = (0, import_react36.useRef)(null);
4469
- const scroll = (0, import_react36.useRef)(null), viewport = (0, import_react36.useRef)(0);
4470
- const { colors } = useKjunStyles(), window2 = (0, import_react_native30.useWindowDimensions)(), [anchored, setAnchored] = (0, import_react36.useState)(false), [sized, setSized] = (0, import_react36.useState)(false), [anchor, setAnchor] = (0, import_react36.useState)({ x: 0, y: 0, width: 0, height: 0 }), [panel, setPanel] = (0, import_react36.useState)({ width: 0, height: 0 });
4471
- const measurement = (0, import_react36.useRef)(0);
4494
+ const [headerHeight, setHeaderHeight] = (0, import_react37.useState)(0);
4495
+ const panelRef = (0, import_react37.useRef)(null);
4496
+ const scroll = (0, import_react37.useRef)(null), viewport = (0, import_react37.useRef)(0);
4497
+ const { colors } = useKjunStyles(), window2 = (0, import_react_native30.useWindowDimensions)(), [anchored, setAnchored] = (0, import_react37.useState)(false), [sized, setSized] = (0, import_react37.useState)(false), [anchor, setAnchor] = (0, import_react37.useState)({ x: 0, y: 0, width: 0, height: 0 }), [panel, setPanel] = (0, import_react37.useState)({ width: 0, height: 0 });
4498
+ const measurement = (0, import_react37.useRef)(0);
4472
4499
  const motion = useNativePresence(open, tooltip ? import_tokens27.tokens.motion.tooltipEnter : import_tokens27.tokens.motion.popupEnter, tooltip ? import_tokens27.tokens.motion.tooltipExit : import_tokens27.tokens.motion.popupExit, anchored && sized);
4473
4500
  const layer = useLayer(open, motion.present, tooltip ? "tooltip" : "popup", () => onOpenChange(false), triggerRef);
4474
- (0, import_react36.useLayoutEffect)(() => {
4501
+ (0, import_react37.useLayoutEffect)(() => {
4475
4502
  var _a;
4476
4503
  const revision = ++measurement.current;
4477
4504
  if (open) setAnchored(false);
@@ -4484,20 +4511,20 @@ function FloatingPanel({
4484
4511
  measurement.current++;
4485
4512
  };
4486
4513
  }, [open, window2.width, window2.height, triggerRef]);
4487
- (0, import_react36.useLayoutEffect)(() => {
4514
+ (0, import_react37.useLayoutEffect)(() => {
4488
4515
  if (!motion.present) {
4489
4516
  setSized(false);
4490
4517
  setAnchored(false);
4491
4518
  }
4492
4519
  }, [motion.present]);
4493
- (0, import_react36.useEffect)(() => {
4520
+ (0, import_react37.useEffect)(() => {
4494
4521
  var _a;
4495
4522
  const row = reveal == null ? void 0 : reveal.current;
4496
4523
  if (!open || !anchored || !sized || !row || !viewport.current) return;
4497
4524
  const offset = row.y + row.height + (menu || field ? import_tokens27.tokens.extensions.menu.padding : 0) - viewport.current;
4498
4525
  if (offset > 0) (_a = scroll.current) == null ? void 0 : _a.scrollTo({ y: offset, animated: false });
4499
4526
  }, [open, anchored, sized, reveal, menu, field]);
4500
- (0, import_react36.useEffect)(() => {
4527
+ (0, import_react37.useEffect)(() => {
4501
4528
  if (!open || !anchored || !sized || !focusOnOpen || !panelRef.current) return;
4502
4529
  if (import_react_native30.Platform.OS === "web") {
4503
4530
  const node = panelRef.current;
@@ -4682,7 +4709,7 @@ function FloatingPanel({
4682
4709
  }
4683
4710
 
4684
4711
  // packages/native/src/popover.tsx
4685
- var import_react37 = require("react");
4712
+ var import_react38 = require("react");
4686
4713
  var import_react_native31 = require("react-native");
4687
4714
  var import_tokens28 = require("@kjun-ui/tokens");
4688
4715
  var import_jsx_runtime27 = require("react/jsx-runtime");
@@ -4703,7 +4730,7 @@ function KjunPopover({
4703
4730
  children
4704
4731
  }) {
4705
4732
  const { colors } = useKjunStyles();
4706
- const [internal, setInternal] = (0, import_react37.useState)(false), open = controlled != null ? controlled : internal, ref = (0, import_react37.useRef)(null);
4733
+ const [internal, setInternal] = (0, import_react38.useState)(false), open = controlled != null ? controlled : internal, ref = (0, import_react38.useRef)(null);
4707
4734
  const change = (v) => {
4708
4735
  setInternal(v);
4709
4736
  onOpenChange == null ? void 0 : onOpenChange(v);
@@ -4742,7 +4769,10 @@ function KjunPopover({
4742
4769
  ] });
4743
4770
  }
4744
4771
  function KjunTooltip({
4745
- content: message = "",
4772
+ content: body = "",
4773
+ title,
4774
+ align,
4775
+ width,
4746
4776
  placement = "top",
4747
4777
  delay = 0,
4748
4778
  children,
@@ -4751,13 +4781,14 @@ function KjunTooltip({
4751
4781
  open: controlled,
4752
4782
  onOpenChange
4753
4783
  }) {
4754
- const { colors } = useKjunStyles(), [internal, setInternal] = (0, import_react37.useState)(false), open = controlled != null ? controlled : internal, latest = (0, import_react37.useRef)({ open, onOpenChange }), setOpen = (value) => {
4784
+ const message = [title, body].filter(Boolean).join(", ");
4785
+ const { colors } = useKjunStyles(), [internal, setInternal] = (0, import_react38.useState)(false), open = controlled != null ? controlled : internal, latest = (0, import_react38.useRef)({ open, onOpenChange }), setOpen = (value) => {
4755
4786
  var _a, _b;
4756
4787
  if (latest.current.open === value) return;
4757
4788
  latest.current.open = value;
4758
4789
  setInternal(value);
4759
4790
  (_b = (_a = latest.current).onOpenChange) == null ? void 0 : _b.call(_a, value);
4760
- }, ref = (0, import_react37.useRef)(null), timer = (0, import_react37.useRef)(void 0);
4791
+ }, ref = (0, import_react38.useRef)(null), timer = (0, import_react38.useRef)(void 0);
4761
4792
  latest.current = { open, onOpenChange };
4762
4793
  const scopeActive = useActiveLayerScope();
4763
4794
  const hide = () => {
@@ -4769,11 +4800,11 @@ function KjunTooltip({
4769
4800
  clearTimeout(timer.current);
4770
4801
  timer.current = setTimeout(() => setOpen(true), delay);
4771
4802
  };
4772
- (0, import_react37.useEffect)(() => {
4803
+ (0, import_react38.useEffect)(() => {
4773
4804
  if (!scopeActive) hide();
4774
4805
  }, [scopeActive]);
4775
- (0, import_react37.useEffect)(() => () => clearTimeout(timer.current), []);
4776
- (0, import_react37.useEffect)(() => {
4806
+ (0, import_react38.useEffect)(() => () => clearTimeout(timer.current), []);
4807
+ (0, import_react38.useEffect)(() => {
4777
4808
  if (import_react_native31.Platform.OS !== "web" || !open) return;
4778
4809
  const escape = (event) => {
4779
4810
  if (event.key === "Escape") hide();
@@ -4781,7 +4812,7 @@ function KjunTooltip({
4781
4812
  document.addEventListener("keydown", escape);
4782
4813
  return () => document.removeEventListener("keydown", escape);
4783
4814
  }, [open]);
4784
- const child = (0, import_react37.isValidElement)(children) ? children : null;
4815
+ const child = (0, import_react38.isValidElement)(children) ? children : null;
4785
4816
  const childProps = child == null ? void 0 : child.props;
4786
4817
  const handlers = {
4787
4818
  // A hint that repeats the trigger's name would be read twice (icon-only RefreshButton, IconToggle).
@@ -4826,7 +4857,7 @@ function KjunTooltip({
4826
4857
  }
4827
4858
  };
4828
4859
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
4829
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react_native31.View, { ref, collapsable: false, style: { alignSelf: (childProps == null ? void 0 : childProps.block) ? "stretch" : "flex-start" }, children: child ? (0, import_react37.cloneElement)(child, handlers) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(AccessiblePressable, { ...handlers, children }) }),
4860
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react_native31.View, { ref, collapsable: false, style: { alignSelf: (childProps == null ? void 0 : childProps.block) ? "stretch" : "flex-start" }, children: child ? (0, import_react38.cloneElement)(child, handlers) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(AccessiblePressable, { ...handlers, children }) }),
4830
4861
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
4831
4862
  FloatingPanel,
4832
4863
  {
@@ -4836,25 +4867,29 @@ function KjunTooltip({
4836
4867
  triggerRef: ref,
4837
4868
  placement,
4838
4869
  ariaLabel: message,
4839
- children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
4870
+ children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
4840
4871
  import_react_native31.View,
4841
4872
  {
4842
4873
  style: {
4843
4874
  paddingVertical: import_tokens28.tokens.extensions.tooltip.paddingY,
4844
4875
  paddingHorizontal: import_tokens28.tokens.extensions.tooltip.paddingX,
4845
- maxWidth: import_tokens28.tokens.extensions.tooltip.maxWidth,
4876
+ maxWidth: width ? import_tokens28.tokens.extensions.tooltip.maxWidths[width] : import_tokens28.tokens.extensions.tooltip.maxWidth,
4846
4877
  backgroundColor: colors.chartTooltipBg
4847
4878
  },
4848
- children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
4849
- KText,
4850
- {
4851
- style: {
4852
- ...typeStyle("caption"),
4853
- color: colors.chartTooltipText
4854
- },
4855
- children: message
4856
- }
4857
- )
4879
+ children: [
4880
+ !!title && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(KText, { style: { ...typeStyle("controlSmall"), color: colors.chartTooltipText, ...align ? { textAlign: align === "center" ? "center" : "left" } : {} }, children: title }),
4881
+ !!body && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
4882
+ KText,
4883
+ {
4884
+ style: {
4885
+ ...typeStyle("caption"),
4886
+ color: colors.chartTooltipText,
4887
+ ...align ? { textAlign: align === "center" ? "center" : "left" } : {}
4888
+ },
4889
+ children: body
4890
+ }
4891
+ )
4892
+ ]
4858
4893
  }
4859
4894
  )
4860
4895
  }
@@ -4864,10 +4899,10 @@ function KjunTooltip({
4864
4899
 
4865
4900
  // packages/native/src/dropdown.tsx
4866
4901
  var import_jsx_runtime28 = require("react/jsx-runtime");
4867
- var DropdownContext = (0, import_react38.createContext)(null);
4868
- var MenuIconSlotContext = (0, import_react38.createContext)(false);
4902
+ var DropdownContext = (0, import_react39.createContext)(null);
4903
+ var MenuIconSlotContext = (0, import_react39.createContext)(false);
4869
4904
  function hasLeadingIcon(node) {
4870
- return import_react38.Children.toArray(node).some((child) => (0, import_react38.isValidElement)(child) && (child.type === import_react38.Fragment ? hasLeadingIcon(child.props.children) : child.type === KjunDropdownItem && !!child.props.icon));
4905
+ return import_react39.Children.toArray(node).some((child) => (0, import_react39.isValidElement)(child) && (child.type === import_react39.Fragment ? hasLeadingIcon(child.props.children) : child.type === KjunDropdownItem && !!child.props.icon));
4871
4906
  }
4872
4907
  function KjunDropdown({
4873
4908
  disabled = false,
@@ -4878,11 +4913,11 @@ function KjunDropdown({
4878
4913
  onClose
4879
4914
  }) {
4880
4915
  const m = useKjunMessages();
4881
- const [open, setOpen] = (0, import_react38.useState)(false), currentOpen = (0, import_react38.useRef)(false), ref = (0, import_react38.useRef)(null);
4916
+ const [open, setOpen] = (0, import_react39.useState)(false), currentOpen = (0, import_react39.useRef)(false), ref = (0, import_react39.useRef)(null);
4882
4917
  let control = trigger;
4883
- while ((0, import_react38.isValidElement)(control) && control.type === KjunTooltip)
4918
+ while ((0, import_react39.isValidElement)(control) && control.type === KjunTooltip)
4884
4919
  control = control.props.children;
4885
- const unavailable = disabled || (0, import_react38.isValidElement)(control) && !!(control.props.disabled || control.props.loading);
4920
+ const unavailable = disabled || (0, import_react39.isValidElement)(control) && !!(control.props.disabled || control.props.loading);
4886
4921
  const change = (v) => {
4887
4922
  if (v === currentOpen.current || v && unavailable) return;
4888
4923
  currentOpen.current = v;
@@ -4890,7 +4925,7 @@ function KjunDropdown({
4890
4925
  if (v) onOpen == null ? void 0 : onOpen();
4891
4926
  else onClose == null ? void 0 : onClose();
4892
4927
  };
4893
- (0, import_react38.useEffect)(() => {
4928
+ (0, import_react39.useEffect)(() => {
4894
4929
  if (unavailable) change(false);
4895
4930
  }, [unavailable]);
4896
4931
  return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(DropdownContext.Provider, { value: () => change(false), children: [
@@ -4924,8 +4959,8 @@ function KjunDropdownItem({
4924
4959
  children,
4925
4960
  onPress
4926
4961
  }) {
4927
- const { colors } = useKjunStyles(), close = (0, import_react38.useContext)(DropdownContext), color = variant === "danger" ? colors.danger : colors.text, iconColor = variant === "danger" ? colors.danger : colors.textSecondary, reserveIcon = (0, import_react38.useContext)(MenuIconSlotContext);
4928
- const [hovered, setHovered] = (0, import_react38.useState)(false);
4962
+ const { colors } = useKjunStyles(), close = (0, import_react39.useContext)(DropdownContext), color = variant === "danger" ? colors.danger : colors.text, iconColor = variant === "danger" ? colors.danger : colors.textSecondary, reserveIcon = (0, import_react39.useContext)(MenuIconSlotContext);
4963
+ const [hovered, setHovered] = (0, import_react39.useState)(false);
4929
4964
  return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
4930
4965
  AccessiblePressable,
4931
4966
  {
@@ -4986,7 +5021,7 @@ function KjunMenuButton({
4986
5021
  ...props
4987
5022
  }) {
4988
5023
  const m = useKjunMessages();
4989
- const [open, setOpen] = (0, import_react38.useState)(false);
5024
+ const [open, setOpen] = (0, import_react39.useState)(false);
4990
5025
  return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
4991
5026
  KjunDropdown,
4992
5027
  {
@@ -5026,7 +5061,7 @@ function KjunMenuButton({
5026
5061
  }
5027
5062
 
5028
5063
  // shared/package-runtime/combobox.ts
5029
- var import_react39 = require("react");
5064
+ var import_react40 = require("react");
5030
5065
 
5031
5066
  // shared/package-runtime/options.ts
5032
5067
  var optionValue = (option, key = "value") => typeof option === "object" ? option[key] : option;
@@ -5104,10 +5139,10 @@ function useComboboxModel({
5104
5139
  }) {
5105
5140
  const selected = options.find((option) => optionValue(option, valueKey) === value);
5106
5141
  const selectedLabel = selected !== void 0 ? optionLabel(selected, labelKey) : "";
5107
- const [query, setQuery] = (0, import_react39.useState)(selectedLabel);
5108
- const [open, setOpen] = (0, import_react39.useState)(false);
5109
- (0, import_react39.useEffect)(() => setQuery(selectedLabel), [value, selectedLabel]);
5110
- (0, import_react39.useEffect)(() => {
5142
+ const [query, setQuery] = (0, import_react40.useState)(selectedLabel);
5143
+ const [open, setOpen] = (0, import_react40.useState)(false);
5144
+ (0, import_react40.useEffect)(() => setQuery(selectedLabel), [value, selectedLabel]);
5145
+ (0, import_react40.useEffect)(() => {
5111
5146
  if (disabled) setOpen(false);
5112
5147
  }, [disabled]);
5113
5148
  const change = (next) => {
@@ -5141,7 +5176,7 @@ function useComboboxModel({
5141
5176
 
5142
5177
  // packages/native/src/combobox.tsx
5143
5178
  var import_tokens31 = require("@kjun-ui/tokens");
5144
- var import_react40 = require("react");
5179
+ var import_react41 = require("react");
5145
5180
  var import_react_native34 = require("react-native");
5146
5181
 
5147
5182
  // packages/native/src/select-options.tsx
@@ -5270,9 +5305,9 @@ function SearchField({
5270
5305
  onEnter
5271
5306
  }) {
5272
5307
  const m = useKjunMessages();
5273
- const searchId = "kjun-popup-search-" + (0, import_react40.useId)().replace(/:/g, "");
5274
- const clearing = (0, import_react40.useRef)(false);
5275
- const ref = (0, import_react40.useRef)(null), { colors } = useKjunStyles();
5308
+ const searchId = "kjun-popup-search-" + (0, import_react41.useId)().replace(/:/g, "");
5309
+ const clearing = (0, import_react41.useRef)(false);
5310
+ const ref = (0, import_react41.useRef)(null), { colors } = useKjunStyles();
5276
5311
  const focusGuard = usePopupFocusGuard(ref);
5277
5312
  const openFromPress = () => {
5278
5313
  if (!disabled) {
@@ -5372,7 +5407,7 @@ function SearchField({
5372
5407
 
5373
5408
  // packages/native/src/date-picker.tsx
5374
5409
  var import_tokens32 = require("@kjun-ui/tokens");
5375
- var import_react41 = require("react");
5410
+ var import_react42 = require("react");
5376
5411
  var import_react_native35 = require("react-native");
5377
5412
  var import_jsx_runtime31 = require("react/jsx-runtime");
5378
5413
  function displayMonth(value) {
@@ -5395,7 +5430,7 @@ function KjunDatePicker({
5395
5430
  const scope = useKjunMessageScope();
5396
5431
  placeholder != null ? placeholder : placeholder = scope.messages.datePlaceholder;
5397
5432
  const clear = clearable && !!value && !disabled;
5398
- const { colors } = useKjunStyles(), field = useKjunField(), [open, setOpen] = (0, import_react41.useState)(false), [month, setMonth] = (0, import_react41.useState)(() => displayMonth(value)), ref = (0, import_react41.useRef)(null), spec = import_tokens32.tokens.input[size];
5433
+ const { colors } = useKjunStyles(), field = useKjunField(), [open, setOpen] = (0, import_react42.useState)(false), [month, setMonth] = (0, import_react42.useState)(() => displayMonth(value)), ref = (0, import_react42.useRef)(null), spec = import_tokens32.tokens.input[size];
5399
5434
  const surface = useFieldSurface(size, { disabled, invalid: error || !!(field == null ? void 0 : field.error), open });
5400
5435
  const year = month.getFullYear(), m = month.getMonth(), first = new Date(year, m, 1).getDay(), count = new Date(year, m + 1, 0).getDate();
5401
5436
  const date = (day) => `${year}-${String(m + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
@@ -5570,10 +5605,10 @@ function KjunDatePicker({
5570
5605
  }
5571
5606
 
5572
5607
  // packages/native/src/search-input.tsx
5573
- var import_react43 = require("react");
5608
+ var import_react44 = require("react");
5574
5609
 
5575
5610
  // shared/package-runtime/search-input.ts
5576
- var import_react42 = require("react");
5611
+ var import_react43 = require("react");
5577
5612
  function useSearchInputModel({
5578
5613
  value,
5579
5614
  disabled = false,
@@ -5583,20 +5618,20 @@ function useSearchInputModel({
5583
5618
  onValueChange,
5584
5619
  onSearchError
5585
5620
  }, open) {
5586
- const [query, setQuery] = (0, import_react42.useState)(value), [options, setOptions] = (0, import_react42.useState)([]), [loading, setLoading] = (0, import_react42.useState)(false), [failed, setFailed] = (0, import_react42.useState)(false), version = (0, import_react42.useRef)(0), notify = (0, import_react42.useRef)(onSearchError);
5621
+ const [query, setQuery] = (0, import_react43.useState)(value), [options, setOptions] = (0, import_react43.useState)([]), [loading, setLoading] = (0, import_react43.useState)(false), [failed, setFailed] = (0, import_react43.useState)(false), version = (0, import_react43.useRef)(0), notify = (0, import_react43.useRef)(onSearchError);
5587
5622
  notify.current = onSearchError;
5588
- const timer = (0, import_react42.useRef)(void 0);
5589
- const notifyValue = (0, import_react42.useRef)(onValueChange);
5623
+ const timer = (0, import_react43.useRef)(void 0);
5624
+ const notifyValue = (0, import_react43.useRef)(onValueChange);
5590
5625
  notifyValue.current = onValueChange;
5591
- (0, import_react42.useLayoutEffect)(() => {
5626
+ (0, import_react43.useLayoutEffect)(() => {
5592
5627
  clearTimeout(timer.current);
5593
5628
  setQuery(value);
5594
5629
  }, [value]);
5595
- (0, import_react42.useLayoutEffect)(() => {
5630
+ (0, import_react43.useLayoutEffect)(() => {
5596
5631
  clearTimeout(timer.current);
5597
5632
  return () => clearTimeout(timer.current);
5598
5633
  }, [disabled, debounce, !!loadOptions]);
5599
- (0, import_react42.useEffect)(() => {
5634
+ (0, import_react43.useEffect)(() => {
5600
5635
  const current = ++version.current, abort = new AbortController();
5601
5636
  setFailed(false);
5602
5637
  if (!loadOptions || !open || disabled || query.length < minChars) {
@@ -5687,7 +5722,7 @@ function KjunSearchInput({
5687
5722
  placeholder != null ? placeholder : placeholder = m.search;
5688
5723
  emptyText != null ? emptyText : emptyText = m.noResults;
5689
5724
  errorText != null ? errorText : errorText = m.searchFailed;
5690
- const [open, setOpen] = (0, import_react43.useState)(false);
5725
+ const [open, setOpen] = (0, import_react44.useState)(false);
5691
5726
  const model = useSearchInputModel({ value, disabled, debounce, loadOptions, minChars, onValueChange, onSearchError }, open);
5692
5727
  const { query, options, loading, failed } = model;
5693
5728
  const update = (value2) => {
@@ -5749,30 +5784,30 @@ function KjunSearchInput({
5749
5784
 
5750
5785
  // packages/native/src/select-control.tsx
5751
5786
  var import_tokens34 = require("@kjun-ui/tokens");
5752
- var import_react46 = require("react");
5787
+ var import_react47 = require("react");
5753
5788
 
5754
5789
  // packages/native/src/compound-control.tsx
5755
- var import_react44 = require("react");
5790
+ var import_react45 = require("react");
5756
5791
  var import_react_native36 = require("react-native");
5757
5792
  var import_tokens33 = require("@kjun-ui/tokens");
5758
5793
  var import_jsx_runtime33 = require("react/jsx-runtime");
5759
- var CompoundControlContext = (0, import_react44.createContext)(null);
5794
+ var CompoundControlContext = (0, import_react45.createContext)(null);
5760
5795
  function CompoundTarget({ children, square = false }) {
5761
5796
  const touch = finePointer() ? void 0 : import_tokens33.tokens.native.minimumTouchTarget;
5762
5797
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_react_native36.View, { style: { minHeight: touch, minWidth: square ? touch : void 0, alignItems: "center", justifyContent: "center" }, children });
5763
5798
  }
5764
5799
 
5765
5800
  // shared/package-runtime/select.ts
5766
- var import_react45 = require("react");
5801
+ var import_react46 = require("react");
5767
5802
  function useSelectState(controlled, disabled, pageSize, onOpenChange) {
5768
- const [internal, setInternal] = (0, import_react45.useState)(false);
5769
- const [query, setQuery] = (0, import_react45.useState)("");
5770
- const [limit, setLimit] = (0, import_react45.useState)(pageSize);
5771
- const [session, setSession] = (0, import_react45.useState)(0);
5803
+ const [internal, setInternal] = (0, import_react46.useState)(false);
5804
+ const [query, setQuery] = (0, import_react46.useState)("");
5805
+ const [limit, setLimit] = (0, import_react46.useState)(pageSize);
5806
+ const [session, setSession] = (0, import_react46.useState)(0);
5772
5807
  const isControlled = typeof controlled === "boolean";
5773
5808
  const open = !disabled && (isControlled ? controlled : internal);
5774
- const previousOpen = (0, import_react45.useRef)(open);
5775
- (0, import_react45.useEffect)(() => {
5809
+ const previousOpen = (0, import_react46.useRef)(open);
5810
+ (0, import_react46.useEffect)(() => {
5776
5811
  if (disabled && !isControlled) setInternal(false);
5777
5812
  if (previousOpen.current && !open) {
5778
5813
  setQuery("");
@@ -5781,7 +5816,7 @@ function useSelectState(controlled, disabled, pageSize, onOpenChange) {
5781
5816
  }
5782
5817
  previousOpen.current = open;
5783
5818
  }, [open, disabled, isControlled, pageSize]);
5784
- (0, import_react45.useEffect)(() => {
5819
+ (0, import_react46.useEffect)(() => {
5785
5820
  setLimit(pageSize);
5786
5821
  }, [pageSize]);
5787
5822
  const changeOpen = (next) => {
@@ -5831,14 +5866,14 @@ function KjunSelect({
5831
5866
  placeholder != null ? placeholder : placeholder = m.select;
5832
5867
  searchPlaceholder != null ? searchPlaceholder : searchPlaceholder = m.search;
5833
5868
  const { open, query, limit, changeOpen, search, showMore } = useSelectState(controlled, disabled, optionPageSize, onOpenChange);
5834
- const { colors } = useKjunStyles(), field = useKjunField(), ref = (0, import_react46.useRef)(null), spec = import_tokens34.tokens.input[size];
5835
- const trigger = (0, import_react46.useRef)(null);
5836
- const compound = (0, import_react46.useContext)(CompoundControlContext);
5837
- (0, import_react46.useEffect)(() => {
5869
+ const { colors } = useKjunStyles(), field = useKjunField(), ref = (0, import_react47.useRef)(null), spec = import_tokens34.tokens.input[size];
5870
+ const trigger = (0, import_react47.useRef)(null);
5871
+ const compound = (0, import_react47.useContext)(CompoundControlContext);
5872
+ (0, import_react47.useEffect)(() => {
5838
5873
  if (compound == null ? void 0 : compound.focusRef) compound.focusRef.current = trigger.current;
5839
5874
  }, [compound == null ? void 0 : compound.focusRef]);
5840
5875
  const expanded = usePopupExpanded(open, ref);
5841
- const selectedRow = (0, import_react46.useRef)(null);
5876
+ const selectedRow = (0, import_react47.useRef)(null);
5842
5877
  const surface = useFieldSurface(size, { disabled, invalid: error || !!(field == null ? void 0 : field.error), open: expanded });
5843
5878
  const change = (v) => {
5844
5879
  onValueChange == null ? void 0 : onValueChange(v);
@@ -6036,16 +6071,17 @@ var import_tokens37 = require("@kjun-ui/tokens");
6036
6071
 
6037
6072
  // packages/native/src/collapse.tsx
6038
6073
  var import_tokens35 = require("@kjun-ui/tokens");
6039
- var import_react47 = require("react");
6074
+ var import_react48 = require("react");
6040
6075
  var import_react_native38 = require("react-native");
6041
6076
  var import_jsx_runtime35 = require("react/jsx-runtime");
6042
- function Collapse({ open, children }) {
6077
+ function Collapse({ open, keepMounted = false, children }) {
6043
6078
  const reduced = useReducedMotion();
6044
- const [retained, setRetained] = (0, import_react47.useState)(open), [height, setHeight] = (0, import_react47.useState)(null);
6045
- const value = (0, import_react47.useRef)(new import_react_native38.Animated.Value(0)).current;
6046
- const [settled, setSettled] = (0, import_react47.useState)(open);
6047
- const version = (0, import_react47.useRef)(0), first = (0, import_react47.useRef)(open);
6048
- (0, import_react47.useLayoutEffect)(() => {
6079
+ const [retained, setRetained] = (0, import_react48.useState)(open), [height, setHeight] = (0, import_react48.useState)(null);
6080
+ const value = (0, import_react48.useRef)(new import_react_native38.Animated.Value(0)).current;
6081
+ const [settled, setSettled] = (0, import_react48.useState)(open);
6082
+ const version = (0, import_react48.useRef)(0), first = (0, import_react48.useRef)(open), opened = (0, import_react48.useRef)(open);
6083
+ if (open) opened.current = true;
6084
+ (0, import_react48.useLayoutEffect)(() => {
6049
6085
  const revision = ++version.current;
6050
6086
  if (open) setRetained(true);
6051
6087
  if (height === null) return;
@@ -6072,13 +6108,13 @@ function Collapse({ open, children }) {
6072
6108
  value.stopAnimation();
6073
6109
  };
6074
6110
  }, [open, height, reduced, value]);
6075
- return open || retained ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
6111
+ return open || retained || keepMounted && opened.current ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
6076
6112
  import_react_native38.Animated.View,
6077
6113
  {
6078
6114
  pointerEvents: open ? "auto" : "none",
6079
6115
  accessibilityElementsHidden: !open,
6080
6116
  importantForAccessibility: open ? "auto" : "no-hide-descendants",
6081
- ...!open ? { inert: true } : {},
6117
+ ...!open ? { inert: true, "aria-hidden": true } : {},
6082
6118
  style: { height: settled && open ? void 0 : value, overflow: "hidden" },
6083
6119
  children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_react_native38.View, { onLayout: (event) => setHeight(event.nativeEvent.layout.height), children })
6084
6120
  }
@@ -6086,10 +6122,10 @@ function Collapse({ open, children }) {
6086
6122
  }
6087
6123
 
6088
6124
  // shared/package-runtime/navigation.ts
6089
- var import_react48 = require("react");
6125
+ var import_react49 = require("react");
6090
6126
  function useInitialTab(value, candidate, change) {
6091
- const requested = (0, import_react48.useRef)(/* @__PURE__ */ new Set());
6092
- (0, import_react48.useEffect)(() => {
6127
+ const requested = (0, import_react49.useRef)(/* @__PURE__ */ new Set());
6128
+ (0, import_react49.useEffect)(() => {
6093
6129
  if (value !== "") {
6094
6130
  requested.current.clear();
6095
6131
  return;
@@ -6100,8 +6136,8 @@ function useInitialTab(value, candidate, change) {
6100
6136
  }, [value, candidate, change]);
6101
6137
  }
6102
6138
  function useAccordionState(multiple) {
6103
- const [opened, setOpened] = (0, import_react48.useState)(/* @__PURE__ */ new Set());
6104
- (0, import_react48.useEffect)(() => {
6139
+ const [opened, setOpened] = (0, import_react49.useState)(/* @__PURE__ */ new Set());
6140
+ (0, import_react49.useEffect)(() => {
6105
6141
  if (!multiple) setOpened((previous) => previous.size > 1 ? new Set([...previous].slice(-1)) : previous);
6106
6142
  }, [multiple]);
6107
6143
  return {
@@ -6125,12 +6161,12 @@ function useAccordionState(multiple) {
6125
6161
  }
6126
6162
 
6127
6163
  // packages/native/src/navigation.tsx
6128
- var import_react50 = require("react");
6164
+ var import_react51 = require("react");
6129
6165
  var import_react_native40 = require("react-native");
6130
6166
 
6131
6167
  // packages/native/src/tabs.tsx
6132
6168
  var import_tokens36 = require("@kjun-ui/tokens");
6133
- var import_react49 = require("react");
6169
+ var import_react50 = require("react");
6134
6170
  var import_react_native39 = require("react-native");
6135
6171
 
6136
6172
  // packages/native/src/tabs-accessibility.ts
@@ -6141,7 +6177,7 @@ function usePanelFocus(_root) {
6141
6177
 
6142
6178
  // packages/native/src/tabs.tsx
6143
6179
  var import_jsx_runtime36 = require("react/jsx-runtime");
6144
- var TabContext = (0, import_react49.createContext)({ value: "", id: "", names: [] });
6180
+ var TabContext = (0, import_react50.createContext)({ value: "", id: "", names: [] });
6145
6181
  function KjunTabs({
6146
6182
  value,
6147
6183
  items: items2 = [],
@@ -6157,21 +6193,21 @@ function KjunTabs({
6157
6193
  var _a, _b;
6158
6194
  const m = useKjunMessages();
6159
6195
  ariaLabel != null ? ariaLabel : ariaLabel = m.tabs;
6160
- const { colors } = useKjunStyles(), panes = import_react49.Children.toArray(children).filter(
6161
- import_react49.isValidElement
6196
+ const { colors } = useKjunStyles(), panes = import_react50.Children.toArray(children).filter(
6197
+ import_react50.isValidElement
6162
6198
  );
6163
6199
  const tabs = items2.length ? items2 : panes.filter((child) => child.type === KjunTabPane).map((child) => child.props);
6164
- const id = "kjun-" + (0, import_react49.useId)(), root = (0, import_react49.useRef)(null);
6165
- const menuState = (0, import_react49.useRef)({ tabs, onTabMenu });
6200
+ const id = "kjun-" + (0, import_react50.useId)(), root = (0, import_react50.useRef)(null);
6201
+ const menuState = (0, import_react50.useRef)({ tabs, onTabMenu });
6166
6202
  menuState.current = { tabs, onTabMenu };
6167
- const pressedMenu = (0, import_react49.useRef)(null);
6168
- (0, import_react49.useLayoutEffect)(() => {
6203
+ const pressedMenu = (0, import_react50.useRef)(null);
6204
+ (0, import_react50.useLayoutEffect)(() => {
6169
6205
  if (!onTabMenu || !tabs.some((tab) => tab.name === pressedMenu.current && !tab.disabled)) pressedMenu.current = null;
6170
6206
  }, [tabs, onTabMenu]);
6171
6207
  const panelNames = panes.filter((child) => child.type === KjunTabPane).map((child) => child.props.name);
6172
6208
  const geometry2 = import_tokens36.tokens.extensions.tabs;
6173
6209
  const underline = variant === "underline";
6174
- const [hoveredTab, setHoveredTab] = (0, import_react49.useState)(null);
6210
+ const [hoveredTab, setHoveredTab] = (0, import_react50.useState)(null);
6175
6211
  const singleLine = import_react_native39.Platform.OS === "web" ? { whiteSpace: "nowrap" } : {};
6176
6212
  const paddingX = !underline ? geometry2.pillsPaddingX : density === "compact" ? geometry2.compactPaddingX : geometry2.paddingX;
6177
6213
  const height = density === "compact" ? geometry2.compactHeight : geometry2.height;
@@ -6180,10 +6216,10 @@ function KjunTabs({
6180
6216
  const roomX = underline ? ringRoom : 0, roomY = Math.max(underline ? ringRoom : 0, slop);
6181
6217
  const selectedTab = tabs.find((tab) => tab.name === value);
6182
6218
  const selection = useButtonGroupMotion(selectedTab ? { ...selectedTab, value: selectedTab.name } : void 0);
6183
- const tabRefs = (0, import_react49.useRef)(/* @__PURE__ */ new Map());
6219
+ const tabRefs = (0, import_react50.useRef)(/* @__PURE__ */ new Map());
6184
6220
  const { updateLayout } = selection;
6185
6221
  const firstName = (_a = tabs[0]) == null ? void 0 : _a.name;
6186
- const measureIndicator = (0, import_react49.useCallback)((name, box) => {
6222
+ const measureIndicator = (0, import_react50.useCallback)((name, box) => {
6187
6223
  const left = underline && name === firstName ? 0 : paddingX;
6188
6224
  updateLayout(name, underline ? {
6189
6225
  x: box.x + left,
@@ -6192,7 +6228,7 @@ function KjunTabs({
6192
6228
  height: geometry2.indicatorHeight
6193
6229
  } : box);
6194
6230
  }, [underline, paddingX, firstName, updateLayout, geometry2]);
6195
- (0, import_react49.useLayoutEffect)(() => {
6231
+ (0, import_react50.useLayoutEffect)(() => {
6196
6232
  for (const [name, node] of tabRefs.current) {
6197
6233
  node.measure((x, y, width, height2) => measureIndicator(name, { x, y, width, height: height2 }));
6198
6234
  }
@@ -6351,8 +6387,8 @@ function KjunTabs({
6351
6387
  ] }) });
6352
6388
  }
6353
6389
  function KjunTabPane({ name, children }) {
6354
- const { value, id, names } = (0, import_react49.useContext)(TabContext);
6355
- const root = (0, import_react49.useRef)(null);
6390
+ const { value, id, names } = (0, import_react50.useContext)(TabContext);
6391
+ const root = (0, import_react50.useRef)(null);
6356
6392
  usePanelFocus(root);
6357
6393
  const hidden = value !== name;
6358
6394
  return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
@@ -6375,7 +6411,7 @@ function KjunTabPane({ name, children }) {
6375
6411
 
6376
6412
  // packages/native/src/navigation.tsx
6377
6413
  var import_jsx_runtime37 = require("react/jsx-runtime");
6378
- var AccordionContext = (0, import_react50.createContext)(null);
6414
+ var AccordionContext = (0, import_react51.createContext)(null);
6379
6415
  function KjunAccordion({
6380
6416
  multiple = false,
6381
6417
  tone = "card",
@@ -6405,14 +6441,32 @@ function KjunAccordionItem({
6405
6441
  defaultOpen = false,
6406
6442
  disabled = false,
6407
6443
  header,
6444
+ open: openProp,
6445
+ onOpenChange,
6446
+ keepMounted = false,
6408
6447
  children
6409
6448
  }) {
6410
- const { colors } = useKjunStyles(), group = (0, import_react50.useContext)(AccordionContext), id = "kjun-" + (0, import_react50.useId)(), [own, setOwn] = (0, import_react50.useState)(defaultOpen), open = group ? group.opened.has(id) : own;
6449
+ const controlled = openProp !== void 0, context = (0, import_react51.useContext)(AccordionContext), { colors } = useKjunStyles(), group = controlled ? null : context, id = "kjun-" + (0, import_react51.useId)(), [own, setOwn] = (0, import_react51.useState)(defaultOpen), open = controlled ? openProp : group ? group.opened.has(id) : own;
6450
+ const reported = (0, import_react51.useRef)(open), notify = (0, import_react51.useRef)(onOpenChange), registering = (0, import_react51.useRef)(!!group && defaultOpen);
6451
+ notify.current = onOpenChange;
6452
+ (0, import_react51.useEffect)(() => {
6453
+ var _a;
6454
+ if (registering.current) {
6455
+ if (open) {
6456
+ registering.current = false;
6457
+ reported.current = open;
6458
+ }
6459
+ return;
6460
+ }
6461
+ if (controlled || reported.current === open) return;
6462
+ reported.current = open;
6463
+ (_a = notify.current) == null ? void 0 : _a.call(notify, open);
6464
+ }, [open, controlled]);
6411
6465
  const rotation = useMotionValue(open ? 1 : 0, import_tokens37.tokens.motion.collapse, void 0, void 0, true);
6412
6466
  const geometry2 = import_tokens37.tokens.extensions.accordion;
6413
- const [hovered, setHovered] = (0, import_react50.useState)(false);
6467
+ const [hovered, setHovered] = (0, import_react51.useState)(false);
6414
6468
  const wrapping = import_react_native40.Platform.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : {};
6415
- (0, import_react50.useEffect)(() => {
6469
+ (0, import_react51.useEffect)(() => {
6416
6470
  return group == null ? void 0 : group.register(id, defaultOpen);
6417
6471
  }, []);
6418
6472
  return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_react_native40.View, { children: [
@@ -6426,7 +6480,10 @@ function KjunAccordionItem({
6426
6480
  accessibilityLabel: title,
6427
6481
  accessibilityState: { expanded: open, disabled },
6428
6482
  disabled,
6429
- onPress: () => group ? group.toggle(id) : setOwn(!own),
6483
+ onPress: () => {
6484
+ registering.current = false;
6485
+ controlled ? onOpenChange == null ? void 0 : onOpenChange(!open) : group ? group.toggle(id) : setOwn(!own);
6486
+ },
6430
6487
  onHoverIn: () => setHovered(true),
6431
6488
  onHoverOut: () => setHovered(false),
6432
6489
  style: ({ pressed }) => ({
@@ -6446,7 +6503,7 @@ function KjunAccordionItem({
6446
6503
  ]
6447
6504
  }
6448
6505
  ),
6449
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Collapse, { open, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
6506
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Collapse, { open, keepMounted, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
6450
6507
  import_react_native40.View,
6451
6508
  {
6452
6509
  nativeID: id + "-panel",
@@ -6489,7 +6546,7 @@ function KjunBreadcrumb({
6489
6546
  }
6490
6547
  )
6491
6548
  ] });
6492
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_react50.Fragment, { children: [
6549
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_react51.Fragment, { children: [
6493
6550
  i > 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
6494
6551
  KjunIcon,
6495
6552
  {
@@ -6643,7 +6700,7 @@ function KjunPagination({
6643
6700
 
6644
6701
  // packages/native/src/actions.tsx
6645
6702
  var import_tokens38 = require("@kjun-ui/tokens");
6646
- var import_react51 = require("react");
6703
+ var import_react52 = require("react");
6647
6704
  var import_react_native41 = require("react-native");
6648
6705
  var import_jsx_runtime38 = require("react/jsx-runtime");
6649
6706
  function KjunIconToggle({
@@ -6662,7 +6719,7 @@ function KjunIconToggle({
6662
6719
  onToggle
6663
6720
  }) {
6664
6721
  const { colors } = useKjunStyles();
6665
- const navigation = (0, import_react51.useContext)(TopNavigationContext);
6722
+ const navigation = (0, import_react52.useContext)(TopNavigationContext);
6666
6723
  const box = navigation ? navigation.controlSize : size === "xs" ? import_tokens38.tokens.extensions.iconToggle.compactSize : import_tokens38.tokens.extensions.iconToggle.sizes[size];
6667
6724
  return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(KjunTooltip, { content: disabled || loading ? "" : tooltip != null ? tooltip : ariaLabel, placement: tooltipPlacement, delay: tooltipDelay, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
6668
6725
  AccessiblePressable,
@@ -6728,8 +6785,8 @@ function KjunCopyButton({
6728
6785
  }) {
6729
6786
  const m = useKjunMessages();
6730
6787
  successText != null ? successText : successText = m.copied;
6731
- const feedback = useOptionalKjunFeedback(), { colors } = useKjunStyles(), [copied, setCopied] = (0, import_react51.useState)(false), [failed, setFailed] = (0, import_react51.useState)(false), busy = (0, import_react51.useRef)(false), timer = (0, import_react51.useRef)(void 0), mounted = (0, import_react51.useRef)(true);
6732
- (0, import_react51.useEffect)(() => {
6788
+ const feedback = useOptionalKjunFeedback(), { colors } = useKjunStyles(), [copied, setCopied] = (0, import_react52.useState)(false), [failed, setFailed] = (0, import_react52.useState)(false), busy = (0, import_react52.useRef)(false), timer = (0, import_react52.useRef)(void 0), mounted = (0, import_react52.useRef)(true);
6789
+ (0, import_react52.useEffect)(() => {
6733
6790
  mounted.current = true;
6734
6791
  return () => {
6735
6792
  mounted.current = false;
@@ -6884,14 +6941,14 @@ function KjunScrollFade({ children }) {
6884
6941
 
6885
6942
  // packages/native/src/data-state.tsx
6886
6943
  var import_tokens39 = require("@kjun-ui/tokens");
6887
- var import_react53 = require("react");
6944
+ var import_react54 = require("react");
6888
6945
  var import_react_native42 = require("react-native");
6889
6946
 
6890
6947
  // shared/package-runtime/use-loading-reveal.ts
6891
- var import_react52 = require("react");
6948
+ var import_react53 = require("react");
6892
6949
  function useLoadingReveal(active, delay = 0) {
6893
- const [revealed, setRevealed] = (0, import_react52.useState)(false);
6894
- (0, import_react52.useEffect)(() => {
6950
+ const [revealed, setRevealed] = (0, import_react53.useState)(false);
6951
+ (0, import_react53.useEffect)(() => {
6895
6952
  setRevealed(false);
6896
6953
  if (!active || delay <= 0) return;
6897
6954
  const timer = setTimeout(() => setRevealed(true), delay);
@@ -6904,7 +6961,7 @@ function useLoadingReveal(active, delay = 0) {
6904
6961
  var import_jsx_runtime39 = require("react/jsx-runtime");
6905
6962
  function useQueryDisplay(props) {
6906
6963
  var _a;
6907
- const [state, setState] = (0, import_react53.useState)(() => (0, import_tokens39.initialQueryDisplay)(props));
6964
+ const [state, setState] = (0, import_react54.useState)(() => (0, import_tokens39.initialQueryDisplay)(props));
6908
6965
  let next = state;
6909
6966
  if (state.queryKey !== ((_a = props.queryKey) != null ? _a : null) || state.resultKey !== props.resultKey || state.loading !== !!props.loading) {
6910
6967
  next = (0, import_tokens39.advanceQueryDisplay)(state, props);
@@ -7013,7 +7070,7 @@ function BoundaryDescription() {
7013
7070
  const { colors } = useKjunStyles();
7014
7071
  return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(KText, { style: { ...typeStyle({ ...import_tokens39.tokens.typography.caption, fontSizePx: import_tokens39.tokens.extensions.state.descriptionSize }), color: colors.textSecondary, textAlign: "center" }, children: m.retryHint });
7015
7072
  }
7016
- var KjunErrorBoundary = class extends import_react53.Component {
7073
+ var KjunErrorBoundary = class extends import_react54.Component {
7017
7074
  constructor() {
7018
7075
  super(...arguments);
7019
7076
  this.state = { error: false };
@@ -7061,7 +7118,7 @@ KjunErrorBoundary.contextType = KjunMessageContext;
7061
7118
 
7062
7119
  // packages/native/src/skeletons.tsx
7063
7120
  var import_tokens43 = require("@kjun-ui/tokens");
7064
- var import_react55 = require("react");
7121
+ var import_react56 = require("react");
7065
7122
  var import_react_native44 = require("react-native");
7066
7123
  var import_react_native_svg5 = __toESM(require("react-native-svg"), 1);
7067
7124
 
@@ -7108,9 +7165,9 @@ function nativeMarketLayout(columns, showActions, containerWidth) {
7108
7165
 
7109
7166
  // packages/native/src/market-layout.ts
7110
7167
  var import_tokens41 = require("@kjun-ui/tokens");
7111
- var import_react54 = require("react");
7168
+ var import_react55 = require("react");
7112
7169
  function useMarketLayout(columns, showActions) {
7113
- const [containerWidth, setContainerWidth] = (0, import_react54.useState)(0);
7170
+ const [containerWidth, setContainerWidth] = (0, import_react55.useState)(0);
7114
7171
  const onLayout = (event) => setContainerWidth(event.nativeEvent.layout.width);
7115
7172
  return { ...nativeMarketLayout(columns, showActions, containerWidth), onLayout };
7116
7173
  }
@@ -7252,8 +7309,8 @@ function KjunChartSkeleton({
7252
7309
  }) {
7253
7310
  const m = useKjunMessages();
7254
7311
  loadingText != null ? loadingText : loadingText = m.chartLoading;
7255
- const kind = chartSkeletonKind(requestedKind), { colors } = useKjunStyles(), reduced = useReducedMotion(), opacity = (0, import_react55.useRef)(new import_react_native44.Animated.Value(1)).current, revealed = useLoadingReveal(true, loadingDelay), [measured, setMeasured] = (0, import_react55.useState)(0), thickness = import_tokens43.tokens.extensions.chartSkeleton.donutThickness, boxHeight = typeof height === "number" ? height : measured, hole = boxHeight > 0 ? chartDonutHole(boxHeight, thickness) : void 0, rowCount = chartSkeletonCount(rows), columnCount = chartSkeletonCount(columns), status = revealed && loadingText ? "status" : void 0, hidden = { "aria-hidden": true, importantForAccessibility: "no-hide-descendants" };
7256
- (0, import_react55.useEffect)(() => {
7312
+ const kind = chartSkeletonKind(requestedKind), { colors } = useKjunStyles(), reduced = useReducedMotion(), opacity = (0, import_react56.useRef)(new import_react_native44.Animated.Value(1)).current, revealed = useLoadingReveal(true, loadingDelay), [measured, setMeasured] = (0, import_react56.useState)(0), thickness = import_tokens43.tokens.extensions.chartSkeleton.donutThickness, boxHeight = typeof height === "number" ? height : measured, hole = boxHeight > 0 ? chartDonutHole(boxHeight, thickness) : void 0, rowCount = chartSkeletonCount(rows), columnCount = chartSkeletonCount(columns), status = revealed && loadingText ? "status" : void 0, hidden = { "aria-hidden": true, importantForAccessibility: "no-hide-descendants" };
7313
+ (0, import_react56.useEffect)(() => {
7257
7314
  if (reduced || !skeleton) return;
7258
7315
  const loop = import_react_native44.Animated.loop(
7259
7316
  import_react_native44.Animated.sequence([
@@ -7334,7 +7391,7 @@ function KjunChartSkeleton({
7334
7391
 
7335
7392
  // packages/native/src/financial-display.tsx
7336
7393
  var import_tokens46 = require("@kjun-ui/tokens");
7337
- var import_react58 = require("react");
7394
+ var import_react59 = require("react");
7338
7395
  var import_react_native46 = require("react-native");
7339
7396
  var import_react_native_svg6 = __toESM(require("react-native-svg"), 1);
7340
7397
 
@@ -7342,7 +7399,7 @@ var import_react_native_svg6 = __toESM(require("react-native-svg"), 1);
7342
7399
  var import_tokens45 = require("@kjun-ui/tokens");
7343
7400
 
7344
7401
  // shared/package-runtime/use-number-motion.ts
7345
- var import_react56 = require("react");
7402
+ var import_react57 = require("react");
7346
7403
 
7347
7404
  // shared/package-runtime/number-motion.ts
7348
7405
  var import_tokens44 = require("@kjun-ui/tokens");
@@ -7388,14 +7445,14 @@ function createNumberMotion(apply) {
7388
7445
 
7389
7446
  // shared/package-runtime/use-number-motion.ts
7390
7447
  function useNumberMotion(value, animated, fromPrevious, reduced) {
7391
- const [display, setDisplay] = (0, import_react56.useState)(typeof value === "string" || fromPrevious || !animated || reduced ? value : 0);
7392
- const motion = (0, import_react56.useRef)(null);
7448
+ const [display, setDisplay] = (0, import_react57.useState)(typeof value === "string" || fromPrevious || !animated || reduced ? value : 0);
7449
+ const motion = (0, import_react57.useRef)(null);
7393
7450
  if (!motion.current) motion.current = createNumberMotion(setDisplay);
7394
- (0, import_react56.useLayoutEffect)(() => {
7451
+ (0, import_react57.useLayoutEffect)(() => {
7395
7452
  if (!motion.current) motion.current = createNumberMotion(setDisplay);
7396
7453
  motion.current.update(value, animated, fromPrevious, reduced);
7397
7454
  }, [value, animated, fromPrevious, reduced]);
7398
- (0, import_react56.useLayoutEffect)(() => () => {
7455
+ (0, import_react57.useLayoutEffect)(() => () => {
7399
7456
  var _a;
7400
7457
  (_a = motion.current) == null ? void 0 : _a.cancel();
7401
7458
  motion.current = null;
@@ -7404,7 +7461,7 @@ function useNumberMotion(value, animated, fromPrevious, reduced) {
7404
7461
  }
7405
7462
 
7406
7463
  // packages/native/src/number-display.tsx
7407
- var import_react57 = require("react");
7464
+ var import_react58 = require("react");
7408
7465
  var import_react_native45 = require("react-native");
7409
7466
  var import_jsx_runtime42 = require("react/jsx-runtime");
7410
7467
  var finiteNumber = (value) => value == null || typeof value === "boolean" || typeof value === "string" && !value.trim() || !["number", "string"].includes(typeof value) || !Number.isFinite(Number(value)) ? null : Number(value) || 0;
@@ -7415,7 +7472,7 @@ var format = (value, decimals = 0) => new Intl.NumberFormat(void 0, {
7415
7472
  function useCountUp(value, animated, fromPrevious) {
7416
7473
  return useNumberMotion(value, animated, fromPrevious, useReducedMotion());
7417
7474
  }
7418
- var MarketQuoteTypeContext = (0, import_react57.createContext)(null);
7475
+ var MarketQuoteTypeContext = (0, import_react58.createContext)(null);
7419
7476
  function KjunAnimatedNumber({
7420
7477
  value,
7421
7478
  decimals = 0,
@@ -7476,7 +7533,7 @@ function KjunPriceCell({
7476
7533
  fetchedAt,
7477
7534
  fromPrevious = true
7478
7535
  }) {
7479
- const { colors } = useKjunStyles(), quoteType = (0, import_react57.useContext)(MarketQuoteTypeContext);
7536
+ const { colors } = useKjunStyles(), quoteType = (0, import_react58.useContext)(MarketQuoteTypeContext);
7480
7537
  return value == null ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(KjunSkeleton, { type: "block", height: import_tokens45.tokens.extensions.financial.priceSkeletonHeight, width: import_tokens45.tokens.extensions.financial.priceSkeletonWidth }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_react_native45.View, { style: { flexDirection: "row", alignItems: "center", gap: import_tokens45.tokens.extensions.financial.affixGap }, children: [
7481
7538
  /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
7482
7539
  import_react_native45.View,
@@ -7510,7 +7567,7 @@ function KjunSignedValue({
7510
7567
  showFreshness = true,
7511
7568
  formatter
7512
7569
  }) {
7513
- const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), quoteType = (0, import_react57.useContext)(MarketQuoteTypeContext), n = finiteNumber(value), role = n === null || n === 0 ? "priceNeutral" : n > 0 ? "priceUp" : "priceDown", color = loading || n === null ? colors.textSecondary : stale ? colors.textTertiary : domainColor(domainColors, role);
7570
+ const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), quoteType = (0, import_react58.useContext)(MarketQuoteTypeContext), n = finiteNumber(value), role = n === null || n === 0 ? "priceNeutral" : n > 0 ? "priceUp" : "priceDown", color = loading || n === null ? colors.textSecondary : stale ? colors.textTertiary : domainColor(domainColors, role);
7514
7571
  const display = n === null ? "\u2014" : formatter ? formatter(n) : prefix + (showSign && n > 0 ? "+" : "") + format(kind === "percent" && !isRaw ? n * 100 : n, decimals) + (kind === "percent" ? "%" : "") + suffix;
7515
7572
  return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
7516
7573
  import_react_native45.View,
@@ -7806,7 +7863,7 @@ function KjunSparkline({
7806
7863
  stretch = false,
7807
7864
  ariaLabel
7808
7865
  }) {
7809
- const { colors, domainColors } = useKjunStyles(), id = "kjun-" + (0, import_react58.useId)().replace(/:/g, ""), values = data.filter(Number.isFinite), min = Math.min(...values), max = Math.max(...values), range = max - min || 1, points = values.map(
7866
+ const { colors, domainColors } = useKjunStyles(), id = "kjun-" + (0, import_react59.useId)().replace(/:/g, ""), values = data.filter(Number.isFinite), min = Math.min(...values), max = Math.max(...values), range = max - min || 1, points = values.map(
7810
7867
  (v, i) => `${2 + i / Math.max(1, values.length - 1) * (width - 4)},${2 + height - 4 - (v - min) / range * (height - 4)}`
7811
7868
  ).join(" "), change = values.length > 1 ? values[values.length - 1] - values[0] : 0, line = color || (change === 0 ? colors.textTertiary : semantic === "status" ? change > 0 ? colors.success : colors.danger : domainColor(domainColors, change > 0 ? "priceUp" : "priceDown"));
7812
7869
  return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
@@ -7852,7 +7909,7 @@ function KjunSparkline({
7852
7909
 
7853
7910
  // shared/package-runtime/table.ts
7854
7911
  var import_tokens47 = require("@kjun-ui/tokens");
7855
- var import_react59 = require("react");
7912
+ var import_react60 = require("react");
7856
7913
 
7857
7914
  // shared/package-runtime/table-model.ts
7858
7915
  function getValue(row, key) {
@@ -7896,11 +7953,11 @@ function useTableModel(props) {
7896
7953
  expandSingle = false,
7897
7954
  onExpandedRowsChange
7898
7955
  } = props;
7899
- const [internalSort, setSort] = (0, import_react59.useState)({
7956
+ const [internalSort, setSort] = (0, import_react60.useState)({
7900
7957
  key: "",
7901
7958
  order: "asc"
7902
- }), [internalExpanded, setExpanded] = (0, import_react59.useState)([]), [search, setSearch] = (0, import_react59.useState)(""), timer = (0, import_react59.useRef)(void 0);
7903
- (0, import_react59.useEffect)(() => () => clearTimeout(timer.current), []);
7959
+ }), [internalExpanded, setExpanded] = (0, import_react60.useState)([]), [search, setSearch] = (0, import_react60.useState)(""), timer = (0, import_react60.useRef)(void 0);
7960
+ (0, import_react60.useEffect)(() => () => clearTimeout(timer.current), []);
7904
7961
  const sort = props.sort === void 0 ? internalSort : (_a = props.sort) != null ? _a : { key: "", order: "asc" };
7905
7962
  const rows = sortTableRows(data, sort, { sortable, sortMode });
7906
7963
  const key = (row, index = 0) => tableRowKey(row, rowKey, index);
@@ -8061,7 +8118,7 @@ function TableToolbar({
8061
8118
 
8062
8119
  // packages/native/src/table-grid.tsx
8063
8120
  var import_tokens49 = require("@kjun-ui/tokens");
8064
- var import_react60 = require("react");
8121
+ var import_react61 = require("react");
8065
8122
  var import_react_native_svg7 = __toESM(require("react-native-svg"), 1);
8066
8123
  var import_react_native48 = require("react-native");
8067
8124
  var import_jsx_runtime45 = require("react/jsx-runtime");
@@ -8069,7 +8126,7 @@ function TableGrid({ initialLoading, skeletonRows, skeletonHidden, state, onRowC
8069
8126
  const m = useKjunMessages();
8070
8127
  const { colors } = useKjunStyles();
8071
8128
  const geometry2 = import_tokens49.tokens.table;
8072
- const [scroll, setScroll] = (0, import_react60.useState)({ viewport: 0, x: 0 }), fadeId = "kjun-table-fade-" + (0, import_react60.useId)().replace(/[^a-z0-9]/gi, "");
8129
+ const [scroll, setScroll] = (0, import_react61.useState)({ viewport: 0, x: 0 }), fadeId = "kjun-table-fade-" + (0, import_react61.useId)().replace(/[^a-z0-9]/gi, "");
8073
8130
  const cellPadding = geometry2.cellPadding[compact ? "compact" : "regular"];
8074
8131
  const width = (col) => typeof col.width === "number" ? col.width : typeof col.width === "string" && /^\d+(px)?$/.test(col.width) ? parseFloat(col.width) : geometry2.columnWidth;
8075
8132
  const totalWidth = visible.reduce((sum, col) => sum + width(col), 0) + (selectable ? geometry2.actionColumnWidth : 0) + (expandable ? geometry2.actionColumnWidth : 0);
@@ -8183,7 +8240,7 @@ function TableGrid({ initialLoading, skeletonRows, skeletonHidden, state, onRowC
8183
8240
  ))
8184
8241
  ] }, i)) : !state.rows.length ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_react_native48.View, { style: { paddingVertical: geometry2.emptyPadding.y, paddingHorizontal: geometry2.emptyPadding.x, alignItems: "center" }, children: content(emptyContent || emptyText, {
8185
8242
  color: colors.textSecondary
8186
- }) }) : state.rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_react60.Fragment, { children: [
8243
+ }) }) : state.rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_react61.Fragment, { children: [
8187
8244
  /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
8188
8245
  AccessiblePressable,
8189
8246
  {
@@ -8812,7 +8869,7 @@ function KjunKpiHero(props) {
8812
8869
 
8813
8870
  // packages/native/src/kpi-row.tsx
8814
8871
  var import_tokens53 = require("@kjun-ui/tokens");
8815
- var import_react61 = require("react");
8872
+ var import_react62 = require("react");
8816
8873
  var import_react_native53 = require("react-native");
8817
8874
  var import_jsx_runtime50 = require("react/jsx-runtime");
8818
8875
  function KjunKpiRow({
@@ -8827,7 +8884,7 @@ function KjunKpiRow({
8827
8884
  const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), mobile = (0, import_react_native53.useWindowDimensions)().width <= import_tokens53.tokens.responsive.kpiRow, small = size === "sm", summary = mobileSummary && mobile && !small;
8828
8885
  const geometry2 = import_tokens53.tokens.extensions.kpiRow;
8829
8886
  const spec = small ? geometry2.sm : mobile ? geometry2.mobile : geometry2.md;
8830
- const [width, setWidth] = (0, import_react61.useState)(0);
8887
+ const [width, setWidth] = (0, import_react62.useState)(0);
8831
8888
  const paddingX = summary ? geometry2.summary.paddingX : spec.paddingX;
8832
8889
  const gapX = summary ? geometry2.summary.gapX : 0;
8833
8890
  const explicit = items2.some(
@@ -9046,7 +9103,7 @@ var import_tokens55 = require("@kjun-ui/tokens");
9046
9103
  var import_react_native55 = require("react-native");
9047
9104
 
9048
9105
  // shared/package-runtime/market-cards.ts
9049
- var import_react62 = require("react");
9106
+ var import_react63 = require("react");
9050
9107
  function useMarketCardsModel(props, persistence) {
9051
9108
  const {
9052
9109
  columns,
@@ -9062,8 +9119,8 @@ function useMarketCardsModel(props, persistence) {
9062
9119
  sortKey: c.sortable ? metricConfig[c.key].sortKey : null,
9063
9120
  isDefaultSort: c.defaultSort
9064
9121
  }));
9065
- const [key, setKey] = (0, import_react62.useState)(""), restoreSignature = JSON.stringify([storageNamespace, pills]);
9066
- const mounted = (0, import_react62.useRef)("");
9122
+ const [key, setKey] = (0, import_react63.useState)(""), restoreSignature = JSON.stringify([storageNamespace, pills]);
9123
+ const mounted = (0, import_react63.useRef)("");
9067
9124
  const save = (key2) => {
9068
9125
  var _a;
9069
9126
  try {
@@ -9071,7 +9128,7 @@ function useMarketCardsModel(props, persistence) {
9071
9128
  } catch (e) {
9072
9129
  }
9073
9130
  };
9074
- (0, import_react62.useEffect)(() => {
9131
+ (0, import_react63.useEffect)(() => {
9075
9132
  var _a, _b;
9076
9133
  if (mounted.current === restoreSignature) return;
9077
9134
  mounted.current = restoreSignature;
@@ -9550,7 +9607,7 @@ function KjunMarketListPanel({
9550
9607
 
9551
9608
  // packages/native/src/market-table.tsx
9552
9609
  var import_tokens58 = require("@kjun-ui/tokens");
9553
- var import_react63 = require("react");
9610
+ var import_react64 = require("react");
9554
9611
  var import_react_native58 = require("react-native");
9555
9612
  var import_react_native_svg8 = __toESM(require("react-native-svg"), 1);
9556
9613
  var import_jsx_runtime55 = require("react/jsx-runtime");
@@ -9637,7 +9694,7 @@ function KjunMarketTable(props) {
9637
9694
  );
9638
9695
  };
9639
9696
  const layout = useMarketLayout(columns, showActions);
9640
- const [scroll, setScroll] = (0, import_react63.useState)({ viewport: 0, x: 0 }), fadeId = "kjun-market-fade-" + (0, import_react63.useId)().replace(/[^a-z0-9]/gi, "");
9697
+ const [scroll, setScroll] = (0, import_react64.useState)({ viewport: 0, x: 0 }), fadeId = "kjun-market-fade-" + (0, import_react64.useId)().replace(/[^a-z0-9]/gi, "");
9641
9698
  const tableQuote = { price: {}, change: { fontWeight: typeStyle("body").fontWeight } };
9642
9699
  return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(
9643
9700
  import_react_native58.View,
@@ -9852,30 +9909,35 @@ function KjunMarketTable(props) {
9852
9909
 
9853
9910
  // packages/native/src/list.tsx
9854
9911
  var import_tokens59 = require("@kjun-ui/tokens");
9855
- var import_react64 = require("react");
9912
+ var import_react65 = require("react");
9856
9913
  var import_react_native59 = require("react-native");
9857
9914
  var import_jsx_runtime56 = require("react/jsx-runtime");
9858
- var ListRowContext = (0, import_react64.createContext)({ divider: true, compact: false });
9859
- function KjunListRow({ title, description, leading, trailing, actions, href, disabled = false, destructive = false, chevron = false, onPress }) {
9860
- const { colors } = useKjunStyles(), { divider, compact } = (0, import_react64.useContext)(ListRowContext);
9915
+ var ListRowContext = (0, import_react65.createContext)({ divider: true, compact: false });
9916
+ function KjunListRow({ title, description, leading, trailing, actions, href, disabled = false, destructive = false, chevron = false, onPress, ariaLabel, children }) {
9917
+ const { colors } = useKjunStyles(), { divider, compact } = (0, import_react65.useContext)(ListRowContext);
9861
9918
  const parts = /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_jsx_runtime56.Fragment, { children: [
9862
9919
  hasContent(leading) && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native59.View, { children: content(leading) }),
9863
9920
  /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_react_native59.View, { style: { flex: 1, minWidth: 0 }, children: [
9864
9921
  /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(KText, { style: { ...typeStyle("label"), ...destructive ? { color: colors.danger } : {} }, children: title }),
9865
- !!description && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(KText, { style: { marginTop: import_tokens59.tokens.extensions.list.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9922
+ !!description && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(KText, { style: { marginTop: import_tokens59.tokens.extensions.list.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description }),
9923
+ hasContent(children) && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native59.View, { style: { marginTop: import_tokens59.tokens.extensions.list.descriptionGap }, children: content(children) })
9866
9924
  ] }),
9867
9925
  hasContent(trailing) && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native59.View, { style: { maxWidth: "35%" }, children: content(trailing) }),
9868
9926
  chevron && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native59.View, { accessible: false, importantForAccessibility: "no-hide-descendants", accessibilityElementsHidden: true, children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(KjunIcon, { name: "chevron-right", size: import_tokens59.tokens.iconSizes.default, color: colors.textTertiary }) })
9869
9927
  ] });
9870
9928
  const style = { flex: 1, minWidth: 0, minHeight: compact ? import_tokens59.tokens.extensions.list.compactMinimumHeight : import_tokens59.tokens.extensions.list.minimumHeight, flexDirection: "row", alignItems: "center", gap: import_tokens59.tokens.extensions.list.gap, padding: import_tokens59.tokens.extensions.list.padding, ...compact ? { paddingVertical: import_tokens59.tokens.extensions.list.compactPaddingY } : {} };
9871
9929
  return /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_react_native59.View, { role: "listitem", style: { flexDirection: "row", alignItems: "center", gap: import_tokens59.tokens.extensions.list.gap, borderBottomWidth: divider ? import_tokens59.tokens.border.defaultWidth : 0, borderBottomColor: colors.divider }, children: [
9872
- onPress || href ? /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(AccessiblePressable, { ...import_react_native59.Platform.OS === "web" && href && !disabled ? { href } : {}, accessibilityRole: href ? "link" : "button", accessibilityLabel: title + (description ? ", " + description : ""), disabled, accessibilityState: { disabled }, onPress, style: ({ pressed }) => ({ ...style, opacity: disabled ? import_tokens59.tokens.states.opacity.disabled : 1, backgroundColor: pressed ? colors.hover : "transparent" }), children: parts }) : /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native59.View, { style, children: parts }),
9930
+ onPress || href ? /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(AccessiblePressable, { ...import_react_native59.Platform.OS === "web" && href && !disabled ? { href } : {}, accessibilityRole: href ? "link" : "button", accessibilityLabel: ariaLabel != null ? ariaLabel : title + (description ? ", " + description : ""), disabled, accessibilityState: { disabled }, onPress, style: ({ pressed }) => ({ ...style, opacity: disabled ? import_tokens59.tokens.states.opacity.disabled : 1, backgroundColor: pressed ? colors.hover : "transparent" }), children: parts }) : /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native59.View, { style, children: parts }),
9873
9931
  hasContent(actions) && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native59.View, { pointerEvents: disabled ? "none" : "auto", ...disabled && import_react_native59.Platform.OS === "web" ? { inert: true } : {}, style: { maxWidth: "45%", flexDirection: "row", flexWrap: "wrap", gap: import_tokens59.tokens.extensions.list.actionsGap, paddingRight: import_tokens59.tokens.extensions.list.actionsPadding, opacity: disabled ? import_tokens59.tokens.states.opacity.disabled : 1 }, children: content(actions) })
9874
9932
  ] });
9875
9933
  }
9876
- function KjunListSection({ title, description, actions, children, ariaLabel, surface = true, divider = true, density = "default" }) {
9934
+ function KjunListSection({ title, description, actions, children, ariaLabel, surface = true, divider = true, density = "default", radius = "none" }) {
9877
9935
  const { colors } = useKjunStyles();
9878
- return /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_react_native59.View, { style: { minWidth: 0, backgroundColor: surface ? colors.surface : "transparent" }, children: [
9936
+ return /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_react_native59.View, { style: {
9937
+ minWidth: 0,
9938
+ backgroundColor: surface ? colors.surface : "transparent",
9939
+ ...radius === "none" ? {} : { borderRadius: import_tokens59.tokens.card.radii[radius], overflow: "hidden" }
9940
+ }, children: [
9879
9941
  !!(title || description || actions) && /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_react_native59.View, { style: { flexDirection: "row", alignItems: "center", gap: import_tokens59.tokens.extensions.list.gap, padding: import_tokens59.tokens.extensions.list.padding }, children: [
9880
9942
  /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_react_native59.View, { style: { flex: 1 }, children: [
9881
9943
  !!title && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(KText, { accessibilityRole: "header", style: { ...typeStyle("cardTitle") }, children: title }),
@@ -9883,11 +9945,11 @@ function KjunListSection({ title, description, actions, children, ariaLabel, sur
9883
9945
  ] }),
9884
9946
  content(actions)
9885
9947
  ] }),
9886
- /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native59.View, { role: "list", accessibilityLabel: ariaLabel || title, children: import_react64.Children.toArray(children).map((row, i, rows) => /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(ListRowContext.Provider, { value: { divider: divider && i < rows.length - 1, compact: density === "compact" }, children: row }, i)) })
9948
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native59.View, { role: "list", accessibilityLabel: ariaLabel || title, children: import_react65.Children.toArray(children).map((row, i, rows) => /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(ListRowContext.Provider, { value: { divider: divider && i < rows.length - 1, compact: density === "compact" }, children: row }, i)) })
9887
9949
  ] });
9888
9950
  }
9889
9951
  function KjunListItem({ children }) {
9890
- const { colors } = useKjunStyles(), { divider, compact } = (0, import_react64.useContext)(ListRowContext);
9952
+ const { colors } = useKjunStyles(), { divider, compact } = (0, import_react65.useContext)(ListRowContext);
9891
9953
  return /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native59.View, { role: "listitem", style: {
9892
9954
  padding: import_tokens59.tokens.extensions.list.padding,
9893
9955
  ...compact ? { paddingVertical: import_tokens59.tokens.extensions.list.compactPaddingY } : {},
@@ -9899,7 +9961,7 @@ function KjunListItem({ children }) {
9899
9961
 
9900
9962
  // packages/native/src/app-navigation.tsx
9901
9963
  var import_tokens60 = require("@kjun-ui/tokens");
9902
- var import_react65 = require("react");
9964
+ var import_react66 = require("react");
9903
9965
  var import_react_native60 = require("react-native");
9904
9966
  var import_jsx_runtime57 = require("react/jsx-runtime");
9905
9967
  function KjunTopNavigation({ title, description, leading, actions, safeAreaTop = 0 }) {
@@ -9908,13 +9970,13 @@ function KjunTopNavigation({ title, description, leading, actions, safeAreaTop =
9908
9970
  const titleInset = Math.max(0, (geometry2.controlSize - import_tokens60.tokens.typography.sectionTitle.lineHeightPx) / 2);
9909
9971
  const wrapping = import_react_native60.Platform.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : {};
9910
9972
  const edgeInset = (geometry2.controlSize - geometry2.iconSize) / 2;
9911
- const [iconEdges, setIconEdges] = (0, import_react65.useState)({ leading: false, trailing: false });
9973
+ const [iconEdges, setIconEdges] = (0, import_react66.useState)({ leading: false, trailing: false });
9912
9974
  const square = (key) => (event) => {
9913
9975
  const { width, height } = event.nativeEvent.layout;
9914
9976
  const next = Math.abs(width - height) < 1 && Math.abs(height - geometry2.controlSize) < 1;
9915
9977
  setIconEdges((current) => current[key] === next ? current : { ...current, [key]: next });
9916
9978
  };
9917
- const flatten2 = (node) => import_react65.Children.toArray(node).flatMap((child) => (0, import_react65.isValidElement)(child) && child.type === import_react65.Fragment ? flatten2(child.props.children) : [child]);
9979
+ const flatten2 = (node) => import_react66.Children.toArray(node).flatMap((child) => (0, import_react66.isValidElement)(child) && child.type === import_react66.Fragment ? flatten2(child.props.children) : [child]);
9918
9980
  const actionList = flatten2(actions);
9919
9981
  return /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(TopNavigationContext.Provider, { value: geometry2, children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(import_react_native60.View, { style: { flexShrink: 0, paddingTop: Math.max(0, safeAreaTop), backgroundColor: colors.surface }, children: /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_react_native60.View, { style: { minHeight: geometry2.minimumHeight, flexDirection: "row", flexWrap: "wrap", alignItems: "flex-start", gap: geometry2.gap, paddingVertical: geometry2.paddingY, paddingHorizontal: geometry2.paddingX }, children: [
9920
9982
  hasContent(leading) && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(TopNavigationContext.Provider, { value: { ...geometry2, slot: "leading" }, children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(import_react_native60.View, { onLayout: square("leading"), style: { minHeight: geometry2.controlSize, minWidth: 0, maxWidth: "40%", flexShrink: 1, justifyContent: "center", marginLeft: iconEdges.leading ? -edgeInset : 0 }, children: content(leading) }) }),
@@ -9990,7 +10052,7 @@ function KjunBottomNavigation({ items: items2, value, ariaLabel, safeAreaBottom
9990
10052
  }
9991
10053
  function KjunBottomActionBar({ description, children, safeAreaBottom = 0, keyboardVisible = false, hideOnKeyboard = false }) {
9992
10054
  const { colors } = useKjunStyles();
9993
- const [width, setWidth] = (0, import_react65.useState)(0);
10055
+ const [width, setWidth] = (0, import_react66.useState)(0);
9994
10056
  const geometry2 = import_tokens60.tokens.extensions.navigation;
9995
10057
  const wide = width >= import_tokens60.tokens.responsive.bottomAction + geometry2.padding * 2;
9996
10058
  const wrapping = import_react_native60.Platform.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : {};
@@ -10012,17 +10074,17 @@ function KjunBottomActionBar({ description, children, safeAreaBottom = 0, keyboa
10012
10074
  }
10013
10075
 
10014
10076
  // packages/native/src/media.tsx
10015
- var import_react66 = require("react");
10077
+ var import_react67 = require("react");
10016
10078
  var import_react_native61 = require("react-native");
10017
10079
  var import_tokens61 = require("@kjun-ui/tokens");
10018
10080
  var import_jsx_runtime58 = require("react/jsx-runtime");
10019
- var ImageBackgroundContext = (0, import_react66.createContext)(void 0);
10081
+ var ImageBackgroundContext = (0, import_react67.createContext)(void 0);
10020
10082
  function KjunImage({ src, source, alt, decorative = false, aspectRatio = 1, fit = "cover", fallback: fallback2, onLoad, onError }) {
10021
- const { colors } = useKjunStyles(), background = (0, import_react66.useContext)(ImageBackgroundContext);
10022
- const request = JSON.stringify(source != null ? source : src), current = (0, import_react66.useRef)({ request, version: 0 });
10083
+ const { colors } = useKjunStyles(), background = (0, import_react67.useContext)(ImageBackgroundContext);
10084
+ const request = JSON.stringify(source != null ? source : src), current = (0, import_react67.useRef)({ request, version: 0 });
10023
10085
  if (current.current.request !== request) current.current = { request, version: current.current.version + 1 };
10024
10086
  const version = current.current.version;
10025
- const [failed, setFailed] = (0, import_react66.useState)(null);
10087
+ const [failed, setFailed] = (0, import_react67.useState)(null);
10026
10088
  const missing = !source && !src;
10027
10089
  return /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(import_react_native61.View, { accessibilityElementsHidden: decorative, importantForAccessibility: decorative ? "no-hide-descendants" : "auto", "aria-hidden": decorative || void 0, style: { width: "100%", aspectRatio: aspectRatio > 0 ? aspectRatio : 1, overflow: "hidden", backgroundColor: background != null ? background : colors.secondary }, children: !missing && failed !== version ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(import_react_native61.Image, { source: source != null ? source : { uri: src }, accessible: !decorative, accessibilityLabel: decorative ? void 0 : alt, resizeMode: fit, style: { position: "absolute", inset: 0, width: "100%", height: "100%" }, onLoad: () => {
10028
10090
  if (version === current.current.version) onLoad == null ? void 0 : onLoad();
@@ -10087,7 +10149,7 @@ function KjunFab({ icon, ariaLabel, disabled = false, safeAreaBottom = 0, offset
10087
10149
  }
10088
10150
 
10089
10151
  // packages/native/src/range-input.tsx
10090
- var import_react67 = require("react");
10152
+ var import_react68 = require("react");
10091
10153
  var import_react_native63 = require("react-native");
10092
10154
  var import_tokens64 = require("@kjun-ui/tokens");
10093
10155
  var import_jsx_runtime61 = require("react/jsx-runtime");
@@ -10112,18 +10174,18 @@ function RangeInput({
10112
10174
  const valid = domain.valid && (!Array.isArray(value) || value.length === 2) && values.every(domain.accepts) && (values.length === 1 || values[0] <= values[1]);
10113
10175
  const blocked = disabled || !valid;
10114
10176
  const effectiveMax = valid ? domain.snap(max) : min;
10115
- const [hovered, setHovered] = (0, import_react67.useState)(null);
10116
- const [dragging, setDragging] = (0, import_react67.useState)(null);
10117
- const [focused, setFocused] = (0, import_react67.useState)(null);
10118
- const track = (0, import_react67.useRef)(null), measured = (0, import_react67.useRef)({ x: 0, width: 1 }), selected = (0, import_react67.useRef)(0), dirty = (0, import_react67.useRef)(false);
10119
- const latest = (0, import_react67.useRef)(values);
10177
+ const [hovered, setHovered] = (0, import_react68.useState)(null);
10178
+ const [dragging, setDragging] = (0, import_react68.useState)(null);
10179
+ const [focused, setFocused] = (0, import_react68.useState)(null);
10180
+ const track = (0, import_react68.useRef)(null), measured = (0, import_react68.useRef)({ x: 0, width: 1 }), selected = (0, import_react68.useRef)(0), dirty = (0, import_react68.useRef)(false);
10181
+ const latest = (0, import_react68.useRef)(values);
10120
10182
  latest.current = values;
10121
- const callbacks = (0, import_react67.useRef)({ onValueChange, onChangeCommit });
10183
+ const callbacks = (0, import_react68.useRef)({ onValueChange, onChangeCommit });
10122
10184
  callbacks.current = { onValueChange, onChangeCommit };
10123
- (0, import_react67.useEffect)(() => {
10185
+ (0, import_react68.useEffect)(() => {
10124
10186
  if (!valid) console.warn("KJUN Slider: \uBC94\uC704\xB7step\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
10125
10187
  }, [valid]);
10126
- (0, import_react67.useEffect)(() => {
10188
+ (0, import_react68.useEffect)(() => {
10127
10189
  if (blocked) {
10128
10190
  setDragging(null);
10129
10191
  setHovered(null);
@@ -10163,7 +10225,7 @@ function RangeInput({
10163
10225
  measured.current = { x: pageX, width: Math.max(1, width) };
10164
10226
  });
10165
10227
  };
10166
- const responder = (0, import_react67.useMemo)(
10228
+ const responder = (0, import_react68.useMemo)(
10167
10229
  () => import_react_native63.PanResponder.create({
10168
10230
  onStartShouldSetPanResponderCapture: () => !disabled && valid,
10169
10231
  onMoveShouldSetPanResponderCapture: (_event, gesture) => !disabled && valid && Math.abs(gesture.dx) > 3 && Math.abs(gesture.dx) > Math.abs(gesture.dy),
@@ -10388,7 +10450,7 @@ function KjunRangeSlider(props) {
10388
10450
  }
10389
10451
 
10390
10452
  // packages/native/src/quantity-stepper.tsx
10391
- var import_react68 = require("react");
10453
+ var import_react69 = require("react");
10392
10454
  var import_react_native64 = require("react-native");
10393
10455
  var import_tokens65 = require("@kjun-ui/tokens");
10394
10456
 
@@ -10411,16 +10473,16 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
10411
10473
  const increaseStyle = { ...buttonStyle, borderRadius: 0, borderTopRightRadius: outer, borderBottomRightRadius: outer };
10412
10474
  const domain = (0, import_tokens65.createNumberDomain)({ min, max, step, precision }), valid = domain.valid && domain.accepts(value), blocked = disabled || !valid;
10413
10475
  const surface = useFieldSurface(size, { disabled: blocked, invalid });
10414
- const [draft, setDraft] = (0, import_react68.useState)(String(value));
10415
- const inputRef = (0, import_react68.useRef)(null);
10416
- const composing = (0, import_react68.useRef)(false), dirty = (0, import_react68.useRef)(false), accepted = (0, import_react68.useRef)(value);
10417
- const cancelledEscape = (0, import_react68.useRef)(false);
10418
- (0, import_react68.useEffect)(() => {
10476
+ const [draft, setDraft] = (0, import_react69.useState)(String(value));
10477
+ const inputRef = (0, import_react69.useRef)(null);
10478
+ const composing = (0, import_react69.useRef)(false), dirty = (0, import_react69.useRef)(false), accepted = (0, import_react69.useRef)(value);
10479
+ const cancelledEscape = (0, import_react69.useRef)(false);
10480
+ (0, import_react69.useEffect)(() => {
10419
10481
  setDraft(String(value));
10420
10482
  accepted.current = value;
10421
10483
  dirty.current = false;
10422
10484
  }, [value, precision, min, max, step]);
10423
- (0, import_react68.useEffect)(() => {
10485
+ (0, import_react69.useEffect)(() => {
10424
10486
  if (!valid) console.warn("KJUN QuantityStepper: \uBC94\uC704\xB7step\xB7\uC815\uBC00\uB3C4\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
10425
10487
  }, [valid]);
10426
10488
  const emit = (next) => {
@@ -10457,7 +10519,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
10457
10519
  } else if (action === "commit") commit();
10458
10520
  else move(action === "increment" ? 1 : -1);
10459
10521
  };
10460
- const handlers = (0, import_react68.useRef)({ key, edit: (text) => {
10522
+ const handlers = (0, import_react69.useRef)({ key, edit: (text) => {
10461
10523
  dirty.current = true;
10462
10524
  setDraft(text);
10463
10525
  } });
@@ -10465,7 +10527,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
10465
10527
  dirty.current = true;
10466
10528
  setDraft(text);
10467
10529
  } };
10468
- (0, import_react68.useEffect)(() => {
10530
+ (0, import_react69.useEffect)(() => {
10469
10531
  if (import_react_native64.Platform.OS !== "web") return;
10470
10532
  const input = inputRef.current;
10471
10533
  const keydown = (event) => handlers.current.key(event);
@@ -10537,7 +10599,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
10537
10599
  }
10538
10600
 
10539
10601
  // packages/native/src/time-picker.tsx
10540
- var import_react69 = require("react");
10602
+ var import_react70 = require("react");
10541
10603
  var import_react_native65 = require("react-native");
10542
10604
  var import_tokens66 = require("@kjun-ui/tokens");
10543
10605
  var import_jsx_runtime63 = require("react/jsx-runtime");
@@ -10546,12 +10608,12 @@ function KjunTimePicker({ value, precision = "minute", min, max, minuteStep = 1,
10546
10608
  ariaLabel != null ? ariaLabel : ariaLabel = m.time;
10547
10609
  const geometry2 = import_tokens66.tokens.extensions.timePicker, spec = import_tokens66.tokens.input[size];
10548
10610
  const { colors } = useKjunStyles();
10549
- const first = (0, import_react69.useRef)(null);
10550
- const [focused, setFocused] = (0, import_react69.useState)(false), [opened, setOpened] = (0, import_react69.useState)(null);
10551
- const field = useKjunField(), uid = (0, import_react69.useId)();
10611
+ const first = (0, import_react70.useRef)(null);
10612
+ const [focused, setFocused] = (0, import_react70.useState)(false), [opened, setOpened] = (0, import_react70.useState)(null);
10613
+ const field = useKjunField(), uid = (0, import_react70.useId)();
10552
10614
  const fieldId = (field == null ? void 0 : field.id) || `kjun-time-${uid}`;
10553
- const domain = (0, import_react69.useMemo)(() => (0, import_tokens66.createTimeDomain)({ precision, min, max, minuteStep, secondStep }), [precision, min, max, minuteStep, secondStep]), valid = domain.valid && domain.accepts(value);
10554
- (0, import_react69.useEffect)(() => {
10615
+ const domain = (0, import_react70.useMemo)(() => (0, import_tokens66.createTimeDomain)({ precision, min, max, minuteStep, secondStep }), [precision, min, max, minuteStep, secondStep]), valid = domain.valid && domain.accepts(value);
10616
+ (0, import_react70.useEffect)(() => {
10555
10617
  if (!valid) console.warn("KJUN TimePicker: \uC2DC\uAC01 \uBC94\uC704\xB7\uAC04\uACA9\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
10556
10618
  }, [valid]);
10557
10619
  const change = (next) => {
@@ -10562,7 +10624,7 @@ function KjunTimePicker({ value, precision = "minute", min, max, minuteStep = 1,
10562
10624
  };
10563
10625
  const time = value === null ? null : domain.parse(value);
10564
10626
  return /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(import_react_native65.View, { accessibilityLabel: ariaLabel, style: { flexDirection: "row", alignItems: "center", maxWidth: "100%", minWidth: 0, backgroundColor: colors.inputBg, borderRadius: spec.radius }, children: [
10565
- /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(import_react_native65.View, { style: { flex: 1, flexDirection: "row", alignItems: "center", minWidth: 0 }, children: [m.hour, m.minute, m.second].slice(0, precision === "second" ? 3 : 2).map((label, index) => /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(import_react69.Fragment, { children: [
10627
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(import_react_native65.View, { style: { flex: 1, flexDirection: "row", alignItems: "center", minWidth: 0 }, children: [m.hour, m.minute, m.second].slice(0, precision === "second" ? 3 : 2).map((label, index) => /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(import_react70.Fragment, { children: [
10566
10628
  index > 0 && /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(KText, { "aria-hidden": true, style: { ...inputTypeStyle(size), width: geometry2.separatorWidth, textAlign: "center", color: disabled || !valid ? colors.textDisabled : colors.textSecondary }, children: ":" }),
10567
10629
  /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(FieldContext.Provider, { value: { ...field, id: index === 0 ? fieldId : `${fieldId}-${index}` }, children: /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(CompoundControlContext.Provider, { value: { size, focusRef: index === 0 ? first : void 0, onFocusChange: setFocused, leading: index === 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(import_react_native65.View, { style: { flexShrink: 0, minWidth: geometry2.segmentMinimumWidth }, children: /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(KjunSelect, { value: time === null ? null : domain.part(time, index), options: domain.options(value, index).map((n) => ({ value: n, label: String(n).padStart(2, "0") })), onValueChange: (next) => {
10568
10630
  if (typeof next === "number") change(domain.select(value, index, next));