@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.
@@ -1898,13 +1898,25 @@ function KjunFormLayout({
1898
1898
  ) });
1899
1899
  }
1900
1900
 
1901
- // shared/package-runtime/content-presence.ts
1901
+ // shared/package-runtime/use-after-close.ts
1902
1902
  var import_react19 = require("react");
1903
+ function useAfterClose(present, onAfterClose, enabled2 = true) {
1904
+ const was = (0, import_react19.useRef)(present), callback = (0, import_react19.useRef)(onAfterClose);
1905
+ callback.current = onAfterClose;
1906
+ (0, import_react19.useEffect)(() => {
1907
+ var _a;
1908
+ if (was.current && !present && enabled2) (_a = callback.current) == null ? void 0 : _a.call(callback);
1909
+ was.current = present;
1910
+ }, [present, enabled2]);
1911
+ }
1912
+
1913
+ // shared/package-runtime/content-presence.ts
1914
+ var import_react20 = require("react");
1903
1915
  function hasContent(node) {
1904
1916
  if (node == null || typeof node === "boolean") return false;
1905
1917
  if (typeof node === "string") return node.trim().length > 0;
1906
1918
  if (Array.isArray(node)) return node.some(hasContent);
1907
- if ((0, import_react19.isValidElement)(node) && node.type === import_react19.Fragment)
1919
+ if ((0, import_react20.isValidElement)(node) && node.type === import_react20.Fragment)
1908
1920
  return hasContent(node.props.children);
1909
1921
  return true;
1910
1922
  }
@@ -1917,7 +1929,7 @@ function LayerBackdrop({ onPress, style }) {
1917
1929
  }
1918
1930
 
1919
1931
  // packages/native/src/modal.tsx
1920
- var import_react22 = require("react");
1932
+ var import_react23 = require("react");
1921
1933
 
1922
1934
  // shared/package-runtime/form-actions.ts
1923
1935
  function formActionsNeedStack(width, widths, gap) {
@@ -1930,17 +1942,17 @@ var import_tokens11 = require("@kjun-ui/tokens");
1930
1942
  var import_react_native14 = require("react-native");
1931
1943
 
1932
1944
  // packages/native/src/overlay-host.tsx
1933
- var import_react21 = require("react");
1945
+ var import_react22 = require("react");
1934
1946
  var import_react_native12 = require("react-native");
1935
1947
 
1936
1948
  // packages/native/src/modal-window.web.tsx
1937
- var import_react20 = require("react");
1949
+ var import_react21 = require("react");
1938
1950
  var import_react_dom = require("react-dom");
1939
1951
  var import_react_native11 = require("react-native");
1940
1952
  var import_jsx_runtime11 = require("react/jsx-runtime");
1941
1953
  function ModalWindow({ generation, children, visible, ...props }) {
1942
- const [container, setContainer] = (0, import_react20.useState)(null);
1943
- (0, import_react20.useLayoutEffect)(() => {
1954
+ const [container, setContainer] = (0, import_react21.useState)(null);
1955
+ (0, import_react21.useLayoutEffect)(() => {
1944
1956
  const element = document.createElement("div");
1945
1957
  element.style.display = "contents";
1946
1958
  setContainer(element);
@@ -1961,17 +1973,18 @@ var import_jsx_runtime12 = (
1961
1973
  // a new window even when an interrupted exit retained the old surface.
1962
1974
  require("react/jsx-runtime")
1963
1975
  );
1964
- var FeedbackOverlayContext = (0, import_react21.createContext)(
1976
+ var FeedbackOverlayContext = (0, import_react22.createContext)(
1965
1977
  null
1966
1978
  );
1967
1979
  function ScopedNativeModal({ children, visible, ...props }) {
1968
- var _a;
1969
- const layers = useLayerState(), scope = (0, import_react21.useContext)(LayerScope);
1980
+ const layers = useLayerState(), scope = (0, import_react22.useContext)(LayerScope);
1970
1981
  const entry = scope ? layers.entries.get(scope) : void 0;
1971
- const generation = (_a = entry == null ? void 0 : entry.order) != null ? _a : 0;
1982
+ const last = (0, import_react22.useRef)(0);
1983
+ if (entry) last.current = entry.order;
1984
+ const generation = last.current;
1972
1985
  const inactive = !!scope && !layers.isActive(scope);
1973
- const host = (0, import_react21.useContext)(FeedbackOverlayContext), id = "kjun-" + (0, import_react21.useId)(), register = host == null ? void 0 : host.register;
1974
- (0, import_react21.useEffect)(
1986
+ const host = (0, import_react22.useContext)(FeedbackOverlayContext), id = "kjun-" + (0, import_react22.useId)(), register = host == null ? void 0 : host.register;
1987
+ (0, import_react22.useEffect)(
1975
1988
  () => visible && register ? register(id) : void 0,
1976
1989
  [visible, register, id, generation]
1977
1990
  );
@@ -2020,7 +2033,7 @@ function KjunFormActions({
2020
2033
  cancelText != null ? cancelText : cancelText = m.cancel;
2021
2034
  confirmText != null ? confirmText : confirmText = m.save;
2022
2035
  const { fontFamily } = useKjunStyles();
2023
- const [width, setWidth] = (0, import_react22.useState)(0), [measurements, setMeasurements] = (0, import_react22.useState)({});
2036
+ const [width, setWidth] = (0, import_react23.useState)(0), [measurements, setMeasurements] = (0, import_react23.useState)({});
2024
2037
  const labels = [showCancel && { text: cancelText }, showConfirm && { text: confirmText }].filter(Boolean);
2025
2038
  const keyFor = (text) => `${fontFamily}:${size}:${text}`;
2026
2039
  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));
@@ -2092,6 +2105,7 @@ function KjunModal({
2092
2105
  open,
2093
2106
  onOpenChange,
2094
2107
  onClose,
2108
+ onAfterClose,
2095
2109
  onCancel,
2096
2110
  onConfirm,
2097
2111
  title = "",
@@ -2124,7 +2138,7 @@ function KjunModal({
2124
2138
  const motion = useNativePresence(open, import_tokens11.tokens.motion.layerEnter, import_tokens11.tokens.motion.layerExit, true, 0, { fade: import_tokens11.tokens.motion.backdropExit });
2125
2139
  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);
2126
2140
  const backdrop = useNativePresence(open, import_tokens11.tokens.motion.backdrop, import_tokens11.tokens.motion.backdropExit, true, 0, { fade: import_tokens11.tokens.motion.backdropExit });
2127
- (0, import_react22.useLayoutEffect)(() => {
2141
+ (0, import_react23.useLayoutEffect)(() => {
2128
2142
  if (!motion.present) {
2129
2143
  offset.stopAnimation();
2130
2144
  offset.setValue(import_tokens11.tokens.motionDistance.modalEnter);
@@ -2139,10 +2153,15 @@ function KjunModal({
2139
2153
  const layer = useLayer(open, motion.present, "window", () => {
2140
2154
  if (closeOnEsc) close();
2141
2155
  });
2156
+ const iosDismiss = import_react_native14.Platform.OS === "ios";
2157
+ useAfterClose(motion.present, onAfterClose, !iosDismiss);
2142
2158
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(LayerScope.Provider, { value: layer.id, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2143
2159
  ScopedNativeModal,
2144
2160
  {
2145
2161
  visible: motion.present,
2162
+ onDismiss: iosDismiss ? () => {
2163
+ if (!open) onAfterClose == null ? void 0 : onAfterClose();
2164
+ } : void 0,
2146
2165
  transparent: true,
2147
2166
  animationType: "none",
2148
2167
  onRequestClose: () => {
@@ -2300,7 +2319,7 @@ function KjunModal({
2300
2319
 
2301
2320
  // packages/native/src/card.tsx
2302
2321
  var import_tokens12 = require("@kjun-ui/tokens");
2303
- var import_react23 = require("react");
2322
+ var import_react24 = require("react");
2304
2323
  var import_react_native15 = require("react-native");
2305
2324
  var import_react_native_svg2 = __toESM(require("react-native-svg"), 1);
2306
2325
  var import_jsx_runtime14 = require("react/jsx-runtime");
@@ -2326,7 +2345,7 @@ function KjunCard({
2326
2345
  onPress
2327
2346
  }) {
2328
2347
  const { colors } = useKjunStyles();
2329
- const id = "kjun-" + (0, import_react23.useId)().replace(/[^a-z0-9]/gi, "");
2348
+ const id = "kjun-" + (0, import_react24.useId)().replace(/[^a-z0-9]/gi, "");
2330
2349
  const inset = import_tokens12.tokens.card.padding[padding], bodyInset = import_tokens12.tokens.card.padding[bodyPadding];
2331
2350
  const corner = import_tokens12.tokens.card.radii[radius];
2332
2351
  const paint = import_tokens12.tokens.cardSurfaces[surface];
@@ -2410,7 +2429,7 @@ function alertRole(tone, live) {
2410
2429
 
2411
2430
  // packages/native/src/display-primitives.tsx
2412
2431
  var import_tokens14 = require("@kjun-ui/tokens");
2413
- var import_react24 = require("react");
2432
+ var import_react25 = require("react");
2414
2433
  var import_react_native16 = require("react-native");
2415
2434
  var import_react_native_svg3 = __toESM(require("react-native-svg"), 1);
2416
2435
 
@@ -2534,7 +2553,7 @@ function KjunEmpty({
2534
2553
  text != null ? text : text = m.noData;
2535
2554
  const { colors } = useKjunStyles();
2536
2555
  const spec = import_tokens14.tokens.extensions.empty;
2537
- const stateContent = (0, import_react24.useContext)(DataStateContentContext);
2556
+ const stateContent = (0, import_react25.useContext)(DataStateContentContext);
2538
2557
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
2539
2558
  import_react_native16.View,
2540
2559
  {
@@ -2568,8 +2587,8 @@ function KjunEmpty({
2568
2587
  }
2569
2588
  function KjunSpinner({ size = "md", text }) {
2570
2589
  const m = useKjunMessages();
2571
- const { colors } = useKjunStyles(), reduced = useReducedMotion(), rotation = (0, import_react24.useRef)(new import_react_native16.Animated.Value(0)).current;
2572
- (0, import_react24.useEffect)(() => {
2590
+ const { colors } = useKjunStyles(), reduced = useReducedMotion(), rotation = (0, import_react25.useRef)(new import_react_native16.Animated.Value(0)).current;
2591
+ (0, import_react25.useEffect)(() => {
2573
2592
  if (reduced) return;
2574
2593
  const loop = import_react_native16.Animated.loop(
2575
2594
  import_react_native16.Animated.timing(rotation, {
@@ -2658,7 +2677,7 @@ function KjunAlert({
2658
2677
  }) {
2659
2678
  const m = useKjunMessages();
2660
2679
  const { colors } = useKjunStyles();
2661
- const [visible, setVisible] = (0, import_react24.useState)(true);
2680
+ const [visible, setVisible] = (0, import_react25.useState)(true);
2662
2681
  if (!visible) return null;
2663
2682
  const tone = variant || (type === "error" ? "danger" : type), small = size === "sm";
2664
2683
  const geometry2 = import_tokens14.tokens.extensions.alert;
@@ -2862,7 +2881,7 @@ function KjunProgress({
2862
2881
 
2863
2882
  // packages/native/src/skeleton-base.tsx
2864
2883
  var import_tokens15 = require("@kjun-ui/tokens");
2865
- var import_react25 = require("react");
2884
+ var import_react26 = require("react");
2866
2885
  var import_react_native17 = require("react-native");
2867
2886
  var import_jsx_runtime16 = require("react/jsx-runtime");
2868
2887
  function KjunSkeleton({
@@ -2874,8 +2893,8 @@ function KjunSkeleton({
2874
2893
  animated = true,
2875
2894
  rounded = true
2876
2895
  }) {
2877
- const { colors } = useKjunStyles(), reduced = useReducedMotion(), opacity = (0, import_react25.useRef)(new import_react_native17.Animated.Value(1)).current;
2878
- (0, import_react25.useEffect)(() => {
2896
+ const { colors } = useKjunStyles(), reduced = useReducedMotion(), opacity = (0, import_react26.useRef)(new import_react_native17.Animated.Value(1)).current;
2897
+ (0, import_react26.useEffect)(() => {
2879
2898
  if (!animated || reduced) return;
2880
2899
  const loop = import_react_native17.Animated.loop(
2881
2900
  import_react_native17.Animated.sequence([
@@ -3030,7 +3049,7 @@ function KjunSkeleton({
3030
3049
  }
3031
3050
 
3032
3051
  // packages/native/src/layout-primitives.tsx
3033
- var import_react26 = require("react");
3052
+ var import_react27 = require("react");
3034
3053
  var import_react_native18 = require("react-native");
3035
3054
  var import_tokens16 = require("@kjun-ui/tokens");
3036
3055
 
@@ -3053,13 +3072,15 @@ var toneRole = {
3053
3072
  inverse: "onBrand"
3054
3073
  };
3055
3074
  var textAlign = { start: "left", center: "center", end: "right" };
3056
- function KjunText({ variant = "body", tone = "default", align, maxLines, style, children }) {
3075
+ function KjunText({ variant = "body", tone = "default", align, maxLines, style, children, ...rest }) {
3076
+ var _a;
3057
3077
  const { colors, fontFamily, numericFontFamily } = useKjunStyles();
3058
3078
  const lines = maxLines && maxLines > 0 ? Math.floor(maxLines) : void 0;
3059
3079
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3060
3080
  KText,
3061
3081
  {
3062
- accessibilityRole: titleVariants.includes(variant) ? "header" : void 0,
3082
+ ...rest,
3083
+ accessibilityRole: (_a = rest.accessibilityRole) != null ? _a : titleVariants.includes(variant) ? "header" : void 0,
3063
3084
  numberOfLines: lines,
3064
3085
  ellipsizeMode: lines ? "tail" : void 0,
3065
3086
  style: [
@@ -3074,23 +3095,23 @@ function KjunText({ variant = "body", tone = "default", align, maxLines, style,
3074
3095
  );
3075
3096
  }
3076
3097
  function flatten(children, path = "") {
3077
- return import_react26.Children.toArray(children).flatMap((child, i) => {
3098
+ return import_react27.Children.toArray(children).flatMap((child, i) => {
3078
3099
  const key = `${path}${i}`;
3079
- return (0, import_react26.isValidElement)(child) && child.type === import_react26.Fragment ? flatten(child.props.children, `${key}.`) : [[key, child]];
3100
+ return (0, import_react27.isValidElement)(child) && child.type === import_react27.Fragment ? flatten(child.props.children, `${key}.`) : [[key, child]];
3080
3101
  });
3081
3102
  }
3082
3103
  function items(children, childAlign) {
3083
3104
  return flatten(children).map(([key, child]) => {
3084
3105
  if (typeof child === "string" || typeof child === "number") return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(KText, { children: child }, key);
3085
- if ((0, import_react26.isValidElement)(child) && child.type === KjunFormGroup) {
3106
+ if ((0, import_react27.isValidElement)(child) && child.type === KjunFormGroup) {
3086
3107
  const group = child;
3087
- return (0, import_react26.cloneElement)(group, { key, style: [{ marginBottom: 0 }, group.props.style] });
3108
+ return (0, import_react27.cloneElement)(group, { key, style: [{ marginBottom: 0 }, group.props.style] });
3088
3109
  }
3089
- if (childAlign && (0, import_react26.isValidElement)(child) && (child.type === KjunButton || child.type === KjunBadge)) {
3110
+ if (childAlign && (0, import_react27.isValidElement)(child) && (child.type === KjunButton || child.type === KjunBadge)) {
3090
3111
  const control = child;
3091
- return (0, import_react26.cloneElement)(control, { key, style: [{ alignSelf: childAlign }, control.props.style] });
3112
+ return (0, import_react27.cloneElement)(control, { key, style: [{ alignSelf: childAlign }, control.props.style] });
3092
3113
  }
3093
- return (0, import_react26.isValidElement)(child) ? (0, import_react26.cloneElement)(child, { key }) : child;
3114
+ return (0, import_react27.isValidElement)(child) ? (0, import_react27.cloneElement)(child, { key }) : child;
3094
3115
  });
3095
3116
  }
3096
3117
  var followed = (align) => align && align !== "stretch" ? crossAxis[align] : void 0;
@@ -3127,13 +3148,13 @@ function KjunInline({ gap = "sm", align, justify = "start", wrap = true, style,
3127
3148
  }
3128
3149
 
3129
3150
  // shared/package-runtime/feedback-dialog.ts
3130
- var import_react27 = require("react");
3151
+ var import_react28 = require("react");
3131
3152
  function useFeedbackDialog(controller, request, messages = kjunMessagesKo) {
3132
3153
  const options = request.options;
3133
- const [value, setValue] = (0, import_react27.useState)(options.initialValue || "");
3134
- const [error, setError] = (0, import_react27.useState)("");
3135
- const [busy, setBusy] = (0, import_react27.useState)(false);
3136
- const working = (0, import_react27.useRef)(false);
3154
+ const [value, setValue] = (0, import_react28.useState)(options.initialValue || "");
3155
+ const [error, setError] = (0, import_react28.useState)("");
3156
+ const [busy, setBusy] = (0, import_react28.useState)(false);
3157
+ const working = (0, import_react28.useRef)(false);
3137
3158
  const active = () => {
3138
3159
  var _a;
3139
3160
  return ((_a = controller.getSnapshot().current) == null ? void 0 : _a.id) === request.id;
@@ -3176,11 +3197,11 @@ function useFeedbackDialog(controller, request, messages = kjunMessagesKo) {
3176
3197
 
3177
3198
  // shared/package-runtime/feedback-motion.ts
3178
3199
  var import_tokens17 = require("@kjun-ui/tokens");
3179
- var import_react28 = require("react");
3200
+ var import_react29 = require("react");
3180
3201
  function usePresentedToasts(toasts, reduced) {
3181
- const [items2, setItems] = (0, import_react28.useState)([]);
3182
- const removals = (0, import_react28.useRef)(/* @__PURE__ */ new Map());
3183
- (0, import_react28.useLayoutEffect)(() => {
3202
+ const [items2, setItems] = (0, import_react29.useState)([]);
3203
+ const removals = (0, import_react29.useRef)(/* @__PURE__ */ new Map());
3204
+ (0, import_react29.useLayoutEffect)(() => {
3184
3205
  const ids = new Set(toasts.map((toast) => toast.id));
3185
3206
  setItems((previous) => {
3186
3207
  const old = new Map(previous.map((item) => [item.toast.id, item]));
@@ -3202,15 +3223,15 @@ function usePresentedToasts(toasts, reduced) {
3202
3223
  removals.current.clear();
3203
3224
  }
3204
3225
  }, [toasts, reduced]);
3205
- (0, import_react28.useLayoutEffect)(() => () => {
3226
+ (0, import_react29.useLayoutEffect)(() => () => {
3206
3227
  for (const timer of removals.current.values()) clearTimeout(timer);
3207
3228
  removals.current.clear();
3208
3229
  }, []);
3209
3230
  return items2;
3210
3231
  }
3211
3232
  function usePresentedRequest(current, reduced) {
3212
- const [request, setRequest] = (0, import_react28.useState)(current);
3213
- (0, import_react28.useLayoutEffect)(() => {
3233
+ const [request, setRequest] = (0, import_react29.useState)(current);
3234
+ (0, import_react29.useLayoutEffect)(() => {
3214
3235
  if (!request || request.id === (current == null ? void 0 : current.id) || reduced) {
3215
3236
  setRequest(current);
3216
3237
  return;
@@ -3222,14 +3243,14 @@ function usePresentedRequest(current, reduced) {
3222
3243
  }
3223
3244
 
3224
3245
  // packages/native/src/feedback-context.ts
3225
- var import_react29 = require("react");
3226
- var KjunFeedbackContext = (0, import_react29.createContext)(null);
3246
+ var import_react30 = require("react");
3247
+ var KjunFeedbackContext = (0, import_react30.createContext)(null);
3227
3248
  function useOptionalKjunFeedback() {
3228
- return (0, import_react29.useContext)(KjunFeedbackContext);
3249
+ return (0, import_react30.useContext)(KjunFeedbackContext);
3229
3250
  }
3230
3251
 
3231
3252
  // packages/native/src/toast-stack.tsx
3232
- var import_react32 = require("react");
3253
+ var import_react33 = require("react");
3233
3254
  var import_react_native20 = require("react-native");
3234
3255
  var import_tokens20 = require("@kjun-ui/tokens");
3235
3256
 
@@ -3247,7 +3268,7 @@ function shadowLayers(layers, colors) {
3247
3268
 
3248
3269
  // packages/native/src/toast.tsx
3249
3270
  var import_tokens18 = require("@kjun-ui/tokens");
3250
- var import_react30 = require("react");
3271
+ var import_react31 = require("react");
3251
3272
  var import_react_native19 = require("react-native");
3252
3273
  var import_jsx_runtime18 = require("react/jsx-runtime");
3253
3274
  function ToastView({
@@ -3255,14 +3276,14 @@ function ToastView({
3255
3276
  controller
3256
3277
  }) {
3257
3278
  const m = useKjunMessages();
3258
- const { colors } = useKjunStyles(), reduced = useReducedMotion(), progress = (0, import_react30.useRef)(new import_react_native19.Animated.Value(1)).current;
3259
- const reasons = (0, import_react30.useRef)({
3279
+ const { colors } = useKjunStyles(), reduced = useReducedMotion(), progress = (0, import_react31.useRef)(new import_react_native19.Animated.Value(1)).current;
3280
+ const reasons = (0, import_react31.useRef)({
3260
3281
  hover: /* @__PURE__ */ Symbol("hover"),
3261
3282
  actionHover: /* @__PURE__ */ Symbol("actionHover"),
3262
3283
  closeHover: /* @__PURE__ */ Symbol("closeHover"),
3263
3284
  focus: /* @__PURE__ */ Symbol("focus")
3264
3285
  }).current;
3265
- (0, import_react30.useEffect)(() => () => {
3286
+ (0, import_react31.useEffect)(() => () => {
3266
3287
  for (const reason of Object.values(reasons)) controller.resumeToast(toast.id, reason);
3267
3288
  }, [controller, toast.id, reasons]);
3268
3289
  const tone = toast.type === "error" ? "danger" : toast.type || "info", icon = {
@@ -3271,7 +3292,7 @@ function ToastView({
3271
3292
  warning: "alert-triangle",
3272
3293
  info: "info-circle"
3273
3294
  }[tone];
3274
- (0, import_react30.useEffect)(() => {
3295
+ (0, import_react31.useEffect)(() => {
3275
3296
  progress.stopAnimation();
3276
3297
  progress.setValue(toast.duration ? toast.remaining / toast.duration : 1);
3277
3298
  if (!toast.paused && !reduced) {
@@ -3385,7 +3406,7 @@ function ToastView({
3385
3406
  }
3386
3407
 
3387
3408
  // packages/native/src/toast-reflow.web.ts
3388
- var import_react31 = require("react");
3409
+ var import_react32 = require("react");
3389
3410
 
3390
3411
  // shared/package-runtime/toast-layout.ts
3391
3412
  var import_tokens19 = require("@kjun-ui/tokens");
@@ -3448,27 +3469,27 @@ function createToastLayout() {
3448
3469
 
3449
3470
  // packages/native/src/toast-reflow.web.ts
3450
3471
  function useToastReflow(root, reduced) {
3451
- const layout = (0, import_react31.useRef)(createToastLayout()).current;
3452
- (0, import_react31.useLayoutEffect)(() => {
3472
+ const layout = (0, import_react32.useRef)(createToastLayout()).current;
3473
+ (0, import_react32.useLayoutEffect)(() => {
3453
3474
  if (root.current) layout.update(root.current, reduced);
3454
3475
  });
3455
- (0, import_react31.useLayoutEffect)(() => () => layout.destroy(), [layout]);
3476
+ (0, import_react32.useLayoutEffect)(() => () => layout.destroy(), [layout]);
3456
3477
  }
3457
3478
 
3458
3479
  // packages/native/src/toast-stack.tsx
3459
3480
  var import_jsx_runtime19 = require("react/jsx-runtime");
3460
3481
  function ToastSlot({ item, controller, boxes }) {
3461
- const born = (0, import_react32.useRef)(Math.min(import_tokens20.tokens.motion.toastEnter, Date.now() - item.born)).current;
3482
+ const born = (0, import_react33.useRef)(Math.min(import_tokens20.tokens.motion.toastEnter, Date.now() - item.born)).current;
3462
3483
  const motion = useNativePresence(!item.exiting, import_tokens20.tokens.motion.toastEnter - born, import_tokens20.tokens.motion.toastExit, true, born / import_tokens20.tokens.motion.toastEnter);
3463
- const offset = (0, import_react32.useRef)(new import_react_native20.Animated.Value(0)).current;
3484
+ const offset = (0, import_react33.useRef)(new import_react_native20.Animated.Value(0)).current;
3464
3485
  const box = boxes.get(item.toast.id);
3465
- (0, import_react32.useLayoutEffect)(() => {
3486
+ (0, import_react33.useLayoutEffect)(() => {
3466
3487
  if (motion.reduced) {
3467
3488
  offset.stopAnimation();
3468
3489
  offset.setValue(0);
3469
3490
  }
3470
3491
  }, [motion.reduced, offset]);
3471
- (0, import_react32.useLayoutEffect)(() => () => offset.stopAnimation(), [offset]);
3492
+ (0, import_react33.useLayoutEffect)(() => () => offset.stopAnimation(), [offset]);
3472
3493
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3473
3494
  import_react_native20.Animated.View,
3474
3495
  {
@@ -3507,9 +3528,9 @@ function ToastSlot({ item, controller, boxes }) {
3507
3528
  function ToastStack({ items: items2, controller }) {
3508
3529
  var _a;
3509
3530
  const layers = useLayerState();
3510
- const root = (0, import_react32.useRef)(null), reduced = useReducedMotion();
3531
+ const root = (0, import_react33.useRef)(null), reduced = useReducedMotion();
3511
3532
  useToastReflow(root, reduced);
3512
- const boxes = (0, import_react32.useRef)(/* @__PURE__ */ new Map()).current;
3533
+ const boxes = (0, import_react33.useRef)(/* @__PURE__ */ new Map()).current;
3513
3534
  for (const id of boxes.keys()) if (!items2.some((item) => item.toast.id === id)) boxes.delete(id);
3514
3535
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native20.View, { ref: root, pointerEvents: "box-none", style: {
3515
3536
  position: "absolute",
@@ -3524,7 +3545,7 @@ function ToastStack({ items: items2, controller }) {
3524
3545
 
3525
3546
  // packages/native/src/feedback.tsx
3526
3547
  var import_tokens21 = require("@kjun-ui/tokens");
3527
- var import_react33 = require("react");
3548
+ var import_react34 = require("react");
3528
3549
  var import_react_native21 = require("react-native");
3529
3550
  var import_tokens22 = require("@kjun-ui/tokens");
3530
3551
  var import_jsx_runtime20 = require("react/jsx-runtime");
@@ -3576,11 +3597,11 @@ function FeedbackDialog({
3576
3597
  );
3577
3598
  }
3578
3599
  function KjunFeedbackProvider({ children, style, controller: controllerProp }) {
3579
- const [owned] = (0, import_react33.useState)(() => controllerProp ? void 0 : (0, import_tokens21.createFeedbackController)());
3600
+ const [owned] = (0, import_react34.useState)(() => controllerProp ? void 0 : (0, import_tokens21.createFeedbackController)());
3580
3601
  const controller = controllerProp ? (0, import_tokens21.feedbackControllerOf)(controllerProp) : owned;
3581
- (0, import_react33.useEffect)(() => controller.attach(), [controller]);
3582
- const life = (0, import_react33.useRef)(0);
3583
- const snapshot = (0, import_react33.useSyncExternalStore)(
3602
+ (0, import_react34.useEffect)(() => controller.attach(), [controller]);
3603
+ const life = (0, import_react34.useRef)(0);
3604
+ const snapshot = (0, import_react34.useSyncExternalStore)(
3584
3605
  controller.subscribe,
3585
3606
  controller.getSnapshot,
3586
3607
  controller.getSnapshot
@@ -3588,7 +3609,7 @@ function KjunFeedbackProvider({ children, style, controller: controllerProp }) {
3588
3609
  const reduced = useReducedMotion();
3589
3610
  const toasts = usePresentedToasts(snapshot.toasts, reduced);
3590
3611
  const dialog = usePresentedRequest(snapshot.current, reduced);
3591
- (0, import_react33.useEffect)(() => {
3612
+ (0, import_react34.useEffect)(() => {
3592
3613
  const version = ++life.current;
3593
3614
  return () => {
3594
3615
  queueMicrotask(() => {
@@ -3596,8 +3617,8 @@ function KjunFeedbackProvider({ children, style, controller: controllerProp }) {
3596
3617
  });
3597
3618
  };
3598
3619
  }, [controller]);
3599
- const [layers, setLayers] = (0, import_react33.useState)([]);
3600
- const register = (0, import_react33.useCallback)((id) => {
3620
+ const [layers, setLayers] = (0, import_react34.useState)([]);
3621
+ const register = (0, import_react34.useCallback)((id) => {
3601
3622
  setLayers((previous) => [...previous, id]);
3602
3623
  return () => setLayers((previous) => previous.filter((item) => item !== id));
3603
3624
  }, []);
@@ -3628,7 +3649,7 @@ function KjunFeedbackProvider({ children, style, controller: controllerProp }) {
3628
3649
  // packages/native/src/choice-controls.tsx
3629
3650
  var import_react_native22 = require("react-native");
3630
3651
  var import_tokens23 = require("@kjun-ui/tokens");
3631
- var import_react34 = require("react");
3652
+ var import_react35 = require("react");
3632
3653
  var import_react_native23 = require("react-native");
3633
3654
  var import_jsx_runtime21 = require("react/jsx-runtime");
3634
3655
  function KjunCheckbox({
@@ -3699,7 +3720,7 @@ function KjunCheckbox({
3699
3720
  }
3700
3721
  function useControlFocus(disabled) {
3701
3722
  const { colors } = useKjunStyles();
3702
- const [focused, setFocused] = (0, import_react34.useState)(false);
3723
+ const [focused, setFocused] = (0, import_react35.useState)(false);
3703
3724
  return {
3704
3725
  handlers: {
3705
3726
  focusRing: false,
@@ -3728,7 +3749,7 @@ function KjunSwitch({
3728
3749
  const offset = width - height;
3729
3750
  const motion = useMotionValue(value ? 1 : 0);
3730
3751
  const focus = useControlFocus(disabled);
3731
- const [hovered, setHovered] = (0, import_react34.useState)(false);
3752
+ const [hovered, setHovered] = (0, import_react35.useState)(false);
3732
3753
  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;
3733
3754
  const offRole = disabled ? colors.borderSecondary : colors.borderStrong;
3734
3755
  const thumbColor = typeof offRole === "string" && typeof colors.switchThumbOn === "string" ? motion.interpolate({ inputRange: [0, 1], outputRange: [offRole, colors.switchThumbOn] }) : value ? colors.switchThumbOn : offRole;
@@ -3813,7 +3834,7 @@ function KjunSwitch({
3813
3834
  }
3814
3835
  );
3815
3836
  }
3816
- var RadioContext = (0, import_react34.createContext)(null);
3837
+ var RadioContext = (0, import_react35.createContext)(null);
3817
3838
  function KjunRadio({
3818
3839
  value,
3819
3840
  val,
@@ -3824,10 +3845,10 @@ function KjunRadio({
3824
3845
  onValueChange,
3825
3846
  onChange
3826
3847
  }) {
3827
- const group = (0, import_react34.useContext)(RadioContext), { colors } = useKjunStyles(), selected = (group ? group.value : value) === val;
3848
+ const group = (0, import_react35.useContext)(RadioContext), { colors } = useKjunStyles(), selected = (group ? group.value : value) === val;
3828
3849
  const geometry2 = import_tokens23.tokens.extensions.radio;
3829
- const [hovered, setHovered] = (0, import_react34.useState)(false);
3830
- (0, import_react34.useEffect)(() => {
3850
+ const [hovered, setHovered] = (0, import_react35.useState)(false);
3851
+ (0, import_react35.useEffect)(() => {
3831
3852
  if (disabled) setHovered(false);
3832
3853
  }, [disabled]);
3833
3854
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
@@ -3899,7 +3920,7 @@ function KjunRadioGroup({
3899
3920
  onValueChange,
3900
3921
  onChange
3901
3922
  }) {
3902
- const root = (0, import_react34.useRef)(null);
3923
+ const root = (0, import_react35.useRef)(null);
3903
3924
  useRadioKeyboard(root);
3904
3925
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3905
3926
  RadioContext.Provider,
@@ -3939,11 +3960,11 @@ function KjunRadioGroup({
3939
3960
 
3940
3961
  // packages/native/src/selection-groups.tsx
3941
3962
  var import_tokens25 = require("@kjun-ui/tokens");
3942
- var import_react36 = require("react");
3963
+ var import_react37 = require("react");
3943
3964
  var import_react_native25 = require("react-native");
3944
3965
 
3945
3966
  // packages/native/src/button-group-motion.tsx
3946
- var import_react35 = require("react");
3967
+ var import_react36 = require("react");
3947
3968
  var import_react_native24 = require("react-native");
3948
3969
  var import_tokens24 = require("@kjun-ui/tokens");
3949
3970
  var import_jsx_runtime22 = require("react/jsx-runtime");
@@ -3952,16 +3973,16 @@ var easing = import_react_native24.Easing.bezier(...curve);
3952
3973
  var sameBox = (a, b) => a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
3953
3974
  function useButtonGroupMotion(selected) {
3954
3975
  const reduced = useReducedMotion();
3955
- const [layouts, setLayouts] = (0, import_react35.useState)(/* @__PURE__ */ new Map());
3956
- const x = (0, import_react35.useRef)(new import_react_native24.Animated.Value(0)).current;
3957
- const width = (0, import_react35.useRef)(new import_react_native24.Animated.Value(0)).current;
3958
- const previous = (0, import_react35.useRef)(null);
3976
+ const [layouts, setLayouts] = (0, import_react36.useState)(/* @__PURE__ */ new Map());
3977
+ const x = (0, import_react36.useRef)(new import_react_native24.Animated.Value(0)).current;
3978
+ const width = (0, import_react36.useRef)(new import_react_native24.Animated.Value(0)).current;
3979
+ const previous = (0, import_react36.useRef)(null);
3959
3980
  const layout = selected && layouts.get(selected.value);
3960
- const updateLayout = (0, import_react35.useCallback)((value, box) => {
3981
+ const updateLayout = (0, import_react36.useCallback)((value, box) => {
3961
3982
  setLayouts((old) => old.has(value) && sameBox(old.get(value), box) ? old : new Map(old).set(value, box));
3962
3983
  }, []);
3963
- const measure = (0, import_react35.useCallback)((value, event) => updateLayout(value, event.nativeEvent.layout), [updateLayout]);
3964
- (0, import_react35.useLayoutEffect)(() => {
3984
+ const measure = (0, import_react36.useCallback)((value, event) => updateLayout(value, event.nativeEvent.layout), [updateLayout]);
3985
+ (0, import_react36.useLayoutEffect)(() => {
3965
3986
  const old = previous.current;
3966
3987
  if (!selected || !layout) {
3967
3988
  previous.current = null;
@@ -3983,7 +4004,7 @@ function useButtonGroupMotion(selected) {
3983
4004
  }
3984
4005
  previous.current = { value: selected.value, box: layout, reduced };
3985
4006
  }, [selected == null ? void 0 : selected.value, layout, reduced, x, width]);
3986
- (0, import_react35.useEffect)(() => () => {
4007
+ (0, import_react36.useEffect)(() => () => {
3987
4008
  x.stopAnimation();
3988
4009
  width.stopAnimation();
3989
4010
  }, [x, width]);
@@ -3991,9 +4012,9 @@ function useButtonGroupMotion(selected) {
3991
4012
  }
3992
4013
  function ButtonGroupLabel({ label, active, size, reduced }) {
3993
4014
  const { colors, fontFamily } = useKjunStyles();
3994
- const progress = (0, import_react35.useRef)(new import_react_native24.Animated.Value(active ? 1 : 0)).current;
4015
+ const progress = (0, import_react36.useRef)(new import_react_native24.Animated.Value(active ? 1 : 0)).current;
3995
4016
  const interpolate = typeof colors.textSecondary === "string" && typeof colors.text === "string";
3996
- (0, import_react35.useEffect)(() => {
4017
+ (0, import_react36.useEffect)(() => {
3997
4018
  progress.stopAnimation();
3998
4019
  if (reduced || !interpolate) progress.setValue(active ? 1 : 0);
3999
4020
  else import_react_native24.Animated.timing(progress, { toValue: active ? 1 : 0, duration: import_tokens24.tokens.motion.indicator, easing, useNativeDriver: false }).start();
@@ -4040,9 +4061,9 @@ function KjunButtonGroup({
4040
4061
  const motion = useButtonGroupMotion(selected);
4041
4062
  const padding = import_tokens25.tokens.buttonGroup.padding[size];
4042
4063
  const itemRadius = import_tokens25.tokens.buttonGroup.itemRadii[size];
4043
- const itemRefs = (0, import_react36.useRef)(/* @__PURE__ */ new Map());
4064
+ const itemRefs = (0, import_react37.useRef)(/* @__PURE__ */ new Map());
4044
4065
  const { updateLayout } = motion;
4045
- (0, import_react36.useLayoutEffect)(() => {
4066
+ (0, import_react37.useLayoutEffect)(() => {
4046
4067
  for (const [value2, node] of itemRefs.current) {
4047
4068
  node.measure((x, y, width, height) => updateLayout(value2, { x, y, width, height }));
4048
4069
  }
@@ -4211,7 +4232,7 @@ function KjunFilterGroup({
4211
4232
  paddingHorizontal: { xs: import_tokens25.tokens.dimension.value8, sm: import_tokens25.tokens.dimension.value12, md: import_tokens25.tokens.dimension.value12, lg: import_tokens25.tokens.dimension.value14, xl: import_tokens25.tokens.dimension.value16 }[size],
4212
4233
  // Corners follow the button scale with the height, so every size keeps the same shape.
4213
4234
  borderRadius: import_tokens25.tokens.button.radii[size],
4214
- backgroundColor: active ? colors.text : colors.secondary
4235
+ backgroundColor: active ? colors.filterSelectedBg : colors.secondary
4215
4236
  },
4216
4237
  children: [
4217
4238
  option.dot && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
@@ -4230,7 +4251,7 @@ function KjunFilterGroup({
4230
4251
  {
4231
4252
  name: option.icon,
4232
4253
  size: import_tokens25.tokens.extensions.selection.iconSize,
4233
- color: active ? colors.inverse : colors.textSecondary
4254
+ color: active ? colors.filterSelectedText : colors.textSecondary
4234
4255
  }
4235
4256
  ),
4236
4257
  /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react_native25.View, { children: [
@@ -4253,7 +4274,7 @@ function KjunFilterGroup({
4253
4274
  right: 0,
4254
4275
  top: 0,
4255
4276
  ...selectionTypeStyle(size, active),
4256
- color: active ? colors.inverse : colors.textSecondary
4277
+ color: active ? colors.filterSelectedText : colors.textSecondary
4257
4278
  },
4258
4279
  children: option.label
4259
4280
  }
@@ -4281,10 +4302,10 @@ function KjunFilterGroup({
4281
4302
 
4282
4303
  // packages/native/src/textarea.tsx
4283
4304
  var import_tokens26 = require("@kjun-ui/tokens");
4284
- var import_react37 = require("react");
4305
+ var import_react38 = require("react");
4285
4306
  var import_react_native26 = require("react-native");
4286
4307
  var import_jsx_runtime24 = require("react/jsx-runtime");
4287
- var KjunTextarea = (0, import_react37.forwardRef)(
4308
+ var KjunTextarea = (0, import_react38.forwardRef)(
4288
4309
  function KjunTextarea2({
4289
4310
  value,
4290
4311
  size = "md",
@@ -4354,7 +4375,7 @@ var KjunTextarea = (0, import_react37.forwardRef)(
4354
4375
 
4355
4376
  // packages/native/src/drawer.tsx
4356
4377
  var import_tokens27 = require("@kjun-ui/tokens");
4357
- var import_react38 = require("react");
4378
+ var import_react39 = require("react");
4358
4379
  var import_react_native27 = require("react-native");
4359
4380
 
4360
4381
  // shared/package-runtime/drawer-geometry.ts
@@ -4383,8 +4404,8 @@ function drawerFrame({ position, windowHeight, safeAreaTop, safeAreaBottom, gap,
4383
4404
  var import_react_native28 = require("react-native");
4384
4405
  var import_jsx_runtime25 = require("react/jsx-runtime");
4385
4406
  function useKeyboardOverlap(enabled2, windowHeight) {
4386
- const [overlap, setOverlap] = (0, import_react38.useState)(0);
4387
- (0, import_react38.useEffect)(() => {
4407
+ const [overlap, setOverlap] = (0, import_react39.useState)(0);
4408
+ (0, import_react39.useEffect)(() => {
4388
4409
  var _a, _b;
4389
4410
  setOverlap(0);
4390
4411
  const events = keyboardEvents(import_react_native28.Platform.OS);
@@ -4414,6 +4435,7 @@ function KjunDrawer({
4414
4435
  footer,
4415
4436
  children,
4416
4437
  onClose,
4438
+ onAfterClose,
4417
4439
  ariaLabel,
4418
4440
  safeAreaTop = 0,
4419
4441
  safeAreaBottom = 0,
@@ -4422,7 +4444,7 @@ function KjunDrawer({
4422
4444
  }) {
4423
4445
  const m = useKjunMessages();
4424
4446
  const { colors } = useKjunStyles(), window2 = (0, import_react_native28.useWindowDimensions)(), vertical = position === "left" || position === "right";
4425
- const [rootHeight, setRootHeight] = (0, import_react38.useState)(0);
4447
+ const [rootHeight, setRootHeight] = (0, import_react39.useState)(0);
4426
4448
  const keyboard = useKeyboardOverlap(open && avoidKeyboard, window2.height);
4427
4449
  const frame = drawerFrame({
4428
4450
  position,
@@ -4433,7 +4455,7 @@ function KjunDrawer({
4433
4455
  keyboard,
4434
4456
  maxHeight
4435
4457
  });
4436
- const [panelHeight, setPanelHeight] = (0, import_react38.useState)(0);
4458
+ const [panelHeight, setPanelHeight] = (0, import_react39.useState)(0);
4437
4459
  const motion = useNativePresence(open, import_tokens27.tokens.motion.layerEnter, import_tokens27.tokens.motion.layerExit, vertical || panelHeight > 0, 0, { enterEasing: motionEmphasized });
4438
4460
  const backdrop = useNativePresence(open, import_tokens27.tokens.motion.backdrop, import_tokens27.tokens.motion.backdropExit, true, 0, { fade: import_tokens27.tokens.motion.backdropExit });
4439
4461
  const close = () => {
@@ -4444,10 +4466,15 @@ function KjunDrawer({
4444
4466
  const layer = useLayer(open, motion.present, "window", () => {
4445
4467
  if (closeOnEsc) close();
4446
4468
  });
4469
+ const iosDismiss = import_react_native28.Platform.OS === "ios";
4470
+ useAfterClose(motion.present, onAfterClose, !iosDismiss);
4447
4471
  return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(LayerScope.Provider, { value: layer.id, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
4448
4472
  ScopedNativeModal,
4449
4473
  {
4450
4474
  visible: motion.present,
4475
+ onDismiss: iosDismiss ? () => {
4476
+ if (!open) onAfterClose == null ? void 0 : onAfterClose();
4477
+ } : void 0,
4451
4478
  transparent: true,
4452
4479
  onRequestClose: () => {
4453
4480
  if (closeOnEsc) close();
@@ -4574,13 +4601,13 @@ function KjunDrawer({
4574
4601
 
4575
4602
  // packages/native/src/dropdown.tsx
4576
4603
  var import_tokens30 = require("@kjun-ui/tokens");
4577
- var import_react41 = require("react");
4604
+ var import_react42 = require("react");
4578
4605
  var import_react_native32 = require("react-native");
4579
4606
 
4580
4607
  // packages/native/src/floating-panel.tsx
4581
4608
  var import_react_native29 = require("react-native");
4582
4609
  var import_tokens28 = require("@kjun-ui/tokens");
4583
- var import_react39 = require("react");
4610
+ var import_react40 = require("react");
4584
4611
  var import_react_native30 = require("react-native");
4585
4612
  var import_react_native_svg4 = __toESM(require("react-native-svg"), 1);
4586
4613
  var import_jsx_runtime26 = require("react/jsx-runtime");
@@ -4589,9 +4616,9 @@ function LayerTrigger({
4589
4616
  onPress,
4590
4617
  disabled = false
4591
4618
  }) {
4592
- if ((0, import_react39.isValidElement)(trigger)) {
4619
+ if ((0, import_react40.isValidElement)(trigger)) {
4593
4620
  const child = trigger;
4594
- return (0, import_react39.cloneElement)(child, {
4621
+ return (0, import_react40.cloneElement)(child, {
4595
4622
  disabled: disabled || child.props.disabled,
4596
4623
  onPress: () => {
4597
4624
  var _a, _b;
@@ -4626,14 +4653,14 @@ function FloatingPanel({
4626
4653
  }) {
4627
4654
  const m = useKjunMessages();
4628
4655
  ariaLabel != null ? ariaLabel : ariaLabel = m.select;
4629
- const [headerHeight, setHeaderHeight] = (0, import_react39.useState)(0);
4630
- const panelRef = (0, import_react39.useRef)(null);
4631
- const scroll = (0, import_react39.useRef)(null), viewport = (0, import_react39.useRef)(0);
4632
- const { colors } = useKjunStyles(), window2 = (0, import_react_native30.useWindowDimensions)(), [anchored, setAnchored] = (0, import_react39.useState)(false), [sized, setSized] = (0, import_react39.useState)(false), [anchor, setAnchor] = (0, import_react39.useState)({ x: 0, y: 0, width: 0, height: 0 }), [panel, setPanel] = (0, import_react39.useState)({ width: 0, height: 0 });
4633
- const measurement = (0, import_react39.useRef)(0);
4656
+ const [headerHeight, setHeaderHeight] = (0, import_react40.useState)(0);
4657
+ const panelRef = (0, import_react40.useRef)(null);
4658
+ const scroll = (0, import_react40.useRef)(null), viewport = (0, import_react40.useRef)(0);
4659
+ const { colors } = useKjunStyles(), window2 = (0, import_react_native30.useWindowDimensions)(), [anchored, setAnchored] = (0, import_react40.useState)(false), [sized, setSized] = (0, import_react40.useState)(false), [anchor, setAnchor] = (0, import_react40.useState)({ x: 0, y: 0, width: 0, height: 0 }), [panel, setPanel] = (0, import_react40.useState)({ width: 0, height: 0 });
4660
+ const measurement = (0, import_react40.useRef)(0);
4634
4661
  const motion = useNativePresence(open, tooltip ? import_tokens28.tokens.motion.tooltipEnter : import_tokens28.tokens.motion.popupEnter, tooltip ? import_tokens28.tokens.motion.tooltipExit : import_tokens28.tokens.motion.popupExit, anchored && sized);
4635
4662
  const layer = useLayer(open, motion.present, tooltip ? "tooltip" : "popup", () => onOpenChange(false), triggerRef);
4636
- (0, import_react39.useLayoutEffect)(() => {
4663
+ (0, import_react40.useLayoutEffect)(() => {
4637
4664
  var _a;
4638
4665
  const revision = ++measurement.current;
4639
4666
  if (open) setAnchored(false);
@@ -4646,20 +4673,20 @@ function FloatingPanel({
4646
4673
  measurement.current++;
4647
4674
  };
4648
4675
  }, [open, window2.width, window2.height, triggerRef]);
4649
- (0, import_react39.useLayoutEffect)(() => {
4676
+ (0, import_react40.useLayoutEffect)(() => {
4650
4677
  if (!motion.present) {
4651
4678
  setSized(false);
4652
4679
  setAnchored(false);
4653
4680
  }
4654
4681
  }, [motion.present]);
4655
- (0, import_react39.useEffect)(() => {
4682
+ (0, import_react40.useEffect)(() => {
4656
4683
  var _a;
4657
4684
  const row = reveal == null ? void 0 : reveal.current;
4658
4685
  if (!open || !anchored || !sized || !row || !viewport.current) return;
4659
4686
  const offset = row.y + row.height + (menu || field ? import_tokens28.tokens.extensions.menu.padding : 0) - viewport.current;
4660
4687
  if (offset > 0) (_a = scroll.current) == null ? void 0 : _a.scrollTo({ y: offset, animated: false });
4661
4688
  }, [open, anchored, sized, reveal, menu, field]);
4662
- (0, import_react39.useEffect)(() => {
4689
+ (0, import_react40.useEffect)(() => {
4663
4690
  if (!open || !anchored || !sized || !focusOnOpen || !panelRef.current) return;
4664
4691
  if (import_react_native30.Platform.OS === "web") {
4665
4692
  const node = panelRef.current;
@@ -4844,7 +4871,7 @@ function FloatingPanel({
4844
4871
  }
4845
4872
 
4846
4873
  // packages/native/src/popover.tsx
4847
- var import_react40 = require("react");
4874
+ var import_react41 = require("react");
4848
4875
  var import_react_native31 = require("react-native");
4849
4876
  var import_tokens29 = require("@kjun-ui/tokens");
4850
4877
  var import_jsx_runtime27 = require("react/jsx-runtime");
@@ -4865,7 +4892,7 @@ function KjunPopover({
4865
4892
  children
4866
4893
  }) {
4867
4894
  const { colors } = useKjunStyles();
4868
- const [internal, setInternal] = (0, import_react40.useState)(false), open = controlled != null ? controlled : internal, ref = (0, import_react40.useRef)(null);
4895
+ const [internal, setInternal] = (0, import_react41.useState)(false), open = controlled != null ? controlled : internal, ref = (0, import_react41.useRef)(null);
4869
4896
  const change = (v) => {
4870
4897
  setInternal(v);
4871
4898
  onOpenChange == null ? void 0 : onOpenChange(v);
@@ -4904,7 +4931,10 @@ function KjunPopover({
4904
4931
  ] });
4905
4932
  }
4906
4933
  function KjunTooltip({
4907
- content: message = "",
4934
+ content: body = "",
4935
+ title,
4936
+ align,
4937
+ width,
4908
4938
  placement = "top",
4909
4939
  delay = 0,
4910
4940
  children,
@@ -4913,13 +4943,14 @@ function KjunTooltip({
4913
4943
  open: controlled,
4914
4944
  onOpenChange
4915
4945
  }) {
4916
- const { colors } = useKjunStyles(), [internal, setInternal] = (0, import_react40.useState)(false), open = controlled != null ? controlled : internal, latest = (0, import_react40.useRef)({ open, onOpenChange }), setOpen = (value) => {
4946
+ const message = [title, body].filter(Boolean).join(", ");
4947
+ const { colors } = useKjunStyles(), [internal, setInternal] = (0, import_react41.useState)(false), open = controlled != null ? controlled : internal, latest = (0, import_react41.useRef)({ open, onOpenChange }), setOpen = (value) => {
4917
4948
  var _a, _b;
4918
4949
  if (latest.current.open === value) return;
4919
4950
  latest.current.open = value;
4920
4951
  setInternal(value);
4921
4952
  (_b = (_a = latest.current).onOpenChange) == null ? void 0 : _b.call(_a, value);
4922
- }, ref = (0, import_react40.useRef)(null), timer = (0, import_react40.useRef)(void 0);
4953
+ }, ref = (0, import_react41.useRef)(null), timer = (0, import_react41.useRef)(void 0);
4923
4954
  latest.current = { open, onOpenChange };
4924
4955
  const scopeActive = useActiveLayerScope();
4925
4956
  const hide = () => {
@@ -4931,11 +4962,11 @@ function KjunTooltip({
4931
4962
  clearTimeout(timer.current);
4932
4963
  timer.current = setTimeout(() => setOpen(true), delay);
4933
4964
  };
4934
- (0, import_react40.useEffect)(() => {
4965
+ (0, import_react41.useEffect)(() => {
4935
4966
  if (!scopeActive) hide();
4936
4967
  }, [scopeActive]);
4937
- (0, import_react40.useEffect)(() => () => clearTimeout(timer.current), []);
4938
- (0, import_react40.useEffect)(() => {
4968
+ (0, import_react41.useEffect)(() => () => clearTimeout(timer.current), []);
4969
+ (0, import_react41.useEffect)(() => {
4939
4970
  if (import_react_native31.Platform.OS !== "web" || !open) return;
4940
4971
  const escape = (event) => {
4941
4972
  if (event.key === "Escape") hide();
@@ -4943,7 +4974,7 @@ function KjunTooltip({
4943
4974
  document.addEventListener("keydown", escape);
4944
4975
  return () => document.removeEventListener("keydown", escape);
4945
4976
  }, [open]);
4946
- const child = (0, import_react40.isValidElement)(children) ? children : null;
4977
+ const child = (0, import_react41.isValidElement)(children) ? children : null;
4947
4978
  const childProps = child == null ? void 0 : child.props;
4948
4979
  const handlers = {
4949
4980
  // A hint that repeats the trigger's name would be read twice (icon-only RefreshButton, IconToggle).
@@ -4988,7 +5019,7 @@ function KjunTooltip({
4988
5019
  }
4989
5020
  };
4990
5021
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
4991
- /* @__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_react40.cloneElement)(child, handlers) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(AccessiblePressable, { ...handlers, children }) }),
5022
+ /* @__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_react41.cloneElement)(child, handlers) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(AccessiblePressable, { ...handlers, children }) }),
4992
5023
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
4993
5024
  FloatingPanel,
4994
5025
  {
@@ -4998,25 +5029,29 @@ function KjunTooltip({
4998
5029
  triggerRef: ref,
4999
5030
  placement,
5000
5031
  ariaLabel: message,
5001
- children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5032
+ children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
5002
5033
  import_react_native31.View,
5003
5034
  {
5004
5035
  style: {
5005
5036
  paddingVertical: import_tokens29.tokens.extensions.tooltip.paddingY,
5006
5037
  paddingHorizontal: import_tokens29.tokens.extensions.tooltip.paddingX,
5007
- maxWidth: import_tokens29.tokens.extensions.tooltip.maxWidth,
5038
+ maxWidth: width ? import_tokens29.tokens.extensions.tooltip.maxWidths[width] : import_tokens29.tokens.extensions.tooltip.maxWidth,
5008
5039
  backgroundColor: colors.chartTooltipBg
5009
5040
  },
5010
- children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5011
- KText,
5012
- {
5013
- style: {
5014
- ...typeStyle("caption"),
5015
- color: colors.chartTooltipText
5016
- },
5017
- children: message
5018
- }
5019
- )
5041
+ children: [
5042
+ !!title && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(KText, { style: { ...typeStyle("controlSmall"), color: colors.chartTooltipText, ...align ? { textAlign: align === "center" ? "center" : "left" } : {} }, children: title }),
5043
+ !!body && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5044
+ KText,
5045
+ {
5046
+ style: {
5047
+ ...typeStyle("caption"),
5048
+ color: colors.chartTooltipText,
5049
+ ...align ? { textAlign: align === "center" ? "center" : "left" } : {}
5050
+ },
5051
+ children: body
5052
+ }
5053
+ )
5054
+ ]
5020
5055
  }
5021
5056
  )
5022
5057
  }
@@ -5026,10 +5061,10 @@ function KjunTooltip({
5026
5061
 
5027
5062
  // packages/native/src/dropdown.tsx
5028
5063
  var import_jsx_runtime28 = require("react/jsx-runtime");
5029
- var DropdownContext = (0, import_react41.createContext)(null);
5030
- var MenuIconSlotContext = (0, import_react41.createContext)(false);
5064
+ var DropdownContext = (0, import_react42.createContext)(null);
5065
+ var MenuIconSlotContext = (0, import_react42.createContext)(false);
5031
5066
  function hasLeadingIcon(node) {
5032
- return import_react41.Children.toArray(node).some((child) => (0, import_react41.isValidElement)(child) && (child.type === import_react41.Fragment ? hasLeadingIcon(child.props.children) : child.type === KjunDropdownItem && !!child.props.icon));
5067
+ return import_react42.Children.toArray(node).some((child) => (0, import_react42.isValidElement)(child) && (child.type === import_react42.Fragment ? hasLeadingIcon(child.props.children) : child.type === KjunDropdownItem && !!child.props.icon));
5033
5068
  }
5034
5069
  function KjunDropdown({
5035
5070
  disabled = false,
@@ -5040,11 +5075,11 @@ function KjunDropdown({
5040
5075
  onClose
5041
5076
  }) {
5042
5077
  const m = useKjunMessages();
5043
- const [open, setOpen] = (0, import_react41.useState)(false), currentOpen = (0, import_react41.useRef)(false), ref = (0, import_react41.useRef)(null);
5078
+ const [open, setOpen] = (0, import_react42.useState)(false), currentOpen = (0, import_react42.useRef)(false), ref = (0, import_react42.useRef)(null);
5044
5079
  let control = trigger;
5045
- while ((0, import_react41.isValidElement)(control) && control.type === KjunTooltip)
5080
+ while ((0, import_react42.isValidElement)(control) && control.type === KjunTooltip)
5046
5081
  control = control.props.children;
5047
- const unavailable = disabled || (0, import_react41.isValidElement)(control) && !!(control.props.disabled || control.props.loading);
5082
+ const unavailable = disabled || (0, import_react42.isValidElement)(control) && !!(control.props.disabled || control.props.loading);
5048
5083
  const change = (v) => {
5049
5084
  if (v === currentOpen.current || v && unavailable) return;
5050
5085
  currentOpen.current = v;
@@ -5052,7 +5087,7 @@ function KjunDropdown({
5052
5087
  if (v) onOpen == null ? void 0 : onOpen();
5053
5088
  else onClose == null ? void 0 : onClose();
5054
5089
  };
5055
- (0, import_react41.useEffect)(() => {
5090
+ (0, import_react42.useEffect)(() => {
5056
5091
  if (unavailable) change(false);
5057
5092
  }, [unavailable]);
5058
5093
  return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(DropdownContext.Provider, { value: () => change(false), children: [
@@ -5086,8 +5121,8 @@ function KjunDropdownItem({
5086
5121
  children,
5087
5122
  onPress
5088
5123
  }) {
5089
- const { colors } = useKjunStyles(), close = (0, import_react41.useContext)(DropdownContext), color = variant === "danger" ? colors.danger : colors.text, iconColor = variant === "danger" ? colors.danger : colors.textSecondary, reserveIcon = (0, import_react41.useContext)(MenuIconSlotContext);
5090
- const [hovered, setHovered] = (0, import_react41.useState)(false);
5124
+ const { colors } = useKjunStyles(), close = (0, import_react42.useContext)(DropdownContext), color = variant === "danger" ? colors.danger : colors.text, iconColor = variant === "danger" ? colors.danger : colors.textSecondary, reserveIcon = (0, import_react42.useContext)(MenuIconSlotContext);
5125
+ const [hovered, setHovered] = (0, import_react42.useState)(false);
5091
5126
  return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
5092
5127
  AccessiblePressable,
5093
5128
  {
@@ -5148,7 +5183,7 @@ function KjunMenuButton({
5148
5183
  ...props
5149
5184
  }) {
5150
5185
  const m = useKjunMessages();
5151
- const [open, setOpen] = (0, import_react41.useState)(false);
5186
+ const [open, setOpen] = (0, import_react42.useState)(false);
5152
5187
  return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
5153
5188
  KjunDropdown,
5154
5189
  {
@@ -5188,7 +5223,7 @@ function KjunMenuButton({
5188
5223
  }
5189
5224
 
5190
5225
  // shared/package-runtime/combobox.ts
5191
- var import_react42 = require("react");
5226
+ var import_react43 = require("react");
5192
5227
 
5193
5228
  // shared/package-runtime/options.ts
5194
5229
  var optionValue = (option, key = "value") => typeof option === "object" ? option[key] : option;
@@ -5266,10 +5301,10 @@ function useComboboxModel({
5266
5301
  }) {
5267
5302
  const selected = options.find((option) => optionValue(option, valueKey) === value);
5268
5303
  const selectedLabel = selected !== void 0 ? optionLabel(selected, labelKey) : "";
5269
- const [query, setQuery] = (0, import_react42.useState)(selectedLabel);
5270
- const [open, setOpen] = (0, import_react42.useState)(false);
5271
- (0, import_react42.useEffect)(() => setQuery(selectedLabel), [value, selectedLabel]);
5272
- (0, import_react42.useEffect)(() => {
5304
+ const [query, setQuery] = (0, import_react43.useState)(selectedLabel);
5305
+ const [open, setOpen] = (0, import_react43.useState)(false);
5306
+ (0, import_react43.useEffect)(() => setQuery(selectedLabel), [value, selectedLabel]);
5307
+ (0, import_react43.useEffect)(() => {
5273
5308
  if (disabled) setOpen(false);
5274
5309
  }, [disabled]);
5275
5310
  const change = (next) => {
@@ -5303,7 +5338,7 @@ function useComboboxModel({
5303
5338
 
5304
5339
  // packages/native/src/combobox.tsx
5305
5340
  var import_tokens32 = require("@kjun-ui/tokens");
5306
- var import_react43 = require("react");
5341
+ var import_react44 = require("react");
5307
5342
  var import_react_native34 = require("react-native");
5308
5343
 
5309
5344
  // packages/native/src/select-options.tsx
@@ -5432,9 +5467,9 @@ function SearchField({
5432
5467
  onEnter
5433
5468
  }) {
5434
5469
  const m = useKjunMessages();
5435
- const searchId = "kjun-popup-search-" + (0, import_react43.useId)().replace(/:/g, "");
5436
- const clearing = (0, import_react43.useRef)(false);
5437
- const ref = (0, import_react43.useRef)(null), { colors } = useKjunStyles();
5470
+ const searchId = "kjun-popup-search-" + (0, import_react44.useId)().replace(/:/g, "");
5471
+ const clearing = (0, import_react44.useRef)(false);
5472
+ const ref = (0, import_react44.useRef)(null), { colors } = useKjunStyles();
5438
5473
  const focusGuard = usePopupFocusGuard(ref);
5439
5474
  const openFromPress = () => {
5440
5475
  if (!disabled) {
@@ -5534,7 +5569,7 @@ function SearchField({
5534
5569
 
5535
5570
  // packages/native/src/date-picker.tsx
5536
5571
  var import_tokens33 = require("@kjun-ui/tokens");
5537
- var import_react44 = require("react");
5572
+ var import_react45 = require("react");
5538
5573
  var import_react_native35 = require("react-native");
5539
5574
  var import_jsx_runtime31 = require("react/jsx-runtime");
5540
5575
  function displayMonth(value) {
@@ -5557,7 +5592,7 @@ function KjunDatePicker({
5557
5592
  const scope = useKjunMessageScope();
5558
5593
  placeholder != null ? placeholder : placeholder = scope.messages.datePlaceholder;
5559
5594
  const clear = clearable && !!value && !disabled;
5560
- const { colors } = useKjunStyles(), field = useKjunField(), [open, setOpen] = (0, import_react44.useState)(false), [month, setMonth] = (0, import_react44.useState)(() => displayMonth(value)), ref = (0, import_react44.useRef)(null), spec = import_tokens33.tokens.input[size];
5595
+ const { colors } = useKjunStyles(), field = useKjunField(), [open, setOpen] = (0, import_react45.useState)(false), [month, setMonth] = (0, import_react45.useState)(() => displayMonth(value)), ref = (0, import_react45.useRef)(null), spec = import_tokens33.tokens.input[size];
5561
5596
  const surface = useFieldSurface(size, { disabled, invalid: error || !!(field == null ? void 0 : field.error), open });
5562
5597
  const year = month.getFullYear(), m = month.getMonth(), first = new Date(year, m, 1).getDay(), count = new Date(year, m + 1, 0).getDate();
5563
5598
  const date = (day) => `${year}-${String(m + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
@@ -5732,10 +5767,10 @@ function KjunDatePicker({
5732
5767
  }
5733
5768
 
5734
5769
  // packages/native/src/search-input.tsx
5735
- var import_react46 = require("react");
5770
+ var import_react47 = require("react");
5736
5771
 
5737
5772
  // shared/package-runtime/search-input.ts
5738
- var import_react45 = require("react");
5773
+ var import_react46 = require("react");
5739
5774
  function useSearchInputModel({
5740
5775
  value,
5741
5776
  disabled = false,
@@ -5745,20 +5780,20 @@ function useSearchInputModel({
5745
5780
  onValueChange,
5746
5781
  onSearchError
5747
5782
  }, open) {
5748
- const [query, setQuery] = (0, import_react45.useState)(value), [options, setOptions] = (0, import_react45.useState)([]), [loading, setLoading] = (0, import_react45.useState)(false), [failed, setFailed] = (0, import_react45.useState)(false), version = (0, import_react45.useRef)(0), notify = (0, import_react45.useRef)(onSearchError);
5783
+ const [query, setQuery] = (0, import_react46.useState)(value), [options, setOptions] = (0, import_react46.useState)([]), [loading, setLoading] = (0, import_react46.useState)(false), [failed, setFailed] = (0, import_react46.useState)(false), version = (0, import_react46.useRef)(0), notify = (0, import_react46.useRef)(onSearchError);
5749
5784
  notify.current = onSearchError;
5750
- const timer = (0, import_react45.useRef)(void 0);
5751
- const notifyValue = (0, import_react45.useRef)(onValueChange);
5785
+ const timer = (0, import_react46.useRef)(void 0);
5786
+ const notifyValue = (0, import_react46.useRef)(onValueChange);
5752
5787
  notifyValue.current = onValueChange;
5753
- (0, import_react45.useLayoutEffect)(() => {
5788
+ (0, import_react46.useLayoutEffect)(() => {
5754
5789
  clearTimeout(timer.current);
5755
5790
  setQuery(value);
5756
5791
  }, [value]);
5757
- (0, import_react45.useLayoutEffect)(() => {
5792
+ (0, import_react46.useLayoutEffect)(() => {
5758
5793
  clearTimeout(timer.current);
5759
5794
  return () => clearTimeout(timer.current);
5760
5795
  }, [disabled, debounce, !!loadOptions]);
5761
- (0, import_react45.useEffect)(() => {
5796
+ (0, import_react46.useEffect)(() => {
5762
5797
  const current = ++version.current, abort = new AbortController();
5763
5798
  setFailed(false);
5764
5799
  if (!loadOptions || !open || disabled || query.length < minChars) {
@@ -5849,7 +5884,7 @@ function KjunSearchInput({
5849
5884
  placeholder != null ? placeholder : placeholder = m.search;
5850
5885
  emptyText != null ? emptyText : emptyText = m.noResults;
5851
5886
  errorText != null ? errorText : errorText = m.searchFailed;
5852
- const [open, setOpen] = (0, import_react46.useState)(false);
5887
+ const [open, setOpen] = (0, import_react47.useState)(false);
5853
5888
  const model = useSearchInputModel({ value, disabled, debounce, loadOptions, minChars, onValueChange, onSearchError }, open);
5854
5889
  const { query, options, loading, failed } = model;
5855
5890
  const update = (value2) => {
@@ -5911,30 +5946,30 @@ function KjunSearchInput({
5911
5946
 
5912
5947
  // packages/native/src/select-control.tsx
5913
5948
  var import_tokens35 = require("@kjun-ui/tokens");
5914
- var import_react49 = require("react");
5949
+ var import_react50 = require("react");
5915
5950
 
5916
5951
  // packages/native/src/compound-control.tsx
5917
- var import_react47 = require("react");
5952
+ var import_react48 = require("react");
5918
5953
  var import_react_native36 = require("react-native");
5919
5954
  var import_tokens34 = require("@kjun-ui/tokens");
5920
5955
  var import_jsx_runtime33 = require("react/jsx-runtime");
5921
- var CompoundControlContext = (0, import_react47.createContext)(null);
5956
+ var CompoundControlContext = (0, import_react48.createContext)(null);
5922
5957
  function CompoundTarget({ children, square = false }) {
5923
5958
  const touch = finePointer() ? void 0 : import_tokens34.tokens.native.minimumTouchTarget;
5924
5959
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_react_native36.View, { style: { minHeight: touch, minWidth: square ? touch : void 0, alignItems: "center", justifyContent: "center" }, children });
5925
5960
  }
5926
5961
 
5927
5962
  // shared/package-runtime/select.ts
5928
- var import_react48 = require("react");
5963
+ var import_react49 = require("react");
5929
5964
  function useSelectState(controlled, disabled, pageSize, onOpenChange) {
5930
- const [internal, setInternal] = (0, import_react48.useState)(false);
5931
- const [query, setQuery] = (0, import_react48.useState)("");
5932
- const [limit, setLimit] = (0, import_react48.useState)(pageSize);
5933
- const [session, setSession] = (0, import_react48.useState)(0);
5965
+ const [internal, setInternal] = (0, import_react49.useState)(false);
5966
+ const [query, setQuery] = (0, import_react49.useState)("");
5967
+ const [limit, setLimit] = (0, import_react49.useState)(pageSize);
5968
+ const [session, setSession] = (0, import_react49.useState)(0);
5934
5969
  const isControlled = typeof controlled === "boolean";
5935
5970
  const open = !disabled && (isControlled ? controlled : internal);
5936
- const previousOpen = (0, import_react48.useRef)(open);
5937
- (0, import_react48.useEffect)(() => {
5971
+ const previousOpen = (0, import_react49.useRef)(open);
5972
+ (0, import_react49.useEffect)(() => {
5938
5973
  if (disabled && !isControlled) setInternal(false);
5939
5974
  if (previousOpen.current && !open) {
5940
5975
  setQuery("");
@@ -5943,7 +5978,7 @@ function useSelectState(controlled, disabled, pageSize, onOpenChange) {
5943
5978
  }
5944
5979
  previousOpen.current = open;
5945
5980
  }, [open, disabled, isControlled, pageSize]);
5946
- (0, import_react48.useEffect)(() => {
5981
+ (0, import_react49.useEffect)(() => {
5947
5982
  setLimit(pageSize);
5948
5983
  }, [pageSize]);
5949
5984
  const changeOpen = (next) => {
@@ -5993,14 +6028,14 @@ function KjunSelect({
5993
6028
  placeholder != null ? placeholder : placeholder = m.select;
5994
6029
  searchPlaceholder != null ? searchPlaceholder : searchPlaceholder = m.search;
5995
6030
  const { open, query, limit, changeOpen, search, showMore } = useSelectState(controlled, disabled, optionPageSize, onOpenChange);
5996
- const { colors } = useKjunStyles(), field = useKjunField(), ref = (0, import_react49.useRef)(null), spec = import_tokens35.tokens.input[size];
5997
- const trigger = (0, import_react49.useRef)(null);
5998
- const compound = (0, import_react49.useContext)(CompoundControlContext);
5999
- (0, import_react49.useEffect)(() => {
6031
+ const { colors } = useKjunStyles(), field = useKjunField(), ref = (0, import_react50.useRef)(null), spec = import_tokens35.tokens.input[size];
6032
+ const trigger = (0, import_react50.useRef)(null);
6033
+ const compound = (0, import_react50.useContext)(CompoundControlContext);
6034
+ (0, import_react50.useEffect)(() => {
6000
6035
  if (compound == null ? void 0 : compound.focusRef) compound.focusRef.current = trigger.current;
6001
6036
  }, [compound == null ? void 0 : compound.focusRef]);
6002
6037
  const expanded = usePopupExpanded(open, ref);
6003
- const selectedRow = (0, import_react49.useRef)(null);
6038
+ const selectedRow = (0, import_react50.useRef)(null);
6004
6039
  const surface = useFieldSurface(size, { disabled, invalid: error || !!(field == null ? void 0 : field.error), open: expanded });
6005
6040
  const change = (v) => {
6006
6041
  onValueChange == null ? void 0 : onValueChange(v);
@@ -6198,16 +6233,17 @@ var import_tokens38 = require("@kjun-ui/tokens");
6198
6233
 
6199
6234
  // packages/native/src/collapse.tsx
6200
6235
  var import_tokens36 = require("@kjun-ui/tokens");
6201
- var import_react50 = require("react");
6236
+ var import_react51 = require("react");
6202
6237
  var import_react_native38 = require("react-native");
6203
6238
  var import_jsx_runtime35 = require("react/jsx-runtime");
6204
- function Collapse({ open, children }) {
6239
+ function Collapse({ open, keepMounted = false, children }) {
6205
6240
  const reduced = useReducedMotion();
6206
- const [retained, setRetained] = (0, import_react50.useState)(open), [height, setHeight] = (0, import_react50.useState)(null);
6207
- const value = (0, import_react50.useRef)(new import_react_native38.Animated.Value(0)).current;
6208
- const [settled, setSettled] = (0, import_react50.useState)(open);
6209
- const version = (0, import_react50.useRef)(0), first = (0, import_react50.useRef)(open);
6210
- (0, import_react50.useLayoutEffect)(() => {
6241
+ const [retained, setRetained] = (0, import_react51.useState)(open), [height, setHeight] = (0, import_react51.useState)(null);
6242
+ const value = (0, import_react51.useRef)(new import_react_native38.Animated.Value(0)).current;
6243
+ const [settled, setSettled] = (0, import_react51.useState)(open);
6244
+ const version = (0, import_react51.useRef)(0), first = (0, import_react51.useRef)(open), opened = (0, import_react51.useRef)(open);
6245
+ if (open) opened.current = true;
6246
+ (0, import_react51.useLayoutEffect)(() => {
6211
6247
  const revision = ++version.current;
6212
6248
  if (open) setRetained(true);
6213
6249
  if (height === null) return;
@@ -6234,13 +6270,13 @@ function Collapse({ open, children }) {
6234
6270
  value.stopAnimation();
6235
6271
  };
6236
6272
  }, [open, height, reduced, value]);
6237
- return open || retained ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
6273
+ return open || retained || keepMounted && opened.current ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
6238
6274
  import_react_native38.Animated.View,
6239
6275
  {
6240
6276
  pointerEvents: open ? "auto" : "none",
6241
6277
  accessibilityElementsHidden: !open,
6242
6278
  importantForAccessibility: open ? "auto" : "no-hide-descendants",
6243
- ...!open ? { inert: true } : {},
6279
+ ...!open ? { inert: true, "aria-hidden": true } : {},
6244
6280
  style: { height: settled && open ? void 0 : value, overflow: "hidden" },
6245
6281
  children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_react_native38.View, { onLayout: (event) => setHeight(event.nativeEvent.layout.height), children })
6246
6282
  }
@@ -6248,10 +6284,10 @@ function Collapse({ open, children }) {
6248
6284
  }
6249
6285
 
6250
6286
  // shared/package-runtime/navigation.ts
6251
- var import_react51 = require("react");
6287
+ var import_react52 = require("react");
6252
6288
  function useInitialTab(value, candidate, change) {
6253
- const requested = (0, import_react51.useRef)(/* @__PURE__ */ new Set());
6254
- (0, import_react51.useEffect)(() => {
6289
+ const requested = (0, import_react52.useRef)(/* @__PURE__ */ new Set());
6290
+ (0, import_react52.useEffect)(() => {
6255
6291
  if (value !== "") {
6256
6292
  requested.current.clear();
6257
6293
  return;
@@ -6262,8 +6298,8 @@ function useInitialTab(value, candidate, change) {
6262
6298
  }, [value, candidate, change]);
6263
6299
  }
6264
6300
  function useAccordionState(multiple) {
6265
- const [opened, setOpened] = (0, import_react51.useState)(/* @__PURE__ */ new Set());
6266
- (0, import_react51.useEffect)(() => {
6301
+ const [opened, setOpened] = (0, import_react52.useState)(/* @__PURE__ */ new Set());
6302
+ (0, import_react52.useEffect)(() => {
6267
6303
  if (!multiple) setOpened((previous) => previous.size > 1 ? new Set([...previous].slice(-1)) : previous);
6268
6304
  }, [multiple]);
6269
6305
  return {
@@ -6287,16 +6323,16 @@ function useAccordionState(multiple) {
6287
6323
  }
6288
6324
 
6289
6325
  // packages/native/src/navigation.tsx
6290
- var import_react54 = require("react");
6326
+ var import_react55 = require("react");
6291
6327
  var import_react_native40 = require("react-native");
6292
6328
 
6293
6329
  // packages/native/src/tabs.tsx
6294
6330
  var import_tokens37 = require("@kjun-ui/tokens");
6295
- var import_react53 = require("react");
6331
+ var import_react54 = require("react");
6296
6332
  var import_react_native39 = require("react-native");
6297
6333
 
6298
6334
  // packages/native/src/tabs-accessibility.web.ts
6299
- var import_react52 = require("react");
6335
+ var import_react53 = require("react");
6300
6336
 
6301
6337
  // shared/package-runtime/tab-keyboard.ts
6302
6338
  function manageTabList(list, select, keyboard = true) {
@@ -6379,16 +6415,16 @@ function panelNeedsFocus(panel) {
6379
6415
 
6380
6416
  // packages/native/src/tabs-accessibility.web.ts
6381
6417
  function useTabKeyboard(root, select) {
6382
- const callback = (0, import_react52.useRef)(select);
6418
+ const callback = (0, import_react53.useRef)(select);
6383
6419
  callback.current = select;
6384
- (0, import_react52.useLayoutEffect)(() => {
6420
+ (0, import_react53.useLayoutEffect)(() => {
6385
6421
  var _a;
6386
6422
  const list = (_a = root.current) == null ? void 0 : _a.querySelector('[role="tablist"]');
6387
6423
  if (list) return manageTabList(list, (name) => callback.current(name));
6388
6424
  }, [root]);
6389
6425
  }
6390
6426
  function usePanelFocus(root) {
6391
- (0, import_react52.useLayoutEffect)(() => {
6427
+ (0, import_react53.useLayoutEffect)(() => {
6392
6428
  const panel = root.current;
6393
6429
  if (panel) panel.tabIndex = panelNeedsFocus(panel) ? 0 : -1;
6394
6430
  });
@@ -6396,7 +6432,7 @@ function usePanelFocus(root) {
6396
6432
 
6397
6433
  // packages/native/src/tabs.tsx
6398
6434
  var import_jsx_runtime36 = require("react/jsx-runtime");
6399
- var TabContext = (0, import_react53.createContext)({ value: "", id: "", names: [] });
6435
+ var TabContext = (0, import_react54.createContext)({ value: "", id: "", names: [] });
6400
6436
  function KjunTabs({
6401
6437
  value,
6402
6438
  items: items2 = [],
@@ -6412,21 +6448,21 @@ function KjunTabs({
6412
6448
  var _a, _b;
6413
6449
  const m = useKjunMessages();
6414
6450
  ariaLabel != null ? ariaLabel : ariaLabel = m.tabs;
6415
- const { colors } = useKjunStyles(), panes = import_react53.Children.toArray(children).filter(
6416
- import_react53.isValidElement
6451
+ const { colors } = useKjunStyles(), panes = import_react54.Children.toArray(children).filter(
6452
+ import_react54.isValidElement
6417
6453
  );
6418
6454
  const tabs = items2.length ? items2 : panes.filter((child) => child.type === KjunTabPane).map((child) => child.props);
6419
- const id = "kjun-" + (0, import_react53.useId)(), root = (0, import_react53.useRef)(null);
6420
- const menuState = (0, import_react53.useRef)({ tabs, onTabMenu });
6455
+ const id = "kjun-" + (0, import_react54.useId)(), root = (0, import_react54.useRef)(null);
6456
+ const menuState = (0, import_react54.useRef)({ tabs, onTabMenu });
6421
6457
  menuState.current = { tabs, onTabMenu };
6422
- const pressedMenu = (0, import_react53.useRef)(null);
6423
- (0, import_react53.useLayoutEffect)(() => {
6458
+ const pressedMenu = (0, import_react54.useRef)(null);
6459
+ (0, import_react54.useLayoutEffect)(() => {
6424
6460
  if (!onTabMenu || !tabs.some((tab) => tab.name === pressedMenu.current && !tab.disabled)) pressedMenu.current = null;
6425
6461
  }, [tabs, onTabMenu]);
6426
6462
  const panelNames = panes.filter((child) => child.type === KjunTabPane).map((child) => child.props.name);
6427
6463
  const geometry2 = import_tokens37.tokens.extensions.tabs;
6428
6464
  const underline = variant === "underline";
6429
- const [hoveredTab, setHoveredTab] = (0, import_react53.useState)(null);
6465
+ const [hoveredTab, setHoveredTab] = (0, import_react54.useState)(null);
6430
6466
  const singleLine = import_react_native39.Platform.OS === "web" ? { whiteSpace: "nowrap" } : {};
6431
6467
  const paddingX = !underline ? geometry2.pillsPaddingX : density === "compact" ? geometry2.compactPaddingX : geometry2.paddingX;
6432
6468
  const height = density === "compact" ? geometry2.compactHeight : geometry2.height;
@@ -6435,10 +6471,10 @@ function KjunTabs({
6435
6471
  const roomX = underline ? ringRoom : 0, roomY = Math.max(underline ? ringRoom : 0, slop);
6436
6472
  const selectedTab = tabs.find((tab) => tab.name === value);
6437
6473
  const selection = useButtonGroupMotion(selectedTab ? { ...selectedTab, value: selectedTab.name } : void 0);
6438
- const tabRefs = (0, import_react53.useRef)(/* @__PURE__ */ new Map());
6474
+ const tabRefs = (0, import_react54.useRef)(/* @__PURE__ */ new Map());
6439
6475
  const { updateLayout } = selection;
6440
6476
  const firstName = (_a = tabs[0]) == null ? void 0 : _a.name;
6441
- const measureIndicator = (0, import_react53.useCallback)((name, box) => {
6477
+ const measureIndicator = (0, import_react54.useCallback)((name, box) => {
6442
6478
  const left = underline && name === firstName ? 0 : paddingX;
6443
6479
  updateLayout(name, underline ? {
6444
6480
  x: box.x + left,
@@ -6447,7 +6483,7 @@ function KjunTabs({
6447
6483
  height: geometry2.indicatorHeight
6448
6484
  } : box);
6449
6485
  }, [underline, paddingX, firstName, updateLayout, geometry2]);
6450
- (0, import_react53.useLayoutEffect)(() => {
6486
+ (0, import_react54.useLayoutEffect)(() => {
6451
6487
  for (const [name, node] of tabRefs.current) {
6452
6488
  node.measure((x, y, width, height2) => measureIndicator(name, { x, y, width, height: height2 }));
6453
6489
  }
@@ -6606,8 +6642,8 @@ function KjunTabs({
6606
6642
  ] }) });
6607
6643
  }
6608
6644
  function KjunTabPane({ name, children }) {
6609
- const { value, id, names } = (0, import_react53.useContext)(TabContext);
6610
- const root = (0, import_react53.useRef)(null);
6645
+ const { value, id, names } = (0, import_react54.useContext)(TabContext);
6646
+ const root = (0, import_react54.useRef)(null);
6611
6647
  usePanelFocus(root);
6612
6648
  const hidden = value !== name;
6613
6649
  return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
@@ -6630,7 +6666,7 @@ function KjunTabPane({ name, children }) {
6630
6666
 
6631
6667
  // packages/native/src/navigation.tsx
6632
6668
  var import_jsx_runtime37 = require("react/jsx-runtime");
6633
- var AccordionContext = (0, import_react54.createContext)(null);
6669
+ var AccordionContext = (0, import_react55.createContext)(null);
6634
6670
  function KjunAccordion({
6635
6671
  multiple = false,
6636
6672
  tone = "card",
@@ -6660,14 +6696,32 @@ function KjunAccordionItem({
6660
6696
  defaultOpen = false,
6661
6697
  disabled = false,
6662
6698
  header,
6699
+ open: openProp,
6700
+ onOpenChange,
6701
+ keepMounted = false,
6663
6702
  children
6664
6703
  }) {
6665
- const { colors } = useKjunStyles(), group = (0, import_react54.useContext)(AccordionContext), id = "kjun-" + (0, import_react54.useId)(), [own, setOwn] = (0, import_react54.useState)(defaultOpen), open = group ? group.opened.has(id) : own;
6704
+ const controlled = openProp !== void 0, context = (0, import_react55.useContext)(AccordionContext), { colors } = useKjunStyles(), group = controlled ? null : context, id = "kjun-" + (0, import_react55.useId)(), [own, setOwn] = (0, import_react55.useState)(defaultOpen), open = controlled ? openProp : group ? group.opened.has(id) : own;
6705
+ const reported = (0, import_react55.useRef)(open), notify = (0, import_react55.useRef)(onOpenChange), registering = (0, import_react55.useRef)(!!group && defaultOpen);
6706
+ notify.current = onOpenChange;
6707
+ (0, import_react55.useEffect)(() => {
6708
+ var _a;
6709
+ if (registering.current) {
6710
+ if (open) {
6711
+ registering.current = false;
6712
+ reported.current = open;
6713
+ }
6714
+ return;
6715
+ }
6716
+ if (controlled || reported.current === open) return;
6717
+ reported.current = open;
6718
+ (_a = notify.current) == null ? void 0 : _a.call(notify, open);
6719
+ }, [open, controlled]);
6666
6720
  const rotation = useMotionValue(open ? 1 : 0, import_tokens38.tokens.motion.collapse, void 0, void 0, true);
6667
6721
  const geometry2 = import_tokens38.tokens.extensions.accordion;
6668
- const [hovered, setHovered] = (0, import_react54.useState)(false);
6722
+ const [hovered, setHovered] = (0, import_react55.useState)(false);
6669
6723
  const wrapping = import_react_native40.Platform.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : {};
6670
- (0, import_react54.useEffect)(() => {
6724
+ (0, import_react55.useEffect)(() => {
6671
6725
  return group == null ? void 0 : group.register(id, defaultOpen);
6672
6726
  }, []);
6673
6727
  return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_react_native40.View, { children: [
@@ -6681,7 +6735,10 @@ function KjunAccordionItem({
6681
6735
  accessibilityLabel: title,
6682
6736
  accessibilityState: { expanded: open, disabled },
6683
6737
  disabled,
6684
- onPress: () => group ? group.toggle(id) : setOwn(!own),
6738
+ onPress: () => {
6739
+ registering.current = false;
6740
+ controlled ? onOpenChange == null ? void 0 : onOpenChange(!open) : group ? group.toggle(id) : setOwn(!own);
6741
+ },
6685
6742
  onHoverIn: () => setHovered(true),
6686
6743
  onHoverOut: () => setHovered(false),
6687
6744
  style: ({ pressed }) => ({
@@ -6701,7 +6758,7 @@ function KjunAccordionItem({
6701
6758
  ]
6702
6759
  }
6703
6760
  ),
6704
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Collapse, { open, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
6761
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Collapse, { open, keepMounted, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
6705
6762
  import_react_native40.View,
6706
6763
  {
6707
6764
  nativeID: id + "-panel",
@@ -6744,7 +6801,7 @@ function KjunBreadcrumb({
6744
6801
  }
6745
6802
  )
6746
6803
  ] });
6747
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_react54.Fragment, { children: [
6804
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_react55.Fragment, { children: [
6748
6805
  i > 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
6749
6806
  KjunIcon,
6750
6807
  {
@@ -6898,7 +6955,7 @@ function KjunPagination({
6898
6955
 
6899
6956
  // packages/native/src/actions.tsx
6900
6957
  var import_tokens39 = require("@kjun-ui/tokens");
6901
- var import_react55 = require("react");
6958
+ var import_react56 = require("react");
6902
6959
  var import_react_native41 = require("react-native");
6903
6960
  var import_jsx_runtime38 = require("react/jsx-runtime");
6904
6961
  function KjunIconToggle({
@@ -6917,7 +6974,7 @@ function KjunIconToggle({
6917
6974
  onToggle
6918
6975
  }) {
6919
6976
  const { colors } = useKjunStyles();
6920
- const navigation = (0, import_react55.useContext)(TopNavigationContext);
6977
+ const navigation = (0, import_react56.useContext)(TopNavigationContext);
6921
6978
  const box = navigation ? navigation.controlSize : size === "xs" ? import_tokens39.tokens.extensions.iconToggle.compactSize : import_tokens39.tokens.extensions.iconToggle.sizes[size];
6922
6979
  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)(
6923
6980
  AccessiblePressable,
@@ -6983,8 +7040,8 @@ function KjunCopyButton({
6983
7040
  }) {
6984
7041
  const m = useKjunMessages();
6985
7042
  successText != null ? successText : successText = m.copied;
6986
- const feedback = useOptionalKjunFeedback(), { colors } = useKjunStyles(), [copied, setCopied] = (0, import_react55.useState)(false), [failed, setFailed] = (0, import_react55.useState)(false), busy = (0, import_react55.useRef)(false), timer = (0, import_react55.useRef)(void 0), mounted = (0, import_react55.useRef)(true);
6987
- (0, import_react55.useEffect)(() => {
7043
+ const feedback = useOptionalKjunFeedback(), { colors } = useKjunStyles(), [copied, setCopied] = (0, import_react56.useState)(false), [failed, setFailed] = (0, import_react56.useState)(false), busy = (0, import_react56.useRef)(false), timer = (0, import_react56.useRef)(void 0), mounted = (0, import_react56.useRef)(true);
7044
+ (0, import_react56.useEffect)(() => {
6988
7045
  mounted.current = true;
6989
7046
  return () => {
6990
7047
  mounted.current = false;
@@ -7139,14 +7196,14 @@ function KjunScrollFade({ children }) {
7139
7196
 
7140
7197
  // packages/native/src/data-state.tsx
7141
7198
  var import_tokens40 = require("@kjun-ui/tokens");
7142
- var import_react57 = require("react");
7199
+ var import_react58 = require("react");
7143
7200
  var import_react_native42 = require("react-native");
7144
7201
 
7145
7202
  // shared/package-runtime/use-loading-reveal.ts
7146
- var import_react56 = require("react");
7203
+ var import_react57 = require("react");
7147
7204
  function useLoadingReveal(active, delay = 0) {
7148
- const [revealed, setRevealed] = (0, import_react56.useState)(false);
7149
- (0, import_react56.useEffect)(() => {
7205
+ const [revealed, setRevealed] = (0, import_react57.useState)(false);
7206
+ (0, import_react57.useEffect)(() => {
7150
7207
  setRevealed(false);
7151
7208
  if (!active || delay <= 0) return;
7152
7209
  const timer = setTimeout(() => setRevealed(true), delay);
@@ -7159,7 +7216,7 @@ function useLoadingReveal(active, delay = 0) {
7159
7216
  var import_jsx_runtime39 = require("react/jsx-runtime");
7160
7217
  function useQueryDisplay(props) {
7161
7218
  var _a;
7162
- const [state, setState] = (0, import_react57.useState)(() => (0, import_tokens40.initialQueryDisplay)(props));
7219
+ const [state, setState] = (0, import_react58.useState)(() => (0, import_tokens40.initialQueryDisplay)(props));
7163
7220
  let next = state;
7164
7221
  if (state.queryKey !== ((_a = props.queryKey) != null ? _a : null) || state.resultKey !== props.resultKey || state.loading !== !!props.loading) {
7165
7222
  next = (0, import_tokens40.advanceQueryDisplay)(state, props);
@@ -7268,7 +7325,7 @@ function BoundaryDescription() {
7268
7325
  const { colors } = useKjunStyles();
7269
7326
  return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(KText, { style: { ...typeStyle({ ...import_tokens40.tokens.typography.caption, fontSizePx: import_tokens40.tokens.extensions.state.descriptionSize }), color: colors.textSecondary, textAlign: "center" }, children: m.retryHint });
7270
7327
  }
7271
- var KjunErrorBoundary = class extends import_react57.Component {
7328
+ var KjunErrorBoundary = class extends import_react58.Component {
7272
7329
  constructor() {
7273
7330
  super(...arguments);
7274
7331
  this.state = { error: false };
@@ -7316,7 +7373,7 @@ KjunErrorBoundary.contextType = KjunMessageContext;
7316
7373
 
7317
7374
  // packages/native/src/skeletons.tsx
7318
7375
  var import_tokens44 = require("@kjun-ui/tokens");
7319
- var import_react59 = require("react");
7376
+ var import_react60 = require("react");
7320
7377
  var import_react_native44 = require("react-native");
7321
7378
  var import_react_native_svg5 = __toESM(require("react-native-svg"), 1);
7322
7379
 
@@ -7363,9 +7420,9 @@ function nativeMarketLayout(columns, showActions, containerWidth) {
7363
7420
 
7364
7421
  // packages/native/src/market-layout.ts
7365
7422
  var import_tokens42 = require("@kjun-ui/tokens");
7366
- var import_react58 = require("react");
7423
+ var import_react59 = require("react");
7367
7424
  function useMarketLayout(columns, showActions) {
7368
- const [containerWidth, setContainerWidth] = (0, import_react58.useState)(0);
7425
+ const [containerWidth, setContainerWidth] = (0, import_react59.useState)(0);
7369
7426
  const onLayout = (event) => setContainerWidth(event.nativeEvent.layout.width);
7370
7427
  return { ...nativeMarketLayout(columns, showActions, containerWidth), onLayout };
7371
7428
  }
@@ -7507,8 +7564,8 @@ function KjunChartSkeleton({
7507
7564
  }) {
7508
7565
  const m = useKjunMessages();
7509
7566
  loadingText != null ? loadingText : loadingText = m.chartLoading;
7510
- const kind = chartSkeletonKind(requestedKind), { colors } = useKjunStyles(), reduced = useReducedMotion(), opacity = (0, import_react59.useRef)(new import_react_native44.Animated.Value(1)).current, revealed = useLoadingReveal(true, loadingDelay), [measured, setMeasured] = (0, import_react59.useState)(0), thickness = import_tokens44.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" };
7511
- (0, import_react59.useEffect)(() => {
7567
+ const kind = chartSkeletonKind(requestedKind), { colors } = useKjunStyles(), reduced = useReducedMotion(), opacity = (0, import_react60.useRef)(new import_react_native44.Animated.Value(1)).current, revealed = useLoadingReveal(true, loadingDelay), [measured, setMeasured] = (0, import_react60.useState)(0), thickness = import_tokens44.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" };
7568
+ (0, import_react60.useEffect)(() => {
7512
7569
  if (reduced || !skeleton) return;
7513
7570
  const loop = import_react_native44.Animated.loop(
7514
7571
  import_react_native44.Animated.sequence([
@@ -7589,7 +7646,7 @@ function KjunChartSkeleton({
7589
7646
 
7590
7647
  // packages/native/src/financial-display.tsx
7591
7648
  var import_tokens47 = require("@kjun-ui/tokens");
7592
- var import_react62 = require("react");
7649
+ var import_react63 = require("react");
7593
7650
  var import_react_native46 = require("react-native");
7594
7651
  var import_react_native_svg6 = __toESM(require("react-native-svg"), 1);
7595
7652
 
@@ -7597,7 +7654,7 @@ var import_react_native_svg6 = __toESM(require("react-native-svg"), 1);
7597
7654
  var import_tokens46 = require("@kjun-ui/tokens");
7598
7655
 
7599
7656
  // shared/package-runtime/use-number-motion.ts
7600
- var import_react60 = require("react");
7657
+ var import_react61 = require("react");
7601
7658
 
7602
7659
  // shared/package-runtime/number-motion.ts
7603
7660
  var import_tokens45 = require("@kjun-ui/tokens");
@@ -7643,14 +7700,14 @@ function createNumberMotion(apply) {
7643
7700
 
7644
7701
  // shared/package-runtime/use-number-motion.ts
7645
7702
  function useNumberMotion(value, animated, fromPrevious, reduced) {
7646
- const [display, setDisplay] = (0, import_react60.useState)(typeof value === "string" || fromPrevious || !animated || reduced ? value : 0);
7647
- const motion = (0, import_react60.useRef)(null);
7703
+ const [display, setDisplay] = (0, import_react61.useState)(typeof value === "string" || fromPrevious || !animated || reduced ? value : 0);
7704
+ const motion = (0, import_react61.useRef)(null);
7648
7705
  if (!motion.current) motion.current = createNumberMotion(setDisplay);
7649
- (0, import_react60.useLayoutEffect)(() => {
7706
+ (0, import_react61.useLayoutEffect)(() => {
7650
7707
  if (!motion.current) motion.current = createNumberMotion(setDisplay);
7651
7708
  motion.current.update(value, animated, fromPrevious, reduced);
7652
7709
  }, [value, animated, fromPrevious, reduced]);
7653
- (0, import_react60.useLayoutEffect)(() => () => {
7710
+ (0, import_react61.useLayoutEffect)(() => () => {
7654
7711
  var _a;
7655
7712
  (_a = motion.current) == null ? void 0 : _a.cancel();
7656
7713
  motion.current = null;
@@ -7659,7 +7716,7 @@ function useNumberMotion(value, animated, fromPrevious, reduced) {
7659
7716
  }
7660
7717
 
7661
7718
  // packages/native/src/number-display.tsx
7662
- var import_react61 = require("react");
7719
+ var import_react62 = require("react");
7663
7720
  var import_react_native45 = require("react-native");
7664
7721
  var import_jsx_runtime42 = require("react/jsx-runtime");
7665
7722
  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;
@@ -7670,7 +7727,7 @@ var format = (value, decimals = 0) => new Intl.NumberFormat(void 0, {
7670
7727
  function useCountUp(value, animated, fromPrevious) {
7671
7728
  return useNumberMotion(value, animated, fromPrevious, useReducedMotion());
7672
7729
  }
7673
- var MarketQuoteTypeContext = (0, import_react61.createContext)(null);
7730
+ var MarketQuoteTypeContext = (0, import_react62.createContext)(null);
7674
7731
  function KjunAnimatedNumber({
7675
7732
  value,
7676
7733
  decimals = 0,
@@ -7731,7 +7788,7 @@ function KjunPriceCell({
7731
7788
  fetchedAt,
7732
7789
  fromPrevious = true
7733
7790
  }) {
7734
- const { colors } = useKjunStyles(), quoteType = (0, import_react61.useContext)(MarketQuoteTypeContext);
7791
+ const { colors } = useKjunStyles(), quoteType = (0, import_react62.useContext)(MarketQuoteTypeContext);
7735
7792
  return value == null ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(KjunSkeleton, { type: "block", height: import_tokens46.tokens.extensions.financial.priceSkeletonHeight, width: import_tokens46.tokens.extensions.financial.priceSkeletonWidth }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_react_native45.View, { style: { flexDirection: "row", alignItems: "center", gap: import_tokens46.tokens.extensions.financial.affixGap }, children: [
7736
7793
  /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
7737
7794
  import_react_native45.View,
@@ -7765,7 +7822,7 @@ function KjunSignedValue({
7765
7822
  showFreshness = true,
7766
7823
  formatter
7767
7824
  }) {
7768
- const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), quoteType = (0, import_react61.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);
7825
+ const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), quoteType = (0, import_react62.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);
7769
7826
  const display = n === null ? "\u2014" : formatter ? formatter(n) : prefix + (showSign && n > 0 ? "+" : "") + format(kind === "percent" && !isRaw ? n * 100 : n, decimals) + (kind === "percent" ? "%" : "") + suffix;
7770
7827
  return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
7771
7828
  import_react_native45.View,
@@ -8061,7 +8118,7 @@ function KjunSparkline({
8061
8118
  stretch = false,
8062
8119
  ariaLabel
8063
8120
  }) {
8064
- const { colors, domainColors } = useKjunStyles(), id = "kjun-" + (0, import_react62.useId)().replace(/:/g, ""), values = data.filter(Number.isFinite), min = Math.min(...values), max = Math.max(...values), range = max - min || 1, points = values.map(
8121
+ const { colors, domainColors } = useKjunStyles(), id = "kjun-" + (0, import_react63.useId)().replace(/:/g, ""), values = data.filter(Number.isFinite), min = Math.min(...values), max = Math.max(...values), range = max - min || 1, points = values.map(
8065
8122
  (v, i) => `${2 + i / Math.max(1, values.length - 1) * (width - 4)},${2 + height - 4 - (v - min) / range * (height - 4)}`
8066
8123
  ).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"));
8067
8124
  return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
@@ -8107,7 +8164,7 @@ function KjunSparkline({
8107
8164
 
8108
8165
  // shared/package-runtime/table.ts
8109
8166
  var import_tokens48 = require("@kjun-ui/tokens");
8110
- var import_react63 = require("react");
8167
+ var import_react64 = require("react");
8111
8168
 
8112
8169
  // shared/package-runtime/table-model.ts
8113
8170
  function getValue(row, key) {
@@ -8151,11 +8208,11 @@ function useTableModel(props) {
8151
8208
  expandSingle = false,
8152
8209
  onExpandedRowsChange
8153
8210
  } = props;
8154
- const [internalSort, setSort] = (0, import_react63.useState)({
8211
+ const [internalSort, setSort] = (0, import_react64.useState)({
8155
8212
  key: "",
8156
8213
  order: "asc"
8157
- }), [internalExpanded, setExpanded] = (0, import_react63.useState)([]), [search, setSearch] = (0, import_react63.useState)(""), timer = (0, import_react63.useRef)(void 0);
8158
- (0, import_react63.useEffect)(() => () => clearTimeout(timer.current), []);
8214
+ }), [internalExpanded, setExpanded] = (0, import_react64.useState)([]), [search, setSearch] = (0, import_react64.useState)(""), timer = (0, import_react64.useRef)(void 0);
8215
+ (0, import_react64.useEffect)(() => () => clearTimeout(timer.current), []);
8159
8216
  const sort = props.sort === void 0 ? internalSort : (_a = props.sort) != null ? _a : { key: "", order: "asc" };
8160
8217
  const rows = sortTableRows(data, sort, { sortable, sortMode });
8161
8218
  const key = (row, index = 0) => tableRowKey(row, rowKey, index);
@@ -8316,7 +8373,7 @@ function TableToolbar({
8316
8373
 
8317
8374
  // packages/native/src/table-grid.tsx
8318
8375
  var import_tokens50 = require("@kjun-ui/tokens");
8319
- var import_react64 = require("react");
8376
+ var import_react65 = require("react");
8320
8377
  var import_react_native_svg7 = __toESM(require("react-native-svg"), 1);
8321
8378
  var import_react_native48 = require("react-native");
8322
8379
  var import_jsx_runtime45 = require("react/jsx-runtime");
@@ -8324,7 +8381,7 @@ function TableGrid({ initialLoading, skeletonRows, skeletonHidden, state, onRowC
8324
8381
  const m = useKjunMessages();
8325
8382
  const { colors } = useKjunStyles();
8326
8383
  const geometry2 = import_tokens50.tokens.table;
8327
- const [scroll, setScroll] = (0, import_react64.useState)({ viewport: 0, x: 0 }), fadeId = "kjun-table-fade-" + (0, import_react64.useId)().replace(/[^a-z0-9]/gi, "");
8384
+ const [scroll, setScroll] = (0, import_react65.useState)({ viewport: 0, x: 0 }), fadeId = "kjun-table-fade-" + (0, import_react65.useId)().replace(/[^a-z0-9]/gi, "");
8328
8385
  const cellPadding = geometry2.cellPadding[compact ? "compact" : "regular"];
8329
8386
  const width = (col) => typeof col.width === "number" ? col.width : typeof col.width === "string" && /^\d+(px)?$/.test(col.width) ? parseFloat(col.width) : geometry2.columnWidth;
8330
8387
  const totalWidth = visible.reduce((sum, col) => sum + width(col), 0) + (selectable ? geometry2.actionColumnWidth : 0) + (expandable ? geometry2.actionColumnWidth : 0);
@@ -8438,7 +8495,7 @@ function TableGrid({ initialLoading, skeletonRows, skeletonHidden, state, onRowC
8438
8495
  ))
8439
8496
  ] }, 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, {
8440
8497
  color: colors.textSecondary
8441
- }) }) : state.rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_react64.Fragment, { children: [
8498
+ }) }) : state.rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_react65.Fragment, { children: [
8442
8499
  /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
8443
8500
  AccessiblePressable,
8444
8501
  {
@@ -9067,7 +9124,7 @@ function KjunKpiHero(props) {
9067
9124
 
9068
9125
  // packages/native/src/kpi-row.tsx
9069
9126
  var import_tokens54 = require("@kjun-ui/tokens");
9070
- var import_react65 = require("react");
9127
+ var import_react66 = require("react");
9071
9128
  var import_react_native53 = require("react-native");
9072
9129
  var import_jsx_runtime50 = require("react/jsx-runtime");
9073
9130
  function KjunKpiRow({
@@ -9082,7 +9139,7 @@ function KjunKpiRow({
9082
9139
  const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), mobile = (0, import_react_native53.useWindowDimensions)().width <= import_tokens54.tokens.responsive.kpiRow, small = size === "sm", summary = mobileSummary && mobile && !small;
9083
9140
  const geometry2 = import_tokens54.tokens.extensions.kpiRow;
9084
9141
  const spec = small ? geometry2.sm : mobile ? geometry2.mobile : geometry2.md;
9085
- const [width, setWidth] = (0, import_react65.useState)(0);
9142
+ const [width, setWidth] = (0, import_react66.useState)(0);
9086
9143
  const paddingX = summary ? geometry2.summary.paddingX : spec.paddingX;
9087
9144
  const gapX = summary ? geometry2.summary.gapX : 0;
9088
9145
  const explicit = items2.some(
@@ -9301,7 +9358,7 @@ var import_tokens56 = require("@kjun-ui/tokens");
9301
9358
  var import_react_native55 = require("react-native");
9302
9359
 
9303
9360
  // shared/package-runtime/market-cards.ts
9304
- var import_react66 = require("react");
9361
+ var import_react67 = require("react");
9305
9362
  function useMarketCardsModel(props, persistence) {
9306
9363
  const {
9307
9364
  columns,
@@ -9317,8 +9374,8 @@ function useMarketCardsModel(props, persistence) {
9317
9374
  sortKey: c.sortable ? metricConfig[c.key].sortKey : null,
9318
9375
  isDefaultSort: c.defaultSort
9319
9376
  }));
9320
- const [key, setKey] = (0, import_react66.useState)(""), restoreSignature = JSON.stringify([storageNamespace, pills]);
9321
- const mounted = (0, import_react66.useRef)("");
9377
+ const [key, setKey] = (0, import_react67.useState)(""), restoreSignature = JSON.stringify([storageNamespace, pills]);
9378
+ const mounted = (0, import_react67.useRef)("");
9322
9379
  const save = (key2) => {
9323
9380
  var _a;
9324
9381
  try {
@@ -9326,7 +9383,7 @@ function useMarketCardsModel(props, persistence) {
9326
9383
  } catch (e) {
9327
9384
  }
9328
9385
  };
9329
- (0, import_react66.useEffect)(() => {
9386
+ (0, import_react67.useEffect)(() => {
9330
9387
  var _a, _b;
9331
9388
  if (mounted.current === restoreSignature) return;
9332
9389
  mounted.current = restoreSignature;
@@ -9805,7 +9862,7 @@ function KjunMarketListPanel({
9805
9862
 
9806
9863
  // packages/native/src/market-table.tsx
9807
9864
  var import_tokens59 = require("@kjun-ui/tokens");
9808
- var import_react67 = require("react");
9865
+ var import_react68 = require("react");
9809
9866
  var import_react_native58 = require("react-native");
9810
9867
  var import_react_native_svg8 = __toESM(require("react-native-svg"), 1);
9811
9868
  var import_jsx_runtime55 = require("react/jsx-runtime");
@@ -9892,7 +9949,7 @@ function KjunMarketTable(props) {
9892
9949
  );
9893
9950
  };
9894
9951
  const layout = useMarketLayout(columns, showActions);
9895
- const [scroll, setScroll] = (0, import_react67.useState)({ viewport: 0, x: 0 }), fadeId = "kjun-market-fade-" + (0, import_react67.useId)().replace(/[^a-z0-9]/gi, "");
9952
+ const [scroll, setScroll] = (0, import_react68.useState)({ viewport: 0, x: 0 }), fadeId = "kjun-market-fade-" + (0, import_react68.useId)().replace(/[^a-z0-9]/gi, "");
9896
9953
  const tableQuote = { price: {}, change: { fontWeight: typeStyle("body").fontWeight } };
9897
9954
  return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(
9898
9955
  import_react_native58.View,
@@ -10107,30 +10164,35 @@ function KjunMarketTable(props) {
10107
10164
 
10108
10165
  // packages/native/src/list.tsx
10109
10166
  var import_tokens60 = require("@kjun-ui/tokens");
10110
- var import_react68 = require("react");
10167
+ var import_react69 = require("react");
10111
10168
  var import_react_native59 = require("react-native");
10112
10169
  var import_jsx_runtime56 = require("react/jsx-runtime");
10113
- var ListRowContext = (0, import_react68.createContext)({ divider: true, compact: false });
10114
- function KjunListRow({ title, description, leading, trailing, actions, href, disabled = false, destructive = false, chevron = false, onPress }) {
10115
- const { colors } = useKjunStyles(), { divider, compact } = (0, import_react68.useContext)(ListRowContext);
10170
+ var ListRowContext = (0, import_react69.createContext)({ divider: true, compact: false });
10171
+ function KjunListRow({ title, description, leading, trailing, actions, href, disabled = false, destructive = false, chevron = false, onPress, ariaLabel, children }) {
10172
+ const { colors } = useKjunStyles(), { divider, compact } = (0, import_react69.useContext)(ListRowContext);
10116
10173
  const parts = /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_jsx_runtime56.Fragment, { children: [
10117
10174
  hasContent(leading) && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native59.View, { children: content(leading) }),
10118
10175
  /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_react_native59.View, { style: { flex: 1, minWidth: 0 }, children: [
10119
10176
  /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(KText, { style: { ...typeStyle("label"), ...destructive ? { color: colors.danger } : {} }, children: title }),
10120
- !!description && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(KText, { style: { marginTop: import_tokens60.tokens.extensions.list.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
10177
+ !!description && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(KText, { style: { marginTop: import_tokens60.tokens.extensions.list.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description }),
10178
+ hasContent(children) && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native59.View, { style: { marginTop: import_tokens60.tokens.extensions.list.descriptionGap }, children: content(children) })
10121
10179
  ] }),
10122
10180
  hasContent(trailing) && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native59.View, { style: { maxWidth: "35%" }, children: content(trailing) }),
10123
10181
  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_tokens60.tokens.iconSizes.default, color: colors.textTertiary }) })
10124
10182
  ] });
10125
10183
  const style = { flex: 1, minWidth: 0, minHeight: compact ? import_tokens60.tokens.extensions.list.compactMinimumHeight : import_tokens60.tokens.extensions.list.minimumHeight, flexDirection: "row", alignItems: "center", gap: import_tokens60.tokens.extensions.list.gap, padding: import_tokens60.tokens.extensions.list.padding, ...compact ? { paddingVertical: import_tokens60.tokens.extensions.list.compactPaddingY } : {} };
10126
10184
  return /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_react_native59.View, { role: "listitem", style: { flexDirection: "row", alignItems: "center", gap: import_tokens60.tokens.extensions.list.gap, borderBottomWidth: divider ? import_tokens60.tokens.border.defaultWidth : 0, borderBottomColor: colors.divider }, children: [
10127
- 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_tokens60.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 }),
10185
+ 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_tokens60.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 }),
10128
10186
  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_tokens60.tokens.extensions.list.actionsGap, paddingRight: import_tokens60.tokens.extensions.list.actionsPadding, opacity: disabled ? import_tokens60.tokens.states.opacity.disabled : 1 }, children: content(actions) })
10129
10187
  ] });
10130
10188
  }
10131
- function KjunListSection({ title, description, actions, children, ariaLabel, surface = true, divider = true, density = "default" }) {
10189
+ function KjunListSection({ title, description, actions, children, ariaLabel, surface = true, divider = true, density = "default", radius = "none" }) {
10132
10190
  const { colors } = useKjunStyles();
10133
- return /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_react_native59.View, { style: { minWidth: 0, backgroundColor: surface ? colors.surface : "transparent" }, children: [
10191
+ return /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_react_native59.View, { style: {
10192
+ minWidth: 0,
10193
+ backgroundColor: surface ? colors.surface : "transparent",
10194
+ ...radius === "none" ? {} : { borderRadius: import_tokens60.tokens.card.radii[radius], overflow: "hidden" }
10195
+ }, children: [
10134
10196
  !!(title || description || actions) && /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_react_native59.View, { style: { flexDirection: "row", alignItems: "center", gap: import_tokens60.tokens.extensions.list.gap, padding: import_tokens60.tokens.extensions.list.padding }, children: [
10135
10197
  /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_react_native59.View, { style: { flex: 1 }, children: [
10136
10198
  !!title && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(KText, { accessibilityRole: "header", style: { ...typeStyle("cardTitle") }, children: title }),
@@ -10138,11 +10200,11 @@ function KjunListSection({ title, description, actions, children, ariaLabel, sur
10138
10200
  ] }),
10139
10201
  content(actions)
10140
10202
  ] }),
10141
- /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native59.View, { role: "list", accessibilityLabel: ariaLabel || title, children: import_react68.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)) })
10203
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native59.View, { role: "list", accessibilityLabel: ariaLabel || title, children: import_react69.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)) })
10142
10204
  ] });
10143
10205
  }
10144
10206
  function KjunListItem({ children }) {
10145
- const { colors } = useKjunStyles(), { divider, compact } = (0, import_react68.useContext)(ListRowContext);
10207
+ const { colors } = useKjunStyles(), { divider, compact } = (0, import_react69.useContext)(ListRowContext);
10146
10208
  return /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native59.View, { role: "listitem", style: {
10147
10209
  padding: import_tokens60.tokens.extensions.list.padding,
10148
10210
  ...compact ? { paddingVertical: import_tokens60.tokens.extensions.list.compactPaddingY } : {},
@@ -10154,7 +10216,7 @@ function KjunListItem({ children }) {
10154
10216
 
10155
10217
  // packages/native/src/app-navigation.tsx
10156
10218
  var import_tokens61 = require("@kjun-ui/tokens");
10157
- var import_react69 = require("react");
10219
+ var import_react70 = require("react");
10158
10220
  var import_react_native60 = require("react-native");
10159
10221
  var import_jsx_runtime57 = require("react/jsx-runtime");
10160
10222
  function KjunTopNavigation({ title, description, leading, actions, safeAreaTop = 0 }) {
@@ -10163,13 +10225,13 @@ function KjunTopNavigation({ title, description, leading, actions, safeAreaTop =
10163
10225
  const titleInset = Math.max(0, (geometry2.controlSize - import_tokens61.tokens.typography.sectionTitle.lineHeightPx) / 2);
10164
10226
  const wrapping = import_react_native60.Platform.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : {};
10165
10227
  const edgeInset = (geometry2.controlSize - geometry2.iconSize) / 2;
10166
- const [iconEdges, setIconEdges] = (0, import_react69.useState)({ leading: false, trailing: false });
10228
+ const [iconEdges, setIconEdges] = (0, import_react70.useState)({ leading: false, trailing: false });
10167
10229
  const square = (key) => (event) => {
10168
10230
  const { width, height } = event.nativeEvent.layout;
10169
10231
  const next = Math.abs(width - height) < 1 && Math.abs(height - geometry2.controlSize) < 1;
10170
10232
  setIconEdges((current) => current[key] === next ? current : { ...current, [key]: next });
10171
10233
  };
10172
- const flatten2 = (node) => import_react69.Children.toArray(node).flatMap((child) => (0, import_react69.isValidElement)(child) && child.type === import_react69.Fragment ? flatten2(child.props.children) : [child]);
10234
+ const flatten2 = (node) => import_react70.Children.toArray(node).flatMap((child) => (0, import_react70.isValidElement)(child) && child.type === import_react70.Fragment ? flatten2(child.props.children) : [child]);
10173
10235
  const actionList = flatten2(actions);
10174
10236
  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: [
10175
10237
  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) }) }),
@@ -10245,7 +10307,7 @@ function KjunBottomNavigation({ items: items2, value, ariaLabel, safeAreaBottom
10245
10307
  }
10246
10308
  function KjunBottomActionBar({ description, children, safeAreaBottom = 0, keyboardVisible = false, hideOnKeyboard = false }) {
10247
10309
  const { colors } = useKjunStyles();
10248
- const [width, setWidth] = (0, import_react69.useState)(0);
10310
+ const [width, setWidth] = (0, import_react70.useState)(0);
10249
10311
  const geometry2 = import_tokens61.tokens.extensions.navigation;
10250
10312
  const wide = width >= import_tokens61.tokens.responsive.bottomAction + geometry2.padding * 2;
10251
10313
  const wrapping = import_react_native60.Platform.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : {};
@@ -10267,17 +10329,17 @@ function KjunBottomActionBar({ description, children, safeAreaBottom = 0, keyboa
10267
10329
  }
10268
10330
 
10269
10331
  // packages/native/src/media.tsx
10270
- var import_react70 = require("react");
10332
+ var import_react71 = require("react");
10271
10333
  var import_react_native61 = require("react-native");
10272
10334
  var import_tokens62 = require("@kjun-ui/tokens");
10273
10335
  var import_jsx_runtime58 = require("react/jsx-runtime");
10274
- var ImageBackgroundContext = (0, import_react70.createContext)(void 0);
10336
+ var ImageBackgroundContext = (0, import_react71.createContext)(void 0);
10275
10337
  function KjunImage({ src, source, alt, decorative = false, aspectRatio = 1, fit = "cover", fallback: fallback2, onLoad, onError }) {
10276
- const { colors } = useKjunStyles(), background = (0, import_react70.useContext)(ImageBackgroundContext);
10277
- const request = JSON.stringify(source != null ? source : src), current = (0, import_react70.useRef)({ request, version: 0 });
10338
+ const { colors } = useKjunStyles(), background = (0, import_react71.useContext)(ImageBackgroundContext);
10339
+ const request = JSON.stringify(source != null ? source : src), current = (0, import_react71.useRef)({ request, version: 0 });
10278
10340
  if (current.current.request !== request) current.current = { request, version: current.current.version + 1 };
10279
10341
  const version = current.current.version;
10280
- const [failed, setFailed] = (0, import_react70.useState)(null);
10342
+ const [failed, setFailed] = (0, import_react71.useState)(null);
10281
10343
  const missing = !source && !src;
10282
10344
  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: () => {
10283
10345
  if (version === current.current.version) onLoad == null ? void 0 : onLoad();
@@ -10342,7 +10404,7 @@ function KjunFab({ icon, ariaLabel, disabled = false, safeAreaBottom = 0, offset
10342
10404
  }
10343
10405
 
10344
10406
  // packages/native/src/range-input.tsx
10345
- var import_react71 = require("react");
10407
+ var import_react72 = require("react");
10346
10408
  var import_react_native63 = require("react-native");
10347
10409
  var import_tokens65 = require("@kjun-ui/tokens");
10348
10410
  var import_jsx_runtime61 = require("react/jsx-runtime");
@@ -10367,18 +10429,18 @@ function RangeInput({
10367
10429
  const valid = domain.valid && (!Array.isArray(value) || value.length === 2) && values.every(domain.accepts) && (values.length === 1 || values[0] <= values[1]);
10368
10430
  const blocked = disabled || !valid;
10369
10431
  const effectiveMax = valid ? domain.snap(max) : min;
10370
- const [hovered, setHovered] = (0, import_react71.useState)(null);
10371
- const [dragging, setDragging] = (0, import_react71.useState)(null);
10372
- const [focused, setFocused] = (0, import_react71.useState)(null);
10373
- const track = (0, import_react71.useRef)(null), measured = (0, import_react71.useRef)({ x: 0, width: 1 }), selected = (0, import_react71.useRef)(0), dirty = (0, import_react71.useRef)(false);
10374
- const latest = (0, import_react71.useRef)(values);
10432
+ const [hovered, setHovered] = (0, import_react72.useState)(null);
10433
+ const [dragging, setDragging] = (0, import_react72.useState)(null);
10434
+ const [focused, setFocused] = (0, import_react72.useState)(null);
10435
+ const track = (0, import_react72.useRef)(null), measured = (0, import_react72.useRef)({ x: 0, width: 1 }), selected = (0, import_react72.useRef)(0), dirty = (0, import_react72.useRef)(false);
10436
+ const latest = (0, import_react72.useRef)(values);
10375
10437
  latest.current = values;
10376
- const callbacks = (0, import_react71.useRef)({ onValueChange, onChangeCommit });
10438
+ const callbacks = (0, import_react72.useRef)({ onValueChange, onChangeCommit });
10377
10439
  callbacks.current = { onValueChange, onChangeCommit };
10378
- (0, import_react71.useEffect)(() => {
10440
+ (0, import_react72.useEffect)(() => {
10379
10441
  if (!valid) console.warn("KJUN Slider: \uBC94\uC704\xB7step\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
10380
10442
  }, [valid]);
10381
- (0, import_react71.useEffect)(() => {
10443
+ (0, import_react72.useEffect)(() => {
10382
10444
  if (blocked) {
10383
10445
  setDragging(null);
10384
10446
  setHovered(null);
@@ -10418,7 +10480,7 @@ function RangeInput({
10418
10480
  measured.current = { x: pageX, width: Math.max(1, width) };
10419
10481
  });
10420
10482
  };
10421
- const responder = (0, import_react71.useMemo)(
10483
+ const responder = (0, import_react72.useMemo)(
10422
10484
  () => import_react_native63.PanResponder.create({
10423
10485
  onStartShouldSetPanResponderCapture: () => !disabled && valid,
10424
10486
  onMoveShouldSetPanResponderCapture: (_event, gesture) => !disabled && valid && Math.abs(gesture.dx) > 3 && Math.abs(gesture.dx) > Math.abs(gesture.dy),
@@ -10643,7 +10705,7 @@ function KjunRangeSlider(props) {
10643
10705
  }
10644
10706
 
10645
10707
  // packages/native/src/quantity-stepper.tsx
10646
- var import_react72 = require("react");
10708
+ var import_react73 = require("react");
10647
10709
  var import_react_native64 = require("react-native");
10648
10710
  var import_tokens66 = require("@kjun-ui/tokens");
10649
10711
 
@@ -10666,16 +10728,16 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
10666
10728
  const increaseStyle = { ...buttonStyle, borderRadius: 0, borderTopRightRadius: outer, borderBottomRightRadius: outer };
10667
10729
  const domain = (0, import_tokens66.createNumberDomain)({ min, max, step, precision }), valid = domain.valid && domain.accepts(value), blocked = disabled || !valid;
10668
10730
  const surface = useFieldSurface(size, { disabled: blocked, invalid });
10669
- const [draft, setDraft] = (0, import_react72.useState)(String(value));
10670
- const inputRef = (0, import_react72.useRef)(null);
10671
- const composing = (0, import_react72.useRef)(false), dirty = (0, import_react72.useRef)(false), accepted = (0, import_react72.useRef)(value);
10672
- const cancelledEscape = (0, import_react72.useRef)(false);
10673
- (0, import_react72.useEffect)(() => {
10731
+ const [draft, setDraft] = (0, import_react73.useState)(String(value));
10732
+ const inputRef = (0, import_react73.useRef)(null);
10733
+ const composing = (0, import_react73.useRef)(false), dirty = (0, import_react73.useRef)(false), accepted = (0, import_react73.useRef)(value);
10734
+ const cancelledEscape = (0, import_react73.useRef)(false);
10735
+ (0, import_react73.useEffect)(() => {
10674
10736
  setDraft(String(value));
10675
10737
  accepted.current = value;
10676
10738
  dirty.current = false;
10677
10739
  }, [value, precision, min, max, step]);
10678
- (0, import_react72.useEffect)(() => {
10740
+ (0, import_react73.useEffect)(() => {
10679
10741
  if (!valid) console.warn("KJUN QuantityStepper: \uBC94\uC704\xB7step\xB7\uC815\uBC00\uB3C4\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
10680
10742
  }, [valid]);
10681
10743
  const emit = (next) => {
@@ -10712,7 +10774,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
10712
10774
  } else if (action === "commit") commit();
10713
10775
  else move(action === "increment" ? 1 : -1);
10714
10776
  };
10715
- const handlers = (0, import_react72.useRef)({ key, edit: (text) => {
10777
+ const handlers = (0, import_react73.useRef)({ key, edit: (text) => {
10716
10778
  dirty.current = true;
10717
10779
  setDraft(text);
10718
10780
  } });
@@ -10720,7 +10782,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
10720
10782
  dirty.current = true;
10721
10783
  setDraft(text);
10722
10784
  } };
10723
- (0, import_react72.useEffect)(() => {
10785
+ (0, import_react73.useEffect)(() => {
10724
10786
  if (import_react_native64.Platform.OS !== "web") return;
10725
10787
  const input = inputRef.current;
10726
10788
  const keydown = (event) => handlers.current.key(event);
@@ -10792,7 +10854,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
10792
10854
  }
10793
10855
 
10794
10856
  // packages/native/src/time-picker.tsx
10795
- var import_react73 = require("react");
10857
+ var import_react74 = require("react");
10796
10858
  var import_react_native65 = require("react-native");
10797
10859
  var import_tokens67 = require("@kjun-ui/tokens");
10798
10860
  var import_jsx_runtime63 = require("react/jsx-runtime");
@@ -10801,12 +10863,12 @@ function KjunTimePicker({ value, precision = "minute", min, max, minuteStep = 1,
10801
10863
  ariaLabel != null ? ariaLabel : ariaLabel = m.time;
10802
10864
  const geometry2 = import_tokens67.tokens.extensions.timePicker, spec = import_tokens67.tokens.input[size];
10803
10865
  const { colors } = useKjunStyles();
10804
- const first = (0, import_react73.useRef)(null);
10805
- const [focused, setFocused] = (0, import_react73.useState)(false), [opened, setOpened] = (0, import_react73.useState)(null);
10806
- const field = useKjunField(), uid = (0, import_react73.useId)();
10866
+ const first = (0, import_react74.useRef)(null);
10867
+ const [focused, setFocused] = (0, import_react74.useState)(false), [opened, setOpened] = (0, import_react74.useState)(null);
10868
+ const field = useKjunField(), uid = (0, import_react74.useId)();
10807
10869
  const fieldId = (field == null ? void 0 : field.id) || `kjun-time-${uid}`;
10808
- const domain = (0, import_react73.useMemo)(() => (0, import_tokens67.createTimeDomain)({ precision, min, max, minuteStep, secondStep }), [precision, min, max, minuteStep, secondStep]), valid = domain.valid && domain.accepts(value);
10809
- (0, import_react73.useEffect)(() => {
10870
+ const domain = (0, import_react74.useMemo)(() => (0, import_tokens67.createTimeDomain)({ precision, min, max, minuteStep, secondStep }), [precision, min, max, minuteStep, secondStep]), valid = domain.valid && domain.accepts(value);
10871
+ (0, import_react74.useEffect)(() => {
10810
10872
  if (!valid) console.warn("KJUN TimePicker: \uC2DC\uAC01 \uBC94\uC704\xB7\uAC04\uACA9\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
10811
10873
  }, [valid]);
10812
10874
  const change = (next) => {
@@ -10817,7 +10879,7 @@ function KjunTimePicker({ value, precision = "minute", min, max, minuteStep = 1,
10817
10879
  };
10818
10880
  const time = value === null ? null : domain.parse(value);
10819
10881
  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: [
10820
- /* @__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_react73.Fragment, { children: [
10882
+ /* @__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_react74.Fragment, { children: [
10821
10883
  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: ":" }),
10822
10884
  /* @__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) => {
10823
10885
  if (typeof next === "number") change(domain.select(value, index, next));