@kjun-ui/native 0.6.1 → 0.8.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.
Files changed (30) hide show
  1. package/dist/index.cjs +413 -411
  2. package/dist/index.js +408 -406
  3. package/dist/index.web.cjs +413 -411
  4. package/dist/index.web.js +408 -406
  5. package/dist/styles.css +70 -52
  6. package/dist/types/packages/native/src/data-state.d.cts +3 -1
  7. package/dist/types/packages/native/src/data-state.d.ts +3 -1
  8. package/dist/types/packages/native/src/kpi-hero.d.cts +2 -0
  9. package/dist/types/packages/native/src/kpi-hero.d.ts +2 -0
  10. package/dist/types/packages/native/src/kpi-row.d.cts +3 -1
  11. package/dist/types/packages/native/src/kpi-row.d.ts +3 -1
  12. package/dist/types/packages/native/src/market-model.d.cts +5 -0
  13. package/dist/types/packages/native/src/market-model.d.ts +5 -0
  14. package/dist/types/packages/native/src/skeleton-base.d.cts +1 -1
  15. package/dist/types/packages/native/src/skeleton-base.d.ts +1 -1
  16. package/dist/types/packages/native/src/skeletons.d.cts +8 -3
  17. package/dist/types/packages/native/src/skeletons.d.ts +8 -3
  18. package/dist/types/packages/native/src/table-cards.d.cts +1 -1
  19. package/dist/types/packages/native/src/table-cards.d.ts +1 -1
  20. package/dist/types/packages/native/src/table-grid.d.cts +1 -1
  21. package/dist/types/packages/native/src/table-grid.d.ts +1 -1
  22. package/dist/types/shared/package-runtime/chart-skeleton.d.cts +12 -0
  23. package/dist/types/shared/package-runtime/chart-skeleton.d.ts +12 -0
  24. package/dist/types/shared/package-runtime/table-presentation.d.cts +2 -0
  25. package/dist/types/shared/package-runtime/table-presentation.d.ts +2 -0
  26. package/dist/types/shared/package-runtime/table.d.cts +5 -0
  27. package/dist/types/shared/package-runtime/table.d.ts +5 -0
  28. package/dist/types/shared/package-runtime/use-loading-reveal.d.cts +2 -0
  29. package/dist/types/shared/package-runtime/use-loading-reveal.d.ts +2 -0
  30. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -2271,36 +2271,6 @@ function KjunSkeleton({
2271
2271
  ))
2272
2272
  }
2273
2273
  ),
2274
- type === "chart" && /* @__PURE__ */ jsxs7(View10, { style: [frame, { height: height || tokens11.extensions.skeleton.chartHeight }], children: [
2275
- /* @__PURE__ */ jsx15(
2276
- View10,
2277
- {
2278
- style: {
2279
- flex: 1,
2280
- flexDirection: "row",
2281
- alignItems: "flex-end",
2282
- justifyContent: "space-around",
2283
- gap: tokens11.extensions.skeleton.gap
2284
- // Bars stand on the axis line, like ChartSkeleton.
2285
- },
2286
- children: [40, 65, 50, 80, 60, 90, 75].map((h, i) => /* @__PURE__ */ jsx15(
2287
- Animated6.View,
2288
- {
2289
- style: {
2290
- flex: 1,
2291
- maxWidth: tokens11.extensions.skeleton.chartBarMaxWidth,
2292
- height: `${h}%`,
2293
- backgroundColor: shimmer,
2294
- borderTopLeftRadius: tokens11.radius.radius4,
2295
- borderTopRightRadius: tokens11.radius.radius4
2296
- }
2297
- },
2298
- i
2299
- ))
2300
- }
2301
- ),
2302
- block("100%", tokens11.extensions.skeleton.chartAxisHeight, { backgroundColor: colors.border })
2303
- ] }),
2304
2274
  type === "stat" && /* @__PURE__ */ jsxs7(
2305
2275
  View10,
2306
2276
  {
@@ -6109,12 +6079,27 @@ import {
6109
6079
  initialQueryDisplay,
6110
6080
  queryDisplayStatus
6111
6081
  } from "@kjun-ui/tokens";
6112
- import { Component, useState as useState29 } from "react";
6082
+ import { Component, useState as useState30 } from "react";
6113
6083
  import { Platform as Platform23, View as View30 } from "react-native";
6084
+
6085
+ // shared/package-runtime/use-loading-reveal.ts
6086
+ import { useEffect as useEffect21, useState as useState29 } from "react";
6087
+ function useLoadingReveal(active, delay = 0) {
6088
+ const [revealed, setRevealed] = useState29(false);
6089
+ useEffect21(() => {
6090
+ setRevealed(false);
6091
+ if (!active || delay <= 0) return;
6092
+ const timer = setTimeout(() => setRevealed(true), delay);
6093
+ return () => clearTimeout(timer);
6094
+ }, [active, delay]);
6095
+ return delay <= 0 || revealed;
6096
+ }
6097
+
6098
+ // packages/native/src/data-state.tsx
6114
6099
  import { jsx as jsx37, jsxs as jsxs24 } from "react/jsx-runtime";
6115
6100
  function useQueryDisplay(props) {
6116
6101
  var _a;
6117
- const [state, setState] = useState29(() => initialQueryDisplay(props));
6102
+ const [state, setState] = useState30(() => initialQueryDisplay(props));
6118
6103
  let next = state;
6119
6104
  if (state.queryKey !== ((_a = props.queryKey) != null ? _a : null) || state.resultKey !== props.resultKey || state.loading !== !!props.loading) {
6120
6105
  next = advanceQueryDisplay(state, props);
@@ -6133,6 +6118,7 @@ function KjunDataState({
6133
6118
  emptyIcon,
6134
6119
  emptyActionText,
6135
6120
  loadingText = "\uB85C\uB529 \uC911...",
6121
+ loadingDelay = 0,
6136
6122
  retryText = "\uB2E4\uC2DC \uC2DC\uB3C4",
6137
6123
  size = "md",
6138
6124
  skeleton = false,
@@ -6146,7 +6132,7 @@ function KjunDataState({
6146
6132
  onEmptyAction,
6147
6133
  ...query
6148
6134
  }) {
6149
- const state = useQueryDisplay(query), blockingError = query.error && !(state.usesQueryState && state.hasCurrentResult), visibleRefreshError = refreshError || (state.usesQueryState && state.hasCurrentResult ? query.error : null), concealed = state.initialLoading || !!blockingError || empty, padding = tokens33.extensions.state.padding[size];
6135
+ const state = useQueryDisplay(query), blockingError = query.error && !(state.usesQueryState && state.hasCurrentResult), visibleRefreshError = refreshError || (state.usesQueryState && state.hasCurrentResult ? query.error : null), concealed = state.initialLoading || !!blockingError || empty, padding = tokens33.extensions.state.padding[size], revealed = useLoadingReveal(state.initialLoading, loadingDelay);
6150
6136
  const { colors } = useKjunStyles();
6151
6137
  return /* @__PURE__ */ jsxs24(
6152
6138
  View30,
@@ -6179,8 +6165,10 @@ function KjunDataState({
6179
6165
  state.initialLoading ? /* @__PURE__ */ jsx37(
6180
6166
  View30,
6181
6167
  {
6182
- accessibilityLabel: loadingText,
6183
- style: { paddingVertical: loadingPadding === "none" ? 0 : padding },
6168
+ accessibilityLabel: revealed ? loadingText : void 0,
6169
+ "aria-hidden": revealed ? void 0 : true,
6170
+ importantForAccessibility: revealed ? void 0 : "no-hide-descendants",
6171
+ style: { paddingVertical: loadingPadding === "none" ? 0 : padding, opacity: revealed ? void 0 : 0 },
6184
6172
  children: loadingContent || (skeleton ? /* @__PURE__ */ jsx37(View30, { style: { gap: tokens33.dimension.value12 }, children: Array.from({ length: skeletonCount }, (_, i) => /* @__PURE__ */ jsx37(KjunSkeleton, { type: skeletonType }, i)) }) : /* @__PURE__ */ jsxs24(View30, { style: { alignItems: "center", gap: tokens33.dimension.value12 }, children: [
6185
6173
  /* @__PURE__ */ jsx37(KjunSpinner, { size: "md" }),
6186
6174
  /* @__PURE__ */ jsx37(KText, { style: { ...typeStyle("body"), color: colors.textSecondary, textAlign: "center", maxWidth: "100%" }, children: loadingText })
@@ -6261,9 +6249,23 @@ var KjunErrorBoundary = class extends Component {
6261
6249
 
6262
6250
  // packages/native/src/skeletons.tsx
6263
6251
  import { tokens as tokens37 } from "@kjun-ui/tokens";
6264
- import { Fragment as Fragment11, useEffect as useEffect21, useRef as useRef28 } from "react";
6265
- import { Animated as Animated17, View as View32, useWindowDimensions as useWindowDimensions6 } from "react-native";
6266
- import Svg5, { G as G2, Path as Path4, Rect as Rect3 } from "react-native-svg";
6252
+ import { useEffect as useEffect22, useRef as useRef28, useState as useState32 } from "react";
6253
+ import { Animated as Animated17, Platform as Platform24, View as View32, useWindowDimensions as useWindowDimensions6 } from "react-native";
6254
+ import Svg5, { Path as Path4 } from "react-native-svg";
6255
+
6256
+ // shared/package-runtime/chart-skeleton.ts
6257
+ var chartFrameViewBox = "0 0 600 240";
6258
+ var chartFramePath = "M20 10V220H590 M20 65H590 M20 120H590 M20 175H590";
6259
+ var chartSkeletonMaxCount = 100;
6260
+ function chartSkeletonCount(value) {
6261
+ return Number.isFinite(value) ? Math.min(chartSkeletonMaxCount, Math.max(1, Math.floor(value))) : 1;
6262
+ }
6263
+ function chartMatrixMuted(row, column) {
6264
+ return (row + column + 2) % 3 === 0;
6265
+ }
6266
+ function chartDonutHole(height, thickness) {
6267
+ return Math.max(0, height * 0.8 - 2 * thickness);
6268
+ }
6267
6269
 
6268
6270
  // packages/native/src/market-skeleton.tsx
6269
6271
  import { tokens as tokens36 } from "@kjun-ui/tokens";
@@ -6290,9 +6292,9 @@ function nativeMarketLayout(columns, showActions, containerWidth) {
6290
6292
 
6291
6293
  // packages/native/src/market-layout.ts
6292
6294
  import { tokens as tokens35 } from "@kjun-ui/tokens";
6293
- import { useState as useState30 } from "react";
6295
+ import { useState as useState31 } from "react";
6294
6296
  function useMarketLayout(columns, showActions) {
6295
- const [containerWidth, setContainerWidth] = useState30(0);
6297
+ const [containerWidth, setContainerWidth] = useState31(0);
6296
6298
  const onLayout = (event) => setContainerWidth(event.nativeEvent.layout.width);
6297
6299
  return { ...nativeMarketLayout(columns, showActions, containerWidth), onLayout };
6298
6300
  }
@@ -6321,7 +6323,7 @@ function KjunMarketTableSkeleton({ columns = [], rows = 8, showActions = false }
6321
6323
  }
6322
6324
 
6323
6325
  // packages/native/src/skeletons.tsx
6324
- import { jsx as jsx39, jsxs as jsxs26 } from "react/jsx-runtime";
6326
+ import { Fragment as Fragment11, jsx as jsx39, jsxs as jsxs26 } from "react/jsx-runtime";
6325
6327
  function KjunListSkeleton({
6326
6328
  rows = 8,
6327
6329
  variant = "market",
@@ -6422,26 +6424,23 @@ function KjunFormSkeleton({
6422
6424
  }
6423
6425
  );
6424
6426
  }
6427
+ var chartPart = (name) => Platform24.OS === "web" ? { dataSet: { kjunChartPart: name } } : {};
6425
6428
  function KjunChartSkeleton({
6429
+ kind = "frame",
6430
+ height = tokens37.extensions.chartSkeleton.height,
6426
6431
  loadingText = "\uCC28\uD2B8\uB97C \uBD88\uB7EC\uC624\uB294 \uC911",
6427
- kind = "line",
6428
- height = tokens37.extensions.chartSkeleton.height
6432
+ skeleton = true,
6433
+ loadingDelay = 0,
6434
+ rows = 8,
6435
+ columns = 14
6429
6436
  }) {
6430
- const { colors } = useKjunStyles(), reduced = useReducedMotion(), opacity = useRef28(new Animated17.Value(1)).current;
6431
- useEffect21(() => {
6432
- if (reduced) return;
6437
+ const { colors } = useKjunStyles(), reduced = useReducedMotion(), opacity = useRef28(new Animated17.Value(1)).current, revealed = useLoadingReveal(true, loadingDelay), [measured, setMeasured] = useState32(0), thickness = tokens37.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" };
6438
+ useEffect22(() => {
6439
+ if (reduced || !skeleton) return;
6433
6440
  const loop = Animated17.loop(
6434
6441
  Animated17.sequence([
6435
- Animated17.timing(opacity, {
6436
- toValue: 0.55,
6437
- duration: tokens37.motion.shimmer / 2,
6438
- useNativeDriver: true
6439
- }),
6440
- Animated17.timing(opacity, {
6441
- toValue: 1,
6442
- duration: tokens37.motion.shimmer / 2,
6443
- useNativeDriver: true
6444
- })
6442
+ Animated17.timing(opacity, { toValue: 0.55, duration: tokens37.motion.shimmer / 2, useNativeDriver: true }),
6443
+ Animated17.timing(opacity, { toValue: 1, duration: tokens37.motion.shimmer / 2, useNativeDriver: true })
6445
6444
  ])
6446
6445
  );
6447
6446
  loop.start();
@@ -6449,98 +6448,73 @@ function KjunChartSkeleton({
6449
6448
  loop.stop();
6450
6449
  opacity.setValue(1);
6451
6450
  };
6452
- }, [opacity, reduced]);
6453
- return /* @__PURE__ */ jsxs26(
6451
+ }, [opacity, reduced, skeleton]);
6452
+ return /* @__PURE__ */ jsx39(
6454
6453
  Animated17.View,
6455
6454
  {
6456
- style: {
6457
- height,
6458
- alignItems: "center",
6459
- justifyContent: "center",
6460
- width: "100%",
6461
- minWidth: 0,
6462
- opacity
6463
- },
6464
- accessibilityElementsHidden: true,
6465
- importantForAccessibility: "no-hide-descendants",
6466
- children: [
6455
+ onLayout: typeof height === "number" ? void 0 : (event) => setMeasured(event.nativeEvent.layout.height),
6456
+ style: { height, alignItems: "center", justifyContent: "center", width: "100%", minWidth: 0, opacity: revealed ? opacity : 0 },
6457
+ "aria-hidden": revealed ? void 0 : true,
6458
+ importantForAccessibility: revealed ? void 0 : "no-hide-descendants",
6459
+ ...Platform24.OS === "web" ? { dataSet: { kjunChartKind: kind, skeleton: skeleton ? void 0 : "false" } } : {},
6460
+ children: !skeleton ? /* @__PURE__ */ jsxs26(View32, { ...chartPart("loading"), role: status, style: { alignItems: "center", gap: tokens37.dimension.value12 }, children: [
6461
+ /* @__PURE__ */ jsx39(View32, { ...hidden, children: /* @__PURE__ */ jsx39(KjunSpinner, { size: "md" }) }),
6462
+ !!loadingText && /* @__PURE__ */ jsx39(KText, { style: { ...typeStyle("body"), color: colors.textSecondary, textAlign: "center", maxWidth: "100%" }, children: loadingText })
6463
+ ] }) : /* @__PURE__ */ jsxs26(Fragment11, { children: [
6467
6464
  kind === "donut" ? /* @__PURE__ */ jsx39(
6468
6465
  View32,
6469
6466
  {
6470
- style: {
6471
- height: "80%",
6472
- aspectRatio: 1,
6473
- borderWidth: tokens37.extensions.chartSkeleton.donutThickness,
6474
- borderColor: colors.skeleton,
6475
- borderRadius: tokens37.radius.radius9999,
6476
- alignItems: "center",
6477
- justifyContent: "center"
6478
- },
6479
- children: /* @__PURE__ */ jsx39(KjunSkeleton, { type: "block", width: tokens37.extensions.chartSkeleton.donutLabelWidth, height: tokens37.extensions.chartSkeleton.donutLabelHeight })
6467
+ ...chartPart("donut"),
6468
+ ...hidden,
6469
+ style: { height: "80%", aspectRatio: 1, borderWidth: thickness, borderColor: colors.skeleton, borderRadius: tokens37.radius.radius9999 }
6480
6470
  }
6481
- ) : /* @__PURE__ */ jsxs26(
6482
- Svg5,
6471
+ ) : kind === "matrix" ? /* @__PURE__ */ jsx39(View32, { ...chartPart("matrix"), ...hidden, style: { alignSelf: "stretch", flex: 1, gap: tokens37.dimension.value4 }, children: Array.from({ length: rowCount }, (_, row) => /* @__PURE__ */ jsx39(View32, { style: { flex: 1, flexDirection: "row", gap: tokens37.dimension.value4 }, children: Array.from({ length: columnCount }, (_2, column) => /* @__PURE__ */ jsx39(
6472
+ View32,
6483
6473
  {
6484
- viewBox: "0 0 600 240",
6485
- preserveAspectRatio: "none",
6486
- width: "100%",
6487
- height: "100%",
6488
- children: [
6489
- /* @__PURE__ */ jsx39(
6490
- Path4,
6491
- {
6492
- d: "M20 10V220H590 M20 65H590 M20 120H590 M20 175H590",
6493
- fill: "none",
6494
- stroke: colors.border,
6495
- strokeWidth: 1
6496
- }
6497
- ),
6498
- kind === "line" && /* @__PURE__ */ jsx39(Path4, { d: "M20 190 L100 160 L180 172 L260 120 L340 140 L420 96 L500 110 L590 64", fill: "none", stroke: colors.skeleton, strokeWidth: 4, strokeLinecap: "round", strokeLinejoin: "round" }),
6499
- kind === "matrix" ? Array.from({ length: 8 }, (_, row) => /* @__PURE__ */ jsx39(G2, { children: Array.from({ length: 14 }, (_2, col) => /* @__PURE__ */ jsx39(
6500
- Rect3,
6501
- {
6502
- x: 24 + col * 40,
6503
- y: 12 + row * 26,
6504
- width: 36,
6505
- height: 22,
6506
- rx: 2,
6507
- fill: colors.skeleton,
6508
- opacity: (row + col + 2) % 3 === 0 ? 0.55 : 1
6474
+ ...chartPart("cell"),
6475
+ style: {
6476
+ flex: 1,
6477
+ borderRadius: tokens37.extensions.skeleton.lineRadius,
6478
+ backgroundColor: colors.skeleton,
6479
+ opacity: chartMatrixMuted(row, column) ? tokens37.states.opacity.pending : 1
6480
+ }
6481
+ },
6482
+ column
6483
+ )) }, row)) }) : /* @__PURE__ */ jsx39(View32, { ...chartPart("plot"), ...hidden, style: { alignSelf: "stretch", flex: 1 }, children: /* @__PURE__ */ jsx39(Svg5, { viewBox: chartFrameViewBox, preserveAspectRatio: "none", width: "100%", height: "100%", children: /* @__PURE__ */ jsx39(Path4, { d: chartFramePath, fill: "none", stroke: colors.chartGrid, strokeWidth: tokens37.border.defaultWidth, vectorEffect: "non-scaling-stroke" }) }) }),
6484
+ !!loadingText && /* @__PURE__ */ jsx39(
6485
+ View32,
6486
+ {
6487
+ ...chartPart("status"),
6488
+ role: status,
6489
+ pointerEvents: "none",
6490
+ style: { position: "absolute", inset: 0, alignItems: "center", justifyContent: "center" },
6491
+ children: /* @__PURE__ */ jsx39(
6492
+ KText,
6493
+ {
6494
+ style: {
6495
+ ...typeStyle("body"),
6496
+ paddingHorizontal: tokens37.dimension.value12,
6497
+ paddingVertical: tokens37.dimension.value4,
6498
+ borderRadius: tokens37.radius.radius6,
6499
+ borderWidth: kind === "donut" ? 0 : tokens37.border.defaultWidth,
6500
+ borderColor: colors.chartGrid,
6501
+ backgroundColor: colors.surface,
6502
+ color: colors.textSecondary,
6503
+ maxWidth: kind === "donut" && hole != null ? hole : "90%",
6504
+ textAlign: "center"
6509
6505
  },
6510
- col
6511
- )) }, row)) : (kind === "bar" || kind === "candle") && [60, 100, 85, 140, 110, 165, 180].map((h, i) => /* @__PURE__ */ jsxs26(Fragment11, { children: [
6512
- kind === "candle" && /* @__PURE__ */ jsx39(
6513
- Path4,
6514
- {
6515
- d: `M${55 + i * 75} ${195 - h}V${225 - h / 2}`,
6516
- fill: "none",
6517
- stroke: colors.skeleton,
6518
- strokeWidth: 4
6519
- }
6520
- ),
6521
- /* @__PURE__ */ jsx39(
6522
- Rect3,
6523
- {
6524
- x: 35 + i * 75,
6525
- y: kind === "candle" ? 210 - h : 220 - h,
6526
- width: kind === "candle" ? 28 : 42,
6527
- height: kind === "candle" ? h / 2 : h,
6528
- rx: 3,
6529
- fill: colors.skeleton
6530
- }
6531
- )
6532
- ] }, i))
6533
- ]
6506
+ children: loadingText
6507
+ }
6508
+ )
6534
6509
  }
6535
- ),
6536
- /* @__PURE__ */ jsx39(View32, { pointerEvents: "none", style: { position: "absolute", inset: 0, alignItems: "center", justifyContent: "center", gap: tokens37.dimension.value12 }, children: !!loadingText && /* @__PURE__ */ jsx39(KText, { style: { ...typeStyle("body"), paddingHorizontal: tokens37.dimension.value12, paddingVertical: tokens37.dimension.value4, borderRadius: tokens37.radius.radius6, backgroundColor: colors.surface, color: colors.textSecondary, maxWidth: "90%", textAlign: "center" }, children: loadingText }) })
6537
- ]
6510
+ )
6511
+ ] })
6538
6512
  }
6539
6513
  );
6540
6514
  }
6541
6515
 
6542
6516
  // packages/native/src/financial-display.tsx
6543
- import { tokens as tokens40 } from "@kjun-ui/tokens";
6517
+ import { deviationLevel, tokens as tokens40 } from "@kjun-ui/tokens";
6544
6518
  import { useId as useId8 } from "react";
6545
6519
  import {
6546
6520
  View as View34
@@ -6558,7 +6532,7 @@ import Svg6, {
6558
6532
  import { tokens as tokens39 } from "@kjun-ui/tokens";
6559
6533
 
6560
6534
  // shared/package-runtime/use-number-motion.ts
6561
- import { useLayoutEffect as useLayoutEffect12, useRef as useRef29, useState as useState31 } from "react";
6535
+ import { useLayoutEffect as useLayoutEffect12, useRef as useRef29, useState as useState33 } from "react";
6562
6536
 
6563
6537
  // shared/package-runtime/number-motion.ts
6564
6538
  import { tokens as tokens38 } from "@kjun-ui/tokens";
@@ -6604,7 +6578,7 @@ function createNumberMotion(apply) {
6604
6578
 
6605
6579
  // shared/package-runtime/use-number-motion.ts
6606
6580
  function useNumberMotion(value, animated, fromPrevious, reduced) {
6607
- const [display, setDisplay] = useState31(typeof value === "string" || fromPrevious || !animated || reduced ? value : 0);
6581
+ const [display, setDisplay] = useState33(typeof value === "string" || fromPrevious || !animated || reduced ? value : 0);
6608
6582
  const motion = useRef29(null);
6609
6583
  if (!motion.current) motion.current = createNumberMotion(setDisplay);
6610
6584
  useLayoutEffect12(() => {
@@ -6790,6 +6764,7 @@ function KjunSignedValue({
6790
6764
 
6791
6765
  // packages/native/src/financial-display.tsx
6792
6766
  import { Fragment as Fragment12, jsx as jsx41, jsxs as jsxs28 } from "react/jsx-runtime";
6767
+ var deviationFill = { neutral: 1, low: tokens40.states.opacity.deviationLow, medium: tokens40.states.opacity.deviationMedium, strong: tokens40.states.opacity.deviationStrong };
6793
6768
  function KjunDeviation({
6794
6769
  value,
6795
6770
  variant = "pill",
@@ -6800,7 +6775,7 @@ function KjunDeviation({
6800
6775
  const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), n = finiteNumber(value);
6801
6776
  if (n === null)
6802
6777
  return /* @__PURE__ */ jsx41(KText, { style: { color: colors.textDisabled }, children: "-" });
6803
- const abs = Math.abs(n), signal = abs >= signalThreshold, neutral = abs < 0.1, color = neutral ? colors.textSecondary : domainColor(domainColors, n > 0 ? "priceUp" : "priceDown"), label = formatter ? formatter(n) : (n > 0 ? "+" : "") + format(n, decimals) + "%";
6778
+ const abs = Math.abs(n), signal = abs >= signalThreshold, level = deviationLevel(n), neutral = level === "neutral", color = neutral ? colors.textSecondary : domainColor(domainColors, n > 0 ? "priceUp" : "priceDown"), label = formatter ? formatter(n) : (n > 0 ? "+" : "") + format(n, decimals) + "%";
6804
6779
  if (variant === "badge")
6805
6780
  return /* @__PURE__ */ jsxs28(
6806
6781
  View34,
@@ -6869,7 +6844,7 @@ function KjunDeviation({
6869
6844
  inset: 0,
6870
6845
  borderRadius: tokens40.extensions.financial.cellRadius,
6871
6846
  backgroundColor: neutral ? colors.secondary : color,
6872
- opacity: neutral ? 1 : abs < 0.5 ? 0.08 : abs < 1 ? 0.14 : 0.22
6847
+ opacity: deviationFill[level]
6873
6848
  }
6874
6849
  }
6875
6850
  ),
@@ -6882,7 +6857,7 @@ function KjunDeviation({
6882
6857
  fontVariant: ["tabular-nums"],
6883
6858
  ...typeStyle("body"),
6884
6859
  // Like Web: control weight, stronger once the deviation reaches 1%.
6885
- fontWeight: abs >= 1 ? typeStyle("numberLg").fontWeight : typeStyle("control").fontWeight
6860
+ fontWeight: level === "strong" ? typeStyle("numberLg").fontWeight : typeStyle("control").fontWeight
6886
6861
  },
6887
6862
  children: label
6888
6863
  }
@@ -7090,7 +7065,7 @@ function KjunSparkline({
7090
7065
 
7091
7066
  // shared/package-runtime/table.ts
7092
7067
  import { nextTableSort } from "@kjun-ui/tokens";
7093
- import { useEffect as useEffect23, useRef as useRef31, useState as useState33 } from "react";
7068
+ import { useEffect as useEffect24, useRef as useRef31, useState as useState35 } from "react";
7094
7069
 
7095
7070
  // shared/package-runtime/table-model.ts
7096
7071
  function getValue(row, key) {
@@ -7134,11 +7109,11 @@ function useTableModel(props) {
7134
7109
  expandSingle = false,
7135
7110
  onExpandedRowsChange
7136
7111
  } = props;
7137
- const [internalSort, setSort] = useState33({
7112
+ const [internalSort, setSort] = useState35({
7138
7113
  key: "",
7139
7114
  order: "asc"
7140
- }), [internalExpanded, setExpanded] = useState33([]), [search, setSearch] = useState33(""), timer = useRef31(void 0);
7141
- useEffect23(() => () => clearTimeout(timer.current), []);
7115
+ }), [internalExpanded, setExpanded] = useState35([]), [search, setSearch] = useState35(""), timer = useRef31(void 0);
7116
+ useEffect24(() => () => clearTimeout(timer.current), []);
7142
7117
  const sort = props.sort === void 0 ? internalSort : (_a = props.sort) != null ? _a : { key: "", order: "asc" };
7143
7118
  const rows = sortTableRows(data, sort, { sortable, sortMode });
7144
7119
  const key = (row, index = 0) => tableRowKey(row, rowKey, index);
@@ -7292,19 +7267,19 @@ function TableToolbar({
7292
7267
 
7293
7268
  // packages/native/src/table-grid.tsx
7294
7269
  import { tokens as tokens42 } from "@kjun-ui/tokens";
7295
- import { Fragment as Fragment14, useId as useId9, useState as useState34 } from "react";
7296
- import Svg7, { Defs as Defs3, LinearGradient as LinearGradient3, Rect as Rect4, Stop as Stop3 } from "react-native-svg";
7270
+ import { Fragment as Fragment14, useId as useId9, useState as useState36 } from "react";
7271
+ import Svg7, { Defs as Defs3, LinearGradient as LinearGradient3, Rect as Rect3, Stop as Stop3 } from "react-native-svg";
7297
7272
  import { ScrollView as ScrollView9, View as View36 } from "react-native";
7298
7273
  import { jsx as jsx43, jsxs as jsxs30 } from "react/jsx-runtime";
7299
- function TableGrid({ initialLoading, skeletonRows, state, onRowClick, selectable, checkbox, cell, expandable, expand, renderExpand, emptyContent, emptyText, visible, maxHeight, stickyHeader, sortable, compact, striped, hoverable }) {
7274
+ function TableGrid({ initialLoading, skeletonRows, skeletonHidden, state, onRowClick, selectable, checkbox, cell, expandable, expand, renderExpand, emptyContent, emptyText, visible, maxHeight, stickyHeader, sortable, compact, striped, hoverable }) {
7300
7275
  const { colors } = useKjunStyles();
7301
7276
  const geometry2 = tokens42.table;
7302
- const [scroll, setScroll] = useState34({ viewport: 0, x: 0 }), fadeId = "kjun-table-fade-" + useId9().replace(/[^a-z0-9]/gi, "");
7277
+ const [scroll, setScroll] = useState36({ viewport: 0, x: 0 }), fadeId = "kjun-table-fade-" + useId9().replace(/[^a-z0-9]/gi, "");
7303
7278
  const cellPadding = geometry2.cellPadding[compact ? "compact" : "regular"];
7304
7279
  const width = (col) => typeof col.width === "number" ? col.width : typeof col.width === "string" && /^\d+(px)?$/.test(col.width) ? parseFloat(col.width) : geometry2.columnWidth;
7305
7280
  const totalWidth = visible.reduce((sum, col) => sum + width(col), 0) + (selectable ? geometry2.actionColumnWidth : 0) + (expandable ? geometry2.actionColumnWidth : 0);
7306
7281
  const fade = totalWidth - scroll.viewport - scroll.x > 1;
7307
- return /* @__PURE__ */ jsxs30(View36, { style: { position: "relative", minWidth: 0 }, children: [
7282
+ return /* @__PURE__ */ jsxs30(View36, { style: { position: "relative", minWidth: 0, opacity: skeletonHidden ? 0 : void 0 }, "aria-hidden": skeletonHidden || void 0, children: [
7308
7283
  /* @__PURE__ */ jsx43(
7309
7284
  ScrollView9,
7310
7285
  {
@@ -7486,7 +7461,7 @@ function TableGrid({ initialLoading, skeletonRows, state, onRowClick, selectable
7486
7461
  /* @__PURE__ */ jsx43(Stop3, { offset: "0", stopColor: colors.surface, stopOpacity: 0 }),
7487
7462
  /* @__PURE__ */ jsx43(Stop3, { offset: "1", stopColor: colors.surface, stopOpacity: 1 })
7488
7463
  ] }) }),
7489
- /* @__PURE__ */ jsx43(Rect4, { width: "100%", height: "100%", fill: `url(#${fadeId})` })
7464
+ /* @__PURE__ */ jsx43(Rect3, { width: "100%", height: "100%", fill: `url(#${fadeId})` })
7490
7465
  ] }) })
7491
7466
  ] });
7492
7467
  }
@@ -7507,6 +7482,7 @@ import { jsx as jsx44, jsxs as jsxs31 } from "react/jsx-runtime";
7507
7482
  function TableCards({
7508
7483
  initialLoading,
7509
7484
  skeletonRows,
7485
+ skeletonHidden,
7510
7486
  state,
7511
7487
  onRowClick,
7512
7488
  selectable,
@@ -7521,149 +7497,156 @@ function TableCards({
7521
7497
  }) {
7522
7498
  const { colors } = useKjunStyles();
7523
7499
  const { title, subtitle, badges, inline, sections, actions, columnsForSection } = layout;
7524
- return /* @__PURE__ */ jsxs31(View37, { style: { gap: tokens43.extensions.tableCard.gap, padding: tokens43.extensions.tableCard.listPadding }, children: [
7525
- initialLoading ? Array.from({ length: skeletonRows }, (_, i) => /* @__PURE__ */ jsx44(KjunSkeleton, { type: "card" }, i)) : state.rows.map((row, i) => /* @__PURE__ */ jsxs31(
7526
- AccessiblePressable,
7527
- {
7528
- accessible: false,
7529
- onPress: () => onRowClick == null ? void 0 : onRowClick(row, i),
7530
- style: ({ pressed }) => ({
7531
- padding: tokens43.extensions.tableCard.padding,
7532
- borderWidth: tokens43.border.defaultWidth,
7533
- borderColor: colors.border,
7534
- borderRadius: tokens43.extensions.tableCard.radius,
7535
- backgroundColor: pressed ? colors.hover : colors.background
7536
- }),
7537
- children: [
7538
- /* @__PURE__ */ jsxs31(
7539
- View37,
7540
- {
7541
- style: {
7542
- flexDirection: "row",
7543
- alignItems: "center",
7544
- gap: tokens43.extensions.tableCard.gap,
7545
- marginBottom: tokens43.dimension.value8
7546
- },
7547
- children: [
7548
- /* @__PURE__ */ jsxs31(
7500
+ return /* @__PURE__ */ jsxs31(
7501
+ View37,
7502
+ {
7503
+ style: { gap: tokens43.extensions.tableCard.gap, padding: tokens43.extensions.tableCard.listPadding, opacity: skeletonHidden ? 0 : void 0 },
7504
+ "aria-hidden": skeletonHidden || void 0,
7505
+ children: [
7506
+ initialLoading ? Array.from({ length: skeletonRows }, (_, i) => /* @__PURE__ */ jsx44(KjunSkeleton, { type: "card" }, i)) : state.rows.map((row, i) => /* @__PURE__ */ jsxs31(
7507
+ AccessiblePressable,
7508
+ {
7509
+ accessible: false,
7510
+ onPress: () => onRowClick == null ? void 0 : onRowClick(row, i),
7511
+ style: ({ pressed }) => ({
7512
+ padding: tokens43.extensions.tableCard.padding,
7513
+ borderWidth: tokens43.border.defaultWidth,
7514
+ borderColor: colors.border,
7515
+ borderRadius: tokens43.extensions.tableCard.radius,
7516
+ backgroundColor: pressed ? colors.hover : colors.background
7517
+ }),
7518
+ children: [
7519
+ /* @__PURE__ */ jsxs31(
7520
+ View37,
7521
+ {
7522
+ style: {
7523
+ flexDirection: "row",
7524
+ alignItems: "center",
7525
+ gap: tokens43.extensions.tableCard.gap,
7526
+ marginBottom: tokens43.dimension.value8
7527
+ },
7528
+ children: [
7529
+ /* @__PURE__ */ jsxs31(
7530
+ View37,
7531
+ {
7532
+ style: {
7533
+ flexDirection: "row",
7534
+ flex: 1,
7535
+ alignItems: "center",
7536
+ gap: tokens43.extensions.tableCard.gap,
7537
+ minWidth: 0
7538
+ },
7539
+ children: [
7540
+ selectable && checkbox(row, i),
7541
+ title && /* @__PURE__ */ jsx44(View37, { style: { flex: 1, minWidth: 0 }, children: cell(title, row, i, "title") })
7542
+ ]
7543
+ }
7544
+ ),
7545
+ badges.length ? /* @__PURE__ */ jsx44(View37, { style: { flexDirection: "row", gap: tokens43.extensions.tableCard.fieldGap }, children: badges.map((col) => /* @__PURE__ */ jsx44(View37, { children: cell(col, row, i) }, col.key)) }) : subtitle ? cell(subtitle, row, i) : null,
7546
+ inline.length > 0 && /* @__PURE__ */ jsx44(View37, { style: { flexDirection: "row", gap: tokens43.extensions.tableCard.fieldGap }, children: inline.map((col) => /* @__PURE__ */ jsx44(View37, { children: cell(col, row, i) }, col.key)) })
7547
+ ]
7548
+ }
7549
+ ),
7550
+ sections.map((section, index) => {
7551
+ const cols = columnsForSection(row, section);
7552
+ return cols.length ? /* @__PURE__ */ jsxs31(
7549
7553
  View37,
7550
7554
  {
7551
7555
  style: {
7552
- flexDirection: "row",
7553
- flex: 1,
7554
- alignItems: "center",
7555
- gap: tokens43.extensions.tableCard.gap,
7556
- minWidth: 0
7556
+ borderTopWidth: index ? tokens43.border.defaultWidth : 0,
7557
+ borderTopColor: colors.border,
7558
+ paddingTop: index ? tokens43.dimension.value12 : 0,
7559
+ marginTop: index ? tokens43.dimension.value12 : 0
7557
7560
  },
7558
7561
  children: [
7559
- selectable && checkbox(row, i),
7560
- title && /* @__PURE__ */ jsx44(View37, { style: { flex: 1, minWidth: 0 }, children: cell(title, row, i, "title") })
7561
- ]
7562
- }
7563
- ),
7564
- badges.length ? /* @__PURE__ */ jsx44(View37, { style: { flexDirection: "row", gap: tokens43.extensions.tableCard.fieldGap }, children: badges.map((col) => /* @__PURE__ */ jsx44(View37, { children: cell(col, row, i) }, col.key)) }) : subtitle ? cell(subtitle, row, i) : null,
7565
- inline.length > 0 && /* @__PURE__ */ jsx44(View37, { style: { flexDirection: "row", gap: tokens43.extensions.tableCard.fieldGap }, children: inline.map((col) => /* @__PURE__ */ jsx44(View37, { children: cell(col, row, i) }, col.key)) })
7566
- ]
7567
- }
7568
- ),
7569
- sections.map((section, index) => {
7570
- const cols = columnsForSection(row, section);
7571
- return cols.length ? /* @__PURE__ */ jsxs31(
7572
- View37,
7573
- {
7574
- style: {
7575
- borderTopWidth: index ? tokens43.border.defaultWidth : 0,
7576
- borderTopColor: colors.border,
7577
- paddingTop: index ? tokens43.dimension.value12 : 0,
7578
- marginTop: index ? tokens43.dimension.value12 : 0
7579
- },
7580
- children: [
7581
- section.label && /* @__PURE__ */ jsx44(
7582
- KText,
7583
- {
7584
- style: {
7585
- ...typeStyle("controlSmall"),
7586
- color: colors.textTertiary,
7587
- marginBottom: tokens43.dimension.value6
7588
- },
7589
- children: section.label
7590
- }
7591
- ),
7592
- /* @__PURE__ */ jsx44(
7593
- View37,
7594
- {
7595
- style: {
7596
- flexDirection: "row",
7597
- flexWrap: "wrap",
7598
- gap: section.layout === "metrics" ? tokens43.extensions.tableCard.gap : tokens43.extensions.tableCard.metricGap
7599
- },
7600
- children: cols.map((col) => /* @__PURE__ */ jsxs31(
7562
+ section.label && /* @__PURE__ */ jsx44(
7563
+ KText,
7564
+ {
7565
+ style: {
7566
+ ...typeStyle("controlSmall"),
7567
+ color: colors.textTertiary,
7568
+ marginBottom: tokens43.dimension.value6
7569
+ },
7570
+ children: section.label
7571
+ }
7572
+ ),
7573
+ /* @__PURE__ */ jsx44(
7601
7574
  View37,
7602
7575
  {
7603
7576
  style: {
7604
- flexBasis: col.fullWidthInCard || section.layout === "stack" ? "100%" : section.layout === "metrics" ? "30%" : "47%",
7605
- flexGrow: 1,
7606
- minWidth: 0,
7607
- gap: tokens43.extensions.tableCard.valueGap
7577
+ flexDirection: "row",
7578
+ flexWrap: "wrap",
7579
+ gap: section.layout === "metrics" ? tokens43.extensions.tableCard.gap : tokens43.extensions.tableCard.metricGap
7608
7580
  },
7609
- children: [
7610
- /* @__PURE__ */ jsx44(
7611
- KText,
7612
- {
7613
- style: {
7614
- ...typeStyle("meta"),
7615
- color: colors.textTertiary,
7616
- textAlign: section.labelAlign
7617
- },
7618
- children: col.label
7619
- }
7620
- ),
7621
- cell(col, row, i)
7622
- ]
7623
- },
7624
- col.key
7625
- ))
7626
- }
7627
- )
7628
- ]
7629
- },
7630
- section.key || index
7631
- ) : null;
7632
- }),
7633
- expandable && expand(row, i),
7634
- expandable && state.expanded.includes(state.key(row, i)) && /* @__PURE__ */ jsx44(
7635
- View37,
7636
- {
7637
- style: {
7638
- borderTopWidth: tokens43.border.defaultWidth,
7639
- borderTopColor: colors.border,
7640
- paddingTop: tokens43.dimension.value8,
7641
- marginTop: tokens43.dimension.value4
7642
- },
7643
- children: content(renderExpand == null ? void 0 : renderExpand(row, i))
7644
- }
7645
- ),
7646
- actions && !actions.inlineInCard && /* @__PURE__ */ jsx44(
7647
- View37,
7648
- {
7649
- style: {
7650
- borderTopWidth: tokens43.border.defaultWidth,
7651
- borderTopColor: colors.border,
7652
- paddingTop: tokens43.dimension.value8,
7653
- marginTop: tokens43.dimension.value8,
7654
- alignItems: "flex-end"
7655
- },
7656
- children: /* @__PURE__ */ jsx44(View37, { style: { alignSelf: "flex-end" }, children: cell(actions, row, i) })
7657
- }
7658
- )
7659
- ]
7660
- },
7661
- state.key(row, i)
7662
- )),
7663
- !initialLoading && !state.rows.length && /* @__PURE__ */ jsx44(View37, { style: { padding: tokens43.extensions.tableCard.emptyPadding, alignItems: "center" }, children: content(emptyContent || emptyText, {
7664
- color: colors.textTertiary
7665
- }) })
7666
- ] });
7581
+ children: cols.map((col) => /* @__PURE__ */ jsxs31(
7582
+ View37,
7583
+ {
7584
+ style: {
7585
+ flexBasis: col.fullWidthInCard || section.layout === "stack" ? "100%" : section.layout === "metrics" ? "30%" : "47%",
7586
+ flexGrow: 1,
7587
+ minWidth: 0,
7588
+ gap: tokens43.extensions.tableCard.valueGap
7589
+ },
7590
+ children: [
7591
+ /* @__PURE__ */ jsx44(
7592
+ KText,
7593
+ {
7594
+ style: {
7595
+ ...typeStyle("meta"),
7596
+ color: colors.textTertiary,
7597
+ textAlign: section.labelAlign
7598
+ },
7599
+ children: col.label
7600
+ }
7601
+ ),
7602
+ cell(col, row, i)
7603
+ ]
7604
+ },
7605
+ col.key
7606
+ ))
7607
+ }
7608
+ )
7609
+ ]
7610
+ },
7611
+ section.key || index
7612
+ ) : null;
7613
+ }),
7614
+ expandable && expand(row, i),
7615
+ expandable && state.expanded.includes(state.key(row, i)) && /* @__PURE__ */ jsx44(
7616
+ View37,
7617
+ {
7618
+ style: {
7619
+ borderTopWidth: tokens43.border.defaultWidth,
7620
+ borderTopColor: colors.border,
7621
+ paddingTop: tokens43.dimension.value8,
7622
+ marginTop: tokens43.dimension.value4
7623
+ },
7624
+ children: content(renderExpand == null ? void 0 : renderExpand(row, i))
7625
+ }
7626
+ ),
7627
+ actions && !actions.inlineInCard && /* @__PURE__ */ jsx44(
7628
+ View37,
7629
+ {
7630
+ style: {
7631
+ borderTopWidth: tokens43.border.defaultWidth,
7632
+ borderTopColor: colors.border,
7633
+ paddingTop: tokens43.dimension.value8,
7634
+ marginTop: tokens43.dimension.value8,
7635
+ alignItems: "flex-end"
7636
+ },
7637
+ children: /* @__PURE__ */ jsx44(View37, { style: { alignSelf: "flex-end" }, children: cell(actions, row, i) })
7638
+ }
7639
+ )
7640
+ ]
7641
+ },
7642
+ state.key(row, i)
7643
+ )),
7644
+ !initialLoading && !state.rows.length && /* @__PURE__ */ jsx44(View37, { style: { padding: tokens43.extensions.tableCard.emptyPadding, alignItems: "center" }, children: content(emptyContent || emptyText, {
7645
+ color: colors.textTertiary
7646
+ }) })
7647
+ ]
7648
+ }
7649
+ );
7667
7650
  }
7668
7651
 
7669
7652
  // packages/native/src/table-view.tsx
@@ -7671,6 +7654,9 @@ import { jsx as jsx45, jsxs as jsxs32 } from "react/jsx-runtime";
7671
7654
  function KjunTable(props) {
7672
7655
  const {
7673
7656
  skeletonRows = 8,
7657
+ skeleton = true,
7658
+ loadingText = "\uB85C\uB529 \uC911...",
7659
+ loadingDelay = 0,
7674
7660
  emptyText = "\uB370\uC774\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4",
7675
7661
  hoverable = true,
7676
7662
  searchable = false,
@@ -7691,7 +7677,7 @@ function KjunTable(props) {
7691
7677
  onRowClick,
7692
7678
  onRetry
7693
7679
  } = props;
7694
- const { colors, domainColors } = useKjunStyles(), state = useTableModel2(props), query = useQueryDisplay(props), mobile = useWindowDimensions7().width < tokens44.table.mobileBreakpoint;
7680
+ const { colors, domainColors } = useKjunStyles(), state = useTableModel2(props), query = useQueryDisplay(props), revealed = useLoadingReveal(query.initialLoading, skeleton ? loadingDelay : 0), mobile = useWindowDimensions7().width < tokens44.table.mobileBreakpoint;
7695
7681
  const layout = tablePresentation(props, mobile);
7696
7682
  const { card, visible } = layout;
7697
7683
  const cell = (col, row, index, role) => {
@@ -7770,8 +7756,11 @@ function KjunTable(props) {
7770
7756
  refreshing: query.queryRefreshing,
7771
7757
  loadingPadding: "none",
7772
7758
  onRetry,
7759
+ loading: skeleton ? void 0 : props.loading,
7760
+ loadingText,
7761
+ loadingDelay: skeleton ? 0 : loadingDelay,
7773
7762
  children: [
7774
- card ? /* @__PURE__ */ jsx45(TableCards, { ...{ initialLoading: query.initialLoading, skeletonRows, state, onRowClick, selectable, checkbox, cell, expandable, expand, renderExpand, emptyContent, emptyText, layout } }) : /* @__PURE__ */ jsx45(TableGrid, { ...{ initialLoading: query.initialLoading, skeletonRows, state, onRowClick, selectable, checkbox, cell, expandable, expand, renderExpand, emptyContent, emptyText, visible, maxHeight, stickyHeader, compact, striped, hoverable }, sortable: props.sortable }),
7763
+ card ? /* @__PURE__ */ jsx45(TableCards, { ...{ initialLoading: query.initialLoading, skeletonRows, skeletonHidden: query.initialLoading && !revealed, state, onRowClick, selectable, checkbox, cell, expandable, expand, renderExpand, emptyContent, emptyText, layout } }) : /* @__PURE__ */ jsx45(TableGrid, { ...{ initialLoading: query.initialLoading, skeletonRows, skeletonHidden: query.initialLoading && !revealed, state, onRowClick, selectable, checkbox, cell, expandable, expand, renderExpand, emptyContent, emptyText, visible, maxHeight, stickyHeader, compact, striped, hoverable }, sortable: props.sortable }),
7775
7764
  pagination && /* @__PURE__ */ jsx45(
7776
7765
  KjunPagination,
7777
7766
  {
@@ -7832,12 +7821,13 @@ function KjunKpiHero(props) {
7832
7821
  secondary = [],
7833
7822
  animated = true,
7834
7823
  size = "md",
7824
+ surface = "card",
7835
7825
  aside,
7836
7826
  formatter,
7837
7827
  deltaFormatter,
7838
7828
  renderSecondary
7839
7829
  } = props;
7840
- const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), mobile = useWindowDimensions8().width <= tokens45.responsive.kpiHero, small = size === "sm", numberRole = small ? "displaySm" : mobile ? "displayMd" : "displayLg", font = typeStyle(numberRole).fontSize, unitStyle = typeStyle(small ? "numberSm" : mobile ? "numberMd" : "numberLg");
7830
+ const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), windowWidth = useWindowDimensions8().width, mobile = windowWidth <= tokens45.responsive.kpiHero, small = size === "sm", numberRole = small ? "displaySm" : mobile ? "displayMd" : "displayLg", font = typeStyle(numberRole).fontSize, unitStyle = typeStyle(small ? "numberSm" : mobile ? "numberMd" : "numberLg");
7841
7831
  const geometry2 = tokens45.extensions.kpiHero;
7842
7832
  const spec = small ? geometry2.sm : mobile ? geometry2.mobile : geometry2.md;
7843
7833
  const numeric = {
@@ -7867,10 +7857,13 @@ function KjunKpiHero(props) {
7867
7857
  {
7868
7858
  accessibilityState: { busy: loading },
7869
7859
  style: {
7870
- paddingVertical: spec.paddingY,
7871
- paddingHorizontal: spec.paddingX,
7872
- backgroundColor: colors.surface,
7873
- borderRadius: geometry2.radius
7860
+ // surface="none" sits inside the app's own card or list, so it drops its own card box.
7861
+ ...surface === "none" ? {} : {
7862
+ paddingVertical: spec.paddingY,
7863
+ paddingHorizontal: spec.paddingX,
7864
+ backgroundColor: colors.surface,
7865
+ borderRadius: geometry2.radius
7866
+ }
7874
7867
  },
7875
7868
  children: [
7876
7869
  /* @__PURE__ */ jsxs34(
@@ -7953,70 +7946,75 @@ function KjunKpiHero(props) {
7953
7946
  borderTopWidth: tokens45.border.defaultWidth,
7954
7947
  borderTopColor: colors.border
7955
7948
  },
7956
- children: secondary.map((item, i) => /* @__PURE__ */ jsxs34(
7957
- View40,
7958
- {
7959
- style: mobile && !small ? {
7960
- flex: 1,
7961
- minWidth: 0,
7962
- paddingLeft: i ? geometry2.itemPadding : 0,
7963
- paddingRight: i === secondary.length - 1 ? 0 : geometry2.itemPadding,
7964
- borderLeftWidth: i ? tokens45.border.defaultWidth : 0,
7965
- borderLeftColor: colors.border
7966
- } : { minWidth: 0, maxWidth: "100%", flexShrink: 1 },
7967
- children: [
7968
- /* @__PURE__ */ jsx47(
7969
- KText,
7970
- {
7971
- style: {
7972
- color: colors.textTertiary,
7973
- ...typeStyle("caption"),
7974
- marginBottom: spec.secondaryLabelGap
7975
- },
7976
- children: item.label
7977
- }
7978
- ),
7979
- loading ? /* @__PURE__ */ jsx47(
7980
- KjunSkeleton,
7981
- {
7982
- type: "block",
7983
- height: typeStyle("numberMd").lineHeight,
7984
- width: geometry2.secondarySkeletonWidth
7985
- }
7986
- ) : (renderSecondary == null ? void 0 : renderSecondary(item, i)) || /* @__PURE__ */ jsx47(
7987
- KpiNumberLine,
7988
- {
7989
- prefix: item.prefix,
7990
- suffix: item.suffix,
7991
- prefixGap: geometry2.secondaryAffixGap,
7992
- suffixGap: geometry2.secondaryAffixGap,
7993
- unitStyle: typeStyle("caption"),
7994
- style: { ...numeric, ...typeStyle("numberMd"), color: item.valueColor || (item.semantic === "price" && Number(item.value) !== 0 ? direction(Number(item.value)) : colors.text) },
7995
- children: kpiFormat(item)
7996
- }
7997
- ),
7998
- item.desc && (loading ? /* @__PURE__ */ jsx47(
7999
- KjunSkeleton,
8000
- {
8001
- type: "block",
8002
- width: geometry2.descriptionSkeletonWidth,
8003
- height: typeStyle("caption").lineHeight
8004
- }
8005
- ) : /* @__PURE__ */ jsx47(
8006
- KText,
8007
- {
8008
- style: {
8009
- ...typeStyle("caption"),
8010
- marginTop: small ? geometry2.sm.descriptionGap : geometry2.md.descriptionGap,
8011
- color: item.descColor || colors.textTertiary
8012
- },
8013
- children: item.desc
8014
- }
8015
- ))
8016
- ]
8017
- },
8018
- i
8019
- ))
7949
+ children: secondary.map((item, i) => {
7950
+ const columns = mobile && !small && windowWidth <= tokens45.responsive.kpiHeroSecondary && secondary.length >= 3 ? 2 : 0;
7951
+ const first = columns ? i % columns === 0 : i === 0;
7952
+ const last = columns ? i % columns === columns - 1 || i === secondary.length - 1 : i === secondary.length - 1;
7953
+ return /* @__PURE__ */ jsxs34(
7954
+ View40,
7955
+ {
7956
+ style: mobile && !small ? {
7957
+ ...columns ? { width: `${100 / columns}%`, marginTop: i >= columns ? spec.secondaryPadding : 0 } : { flex: 1 },
7958
+ minWidth: 0,
7959
+ paddingLeft: first ? 0 : geometry2.itemPadding,
7960
+ paddingRight: last ? 0 : geometry2.itemPadding,
7961
+ borderLeftWidth: first ? 0 : tokens45.border.defaultWidth,
7962
+ borderLeftColor: colors.border
7963
+ } : { minWidth: 0, maxWidth: "100%", flexShrink: 1 },
7964
+ children: [
7965
+ /* @__PURE__ */ jsx47(
7966
+ KText,
7967
+ {
7968
+ style: {
7969
+ color: colors.textTertiary,
7970
+ ...typeStyle("caption"),
7971
+ marginBottom: spec.secondaryLabelGap
7972
+ },
7973
+ children: item.label
7974
+ }
7975
+ ),
7976
+ loading ? /* @__PURE__ */ jsx47(
7977
+ KjunSkeleton,
7978
+ {
7979
+ type: "block",
7980
+ height: typeStyle("numberMd").lineHeight,
7981
+ width: geometry2.secondarySkeletonWidth
7982
+ }
7983
+ ) : (renderSecondary == null ? void 0 : renderSecondary(item, i)) || /* @__PURE__ */ jsx47(
7984
+ KpiNumberLine,
7985
+ {
7986
+ prefix: item.prefix,
7987
+ suffix: item.suffix,
7988
+ prefixGap: geometry2.secondaryAffixGap,
7989
+ suffixGap: geometry2.secondaryAffixGap,
7990
+ unitStyle: typeStyle("caption"),
7991
+ style: { ...numeric, ...typeStyle("numberMd"), color: item.valueColor || (item.semantic === "price" && Number(item.value) !== 0 ? direction(Number(item.value)) : colors.text) },
7992
+ children: kpiFormat(item)
7993
+ }
7994
+ ),
7995
+ item.desc && (loading ? /* @__PURE__ */ jsx47(
7996
+ KjunSkeleton,
7997
+ {
7998
+ type: "block",
7999
+ width: geometry2.descriptionSkeletonWidth,
8000
+ height: typeStyle("caption").lineHeight
8001
+ }
8002
+ ) : /* @__PURE__ */ jsx47(
8003
+ KText,
8004
+ {
8005
+ style: {
8006
+ ...typeStyle("caption"),
8007
+ marginTop: small ? geometry2.sm.descriptionGap : geometry2.md.descriptionGap,
8008
+ color: item.descColor || colors.textTertiary
8009
+ },
8010
+ children: item.desc
8011
+ }
8012
+ ))
8013
+ ]
8014
+ },
8015
+ i
8016
+ );
8017
+ })
8020
8018
  }
8021
8019
  )
8022
8020
  ]
@@ -8026,7 +8024,7 @@ function KjunKpiHero(props) {
8026
8024
 
8027
8025
  // packages/native/src/kpi-row.tsx
8028
8026
  import { tokens as tokens46 } from "@kjun-ui/tokens";
8029
- import { useState as useState35 } from "react";
8027
+ import { useState as useState37 } from "react";
8030
8028
  import {
8031
8029
  Text as Text6,
8032
8030
  View as View41,
@@ -8037,6 +8035,7 @@ function KjunKpiRow({
8037
8035
  loading = false,
8038
8036
  items,
8039
8037
  size = "md",
8038
+ surface = "card",
8040
8039
  mobileSummary = true,
8041
8040
  onItemClick,
8042
8041
  renderValue
@@ -8044,7 +8043,7 @@ function KjunKpiRow({
8044
8043
  const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), mobile = useWindowDimensions9().width <= tokens46.responsive.kpiRow, small = size === "sm", summary = mobileSummary && mobile && !small;
8045
8044
  const geometry2 = tokens46.extensions.kpiRow;
8046
8045
  const spec = small ? geometry2.sm : mobile ? geometry2.mobile : geometry2.md;
8047
- const [width, setWidth] = useState35(0);
8046
+ const [width, setWidth] = useState37(0);
8048
8047
  const paddingX = summary ? geometry2.summary.paddingX : spec.paddingX;
8049
8048
  const gapX = summary ? geometry2.summary.gapX : 0;
8050
8049
  const explicit = items.some(
@@ -8085,10 +8084,13 @@ function KjunKpiRow({
8085
8084
  style: {
8086
8085
  flexDirection: "row",
8087
8086
  flexWrap: mobile ? "wrap" : "nowrap",
8088
- paddingVertical: summary ? geometry2.summary.paddingY : spec.paddingY,
8089
- paddingHorizontal: paddingX,
8090
- borderRadius: geometry2.radius,
8091
- backgroundColor: colors.surface,
8087
+ // surface="none" sits inside the app's own card or list, so it drops its own card box.
8088
+ ...surface === "none" ? {} : {
8089
+ paddingVertical: summary ? geometry2.summary.paddingY : spec.paddingY,
8090
+ paddingHorizontal: paddingX,
8091
+ borderRadius: geometry2.radius,
8092
+ backgroundColor: colors.surface
8093
+ },
8092
8094
  rowGap: summary ? geometry2.summary.gapY : 0,
8093
8095
  columnGap: gapX
8094
8096
  },
@@ -8260,7 +8262,7 @@ import { tokens as tokens48 } from "@kjun-ui/tokens";
8260
8262
  import { ScrollView as ScrollView10, View as View43 } from "react-native";
8261
8263
 
8262
8264
  // shared/package-runtime/market-cards.ts
8263
- import { useEffect as useEffect24, useRef as useRef32, useState as useState36 } from "react";
8265
+ import { useEffect as useEffect25, useRef as useRef32, useState as useState38 } from "react";
8264
8266
  function useMarketCardsModel(props, persistence) {
8265
8267
  const {
8266
8268
  columns,
@@ -8276,7 +8278,7 @@ function useMarketCardsModel(props, persistence) {
8276
8278
  sortKey: c.sortable ? metricConfig[c.key].sortKey : null,
8277
8279
  isDefaultSort: c.defaultSort
8278
8280
  }));
8279
- const [key, setKey] = useState36(""), restoreSignature = JSON.stringify([storageNamespace, pills]);
8281
+ const [key, setKey] = useState38(""), restoreSignature = JSON.stringify([storageNamespace, pills]);
8280
8282
  const mounted = useRef32("");
8281
8283
  const save = (key2) => {
8282
8284
  var _a;
@@ -8285,7 +8287,7 @@ function useMarketCardsModel(props, persistence) {
8285
8287
  } catch (e) {
8286
8288
  }
8287
8289
  };
8288
- useEffect24(() => {
8290
+ useEffect25(() => {
8289
8291
  var _a, _b;
8290
8292
  if (mounted.current === restoreSignature) return;
8291
8293
  mounted.current = restoreSignature;
@@ -8527,7 +8529,7 @@ function KjunMarketCards(props) {
8527
8529
  ...props,
8528
8530
  loadingPadding: "none",
8529
8531
  empty: !rows.length,
8530
- loadingContent: /* @__PURE__ */ jsx50(KjunListSkeleton, { variant: "compact" }),
8532
+ loadingContent: props.skeleton === false ? void 0 : /* @__PURE__ */ jsx50(KjunListSkeleton, { variant: "compact" }),
8531
8533
  emptyContent: /* @__PURE__ */ jsx50(KjunEmpty, { text: emptyMessage, description: emptySubMessage }),
8532
8534
  children: /* @__PURE__ */ jsxs37(View43, { children: [
8533
8535
  state.pill && /* @__PURE__ */ jsx50(KText, { style: { textAlign: "right", paddingHorizontal: tokens48.dimension.value16, paddingVertical: tokens48.dimension.value8, ...typeStyle("caption"), color: colors.textSecondary }, children: state.pill.label }),
@@ -8638,7 +8640,7 @@ function KjunMarketSimpleList(props) {
8638
8640
  loadingPadding: "none",
8639
8641
  empty: !rows.length,
8640
8642
  emptyText: emptyMessage,
8641
- loadingContent: /* @__PURE__ */ jsx51(KjunListSkeleton, {}),
8643
+ loadingContent: props.skeleton === false ? void 0 : /* @__PURE__ */ jsx51(KjunListSkeleton, {}),
8642
8644
  emptyContent: /* @__PURE__ */ jsx51(KjunEmpty, { text: emptyMessage, description: emptySubMessage }),
8643
8645
  children: /* @__PURE__ */ jsx51(View44, { children: rows.map((row, i) => {
8644
8646
  var _a, _b;
@@ -8765,9 +8767,9 @@ function KjunMarketListPanel({
8765
8767
 
8766
8768
  // packages/native/src/market-table.tsx
8767
8769
  import { tokens as tokens51 } from "@kjun-ui/tokens";
8768
- import { useId as useId10, useState as useState37 } from "react";
8770
+ import { useId as useId10, useState as useState39 } from "react";
8769
8771
  import { ScrollView as ScrollView11, View as View46 } from "react-native";
8770
- import Svg8, { Defs as Defs4, LinearGradient as LinearGradient4, Rect as Rect5, Stop as Stop4 } from "react-native-svg";
8772
+ import Svg8, { Defs as Defs4, LinearGradient as LinearGradient4, Rect as Rect4, Stop as Stop4 } from "react-native-svg";
8771
8773
  import { jsx as jsx53, jsxs as jsxs40 } from "react/jsx-runtime";
8772
8774
  function KjunMarketTable(props) {
8773
8775
  const {
@@ -8851,7 +8853,7 @@ function KjunMarketTable(props) {
8851
8853
  );
8852
8854
  };
8853
8855
  const layout = useMarketLayout(columns, showActions);
8854
- const [scroll, setScroll] = useState37({ viewport: 0, x: 0 }), fadeId = "kjun-market-fade-" + useId10().replace(/[^a-z0-9]/gi, "");
8856
+ const [scroll, setScroll] = useState39({ viewport: 0, x: 0 }), fadeId = "kjun-market-fade-" + useId10().replace(/[^a-z0-9]/gi, "");
8855
8857
  const tableQuote = { price: {}, change: { fontWeight: typeStyle("body").fontWeight } };
8856
8858
  return /* @__PURE__ */ jsxs40(
8857
8859
  View46,
@@ -8869,7 +8871,7 @@ function KjunMarketTable(props) {
8869
8871
  loadingPadding: "none",
8870
8872
  empty: !rows.length,
8871
8873
  emptyText: emptyMessage,
8872
- loadingContent: /* @__PURE__ */ jsx53(KjunMarketTableSkeleton, { columns, showActions }),
8874
+ loadingContent: props.skeleton === false ? void 0 : /* @__PURE__ */ jsx53(KjunMarketTableSkeleton, { columns, showActions }),
8873
8875
  emptyContent: /* @__PURE__ */ jsx53(KjunEmpty, { text: emptyMessage, description: emptySubMessage }),
8874
8876
  children: /* @__PURE__ */ jsx53(MarketQuoteTypeContext.Provider, { value: tableQuote, children: /* @__PURE__ */ jsxs40(View46, { style: { position: "relative", minWidth: 0 }, children: [
8875
8877
  /* @__PURE__ */ jsx53(
@@ -9061,7 +9063,7 @@ function KjunMarketTable(props) {
9061
9063
  /* @__PURE__ */ jsx53(Stop4, { offset: "0", stopColor: colors.surface, stopOpacity: 0 }),
9062
9064
  /* @__PURE__ */ jsx53(Stop4, { offset: "1", stopColor: colors.surface, stopOpacity: 1 })
9063
9065
  ] }) }),
9064
- /* @__PURE__ */ jsx53(Rect5, { width: "100%", height: "100%", fill: `url(#${fadeId})` })
9066
+ /* @__PURE__ */ jsx53(Rect4, { width: "100%", height: "100%", fill: `url(#${fadeId})` })
9065
9067
  ] }) })
9066
9068
  ] }) })
9067
9069
  }
@@ -9075,7 +9077,7 @@ function KjunMarketTable(props) {
9075
9077
  // packages/native/src/list.tsx
9076
9078
  import { tokens as tokens52 } from "@kjun-ui/tokens";
9077
9079
  import { Children as Children4, createContext as createContext20, useContext as useContext17 } from "react";
9078
- import { View as View47, Platform as Platform24 } from "react-native";
9080
+ import { View as View47, Platform as Platform25 } from "react-native";
9079
9081
  import { Fragment as Fragment18, jsx as jsx54, jsxs as jsxs41 } from "react/jsx-runtime";
9080
9082
  var LastListRow = createContext20(false);
9081
9083
  function KjunListRow({ title, description, leading, trailing, actions, href, disabled = false, onPress }) {
@@ -9090,8 +9092,8 @@ function KjunListRow({ title, description, leading, trailing, actions, href, dis
9090
9092
  ] });
9091
9093
  const style = { flex: 1, minWidth: 0, minHeight: tokens52.extensions.list.minimumHeight, flexDirection: "row", alignItems: "center", gap: tokens52.extensions.list.gap, padding: tokens52.extensions.list.padding };
9092
9094
  return /* @__PURE__ */ jsxs41(View47, { role: "listitem", style: { flexDirection: "row", alignItems: "center", gap: tokens52.extensions.list.gap, borderBottomWidth: last ? 0 : tokens52.border.defaultWidth, borderBottomColor: colors.divider }, children: [
9093
- onPress || href ? /* @__PURE__ */ jsx54(AccessiblePressable, { ...Platform24.OS === "web" && href && !disabled ? { href } : {}, accessibilityRole: href ? "link" : "button", accessibilityLabel: title + (description ? ", " + description : ""), disabled, accessibilityState: { disabled }, onPress, style: ({ pressed }) => ({ ...style, opacity: disabled ? tokens52.states.opacity.disabled : 1, backgroundColor: pressed ? colors.hover : "transparent" }), children: parts }) : /* @__PURE__ */ jsx54(View47, { style, children: parts }),
9094
- actions && /* @__PURE__ */ jsx54(View47, { pointerEvents: disabled ? "none" : "auto", ...disabled && Platform24.OS === "web" ? { inert: true } : {}, style: { maxWidth: "45%", flexDirection: "row", flexWrap: "wrap", gap: tokens52.extensions.list.actionsGap, paddingRight: tokens52.extensions.list.actionsPadding, opacity: disabled ? tokens52.states.opacity.disabled : 1 }, children: content(actions) })
9095
+ onPress || href ? /* @__PURE__ */ jsx54(AccessiblePressable, { ...Platform25.OS === "web" && href && !disabled ? { href } : {}, accessibilityRole: href ? "link" : "button", accessibilityLabel: title + (description ? ", " + description : ""), disabled, accessibilityState: { disabled }, onPress, style: ({ pressed }) => ({ ...style, opacity: disabled ? tokens52.states.opacity.disabled : 1, backgroundColor: pressed ? colors.hover : "transparent" }), children: parts }) : /* @__PURE__ */ jsx54(View47, { style, children: parts }),
9096
+ actions && /* @__PURE__ */ jsx54(View47, { pointerEvents: disabled ? "none" : "auto", ...disabled && Platform25.OS === "web" ? { inert: true } : {}, style: { maxWidth: "45%", flexDirection: "row", flexWrap: "wrap", gap: tokens52.extensions.list.actionsGap, paddingRight: tokens52.extensions.list.actionsPadding, opacity: disabled ? tokens52.states.opacity.disabled : 1 }, children: content(actions) })
9095
9097
  ] });
9096
9098
  }
9097
9099
  function KjunListSection({ title, description, actions, children, ariaLabel }) {
@@ -9110,16 +9112,16 @@ function KjunListSection({ title, description, actions, children, ariaLabel }) {
9110
9112
 
9111
9113
  // packages/native/src/app-navigation.tsx
9112
9114
  import { tokens as tokens53 } from "@kjun-ui/tokens";
9113
- import { Children as Children5, Fragment as Fragment19, isValidElement as isValidElement7, useState as useState38 } from "react";
9114
- import { View as View48, Platform as Platform25 } from "react-native";
9115
+ import { Children as Children5, Fragment as Fragment19, isValidElement as isValidElement7, useState as useState40 } from "react";
9116
+ import { View as View48, Platform as Platform26 } from "react-native";
9115
9117
  import { jsx as jsx55, jsxs as jsxs42 } from "react/jsx-runtime";
9116
9118
  function KjunTopNavigation({ title, description, leading, actions, safeAreaTop = 0 }) {
9117
9119
  const { colors } = useKjunStyles();
9118
9120
  const geometry2 = tokens53.extensions.topNavigation;
9119
9121
  const titleInset = Math.max(0, (geometry2.controlSize - tokens53.typography.sectionTitle.lineHeightPx) / 2);
9120
- const wrapping = Platform25.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : {};
9122
+ const wrapping = Platform26.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : {};
9121
9123
  const edgeInset = (geometry2.controlSize - geometry2.iconSize) / 2;
9122
- const [iconEdges, setIconEdges] = useState38({ leading: false, trailing: false });
9124
+ const [iconEdges, setIconEdges] = useState40({ leading: false, trailing: false });
9123
9125
  const square = (key) => (event) => {
9124
9126
  const { width, height } = event.nativeEvent.layout;
9125
9127
  const next = Math.abs(width - height) < 1 && Math.abs(height - geometry2.controlSize) < 1;
@@ -9164,8 +9166,8 @@ function KjunBottomNavigation({ items, value, ariaLabel = "\uC8FC\uC694 \uD0D0\u
9164
9166
  AccessiblePressable,
9165
9167
  {
9166
9168
  focusRingInset: true,
9167
- ...Platform25.OS === "web" ? { href: item.disabled ? void 0 : item.href, "aria-current": selected ? "page" : void 0 } : {},
9168
- accessibilityRole: Platform25.OS === "web" && item.href ? "link" : "button",
9169
+ ...Platform26.OS === "web" ? { href: item.disabled ? void 0 : item.href, "aria-current": selected ? "page" : void 0 } : {},
9170
+ accessibilityRole: Platform26.OS === "web" && item.href ? "link" : "button",
9169
9171
  accessibilityLabel: item.label + (item.badge != null ? ", " + item.badge : ""),
9170
9172
  accessibilityState: { selected, disabled: !!item.disabled },
9171
9173
  disabled: item.disabled,
@@ -9199,10 +9201,10 @@ function KjunBottomNavigation({ items, value, ariaLabel = "\uC8FC\uC694 \uD0D0\u
9199
9201
  }
9200
9202
  function KjunBottomActionBar({ description, children, safeAreaBottom = 0, keyboardVisible = false, hideOnKeyboard = false }) {
9201
9203
  const { colors } = useKjunStyles();
9202
- const [width, setWidth] = useState38(0);
9204
+ const [width, setWidth] = useState40(0);
9203
9205
  const geometry2 = tokens53.extensions.navigation;
9204
9206
  const wide = width >= tokens53.responsive.bottomAction + geometry2.padding * 2;
9205
- const wrapping = Platform25.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : {};
9207
+ const wrapping = Platform26.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : {};
9206
9208
  if (keyboardVisible && hideOnKeyboard) return null;
9207
9209
  return /* @__PURE__ */ jsx55(BottomActionBarContext.Provider, { value: !wide, children: /* @__PURE__ */ jsxs42(View48, { onLayout: (event) => setWidth(event.nativeEvent.layout.width), style: {
9208
9210
  flexShrink: 0,
@@ -9221,7 +9223,7 @@ function KjunBottomActionBar({ description, children, safeAreaBottom = 0, keyboa
9221
9223
  }
9222
9224
 
9223
9225
  // packages/native/src/media.tsx
9224
- import { useRef as useRef33, useState as useState39 } from "react";
9226
+ import { useRef as useRef33, useState as useState41 } from "react";
9225
9227
  import { Image, View as View49 } from "react-native";
9226
9228
  import { tokens as tokens54 } from "@kjun-ui/tokens";
9227
9229
  import { jsx as jsx56 } from "react/jsx-runtime";
@@ -9230,7 +9232,7 @@ function KjunImage({ src, source, alt, decorative = false, aspectRatio = 1, fit
9230
9232
  const request = JSON.stringify(source != null ? source : src), current = useRef33({ request, version: 0 });
9231
9233
  if (current.current.request !== request) current.current = { request, version: current.current.version + 1 };
9232
9234
  const version = current.current.version;
9233
- const [failed, setFailed] = useState39(null);
9235
+ const [failed, setFailed] = useState41(null);
9234
9236
  const missing = !source && !src;
9235
9237
  return /* @__PURE__ */ jsx56(View49, { accessibilityElementsHidden: decorative, importantForAccessibility: decorative ? "no-hide-descendants" : "auto", "aria-hidden": decorative || void 0, style: { width: "100%", aspectRatio: aspectRatio > 0 ? aspectRatio : 1, overflow: "hidden", backgroundColor: colors.secondary }, children: !missing && failed !== version ? /* @__PURE__ */ jsx56(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: () => {
9236
9238
  if (version === current.current.version) onLoad == null ? void 0 : onLoad();
@@ -9260,8 +9262,8 @@ function KjunChip({ label, icon, removable = false, disabled = false, size = "md
9260
9262
  }
9261
9263
 
9262
9264
  // packages/native/src/range-input.tsx
9263
- import { useEffect as useEffect25, useMemo as useMemo2, useRef as useRef34, useState as useState40 } from "react";
9264
- import { PanResponder, Platform as Platform26, Pressable as Pressable3, View as View51 } from "react-native";
9265
+ import { useEffect as useEffect26, useMemo as useMemo2, useRef as useRef34, useState as useState42 } from "react";
9266
+ import { PanResponder, Platform as Platform27, Pressable as Pressable3, View as View51 } from "react-native";
9265
9267
  import { tokens as tokens56, createSliderDomain, sliderKey } from "@kjun-ui/tokens";
9266
9268
  import { jsx as jsx58, jsxs as jsxs44 } from "react/jsx-runtime";
9267
9269
  var geometry = tokens56.extensions.slider;
@@ -9284,18 +9286,18 @@ function RangeInput({
9284
9286
  const valid = domain.valid && (!Array.isArray(value) || value.length === 2) && values.every(domain.accepts) && (values.length === 1 || values[0] <= values[1]);
9285
9287
  const blocked = disabled || !valid;
9286
9288
  const effectiveMax = valid ? domain.snap(max) : min;
9287
- const [hovered, setHovered] = useState40(null);
9288
- const [dragging, setDragging] = useState40(null);
9289
- const [focused, setFocused] = useState40(null);
9289
+ const [hovered, setHovered] = useState42(null);
9290
+ const [dragging, setDragging] = useState42(null);
9291
+ const [focused, setFocused] = useState42(null);
9290
9292
  const track = useRef34(null), measured = useRef34({ x: 0, width: 1 }), selected = useRef34(0), dirty = useRef34(false);
9291
9293
  const latest = useRef34(values);
9292
9294
  latest.current = values;
9293
9295
  const callbacks = useRef34({ onValueChange, onChangeCommit });
9294
9296
  callbacks.current = { onValueChange, onChangeCommit };
9295
- useEffect25(() => {
9297
+ useEffect26(() => {
9296
9298
  if (!valid) console.warn("KJUN Slider: \uBC94\uC704\xB7step\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
9297
9299
  }, [valid]);
9298
- useEffect25(() => {
9300
+ useEffect26(() => {
9299
9301
  if (blocked) {
9300
9302
  setDragging(null);
9301
9303
  setHovered(null);
@@ -9492,7 +9494,7 @@ function RangeInput({
9492
9494
  setFocused(null);
9493
9495
  commit();
9494
9496
  },
9495
- ...Platform26.OS === "web" ? {
9497
+ ...Platform27.OS === "web" ? {
9496
9498
  "aria-valuenow": number,
9497
9499
  "aria-valuemin": index === 1 ? values[0] : min,
9498
9500
  "aria-valuemax": values.length === 2 && index === 0 ? values[1] : domain.snap(max),
@@ -9516,7 +9518,7 @@ function RangeInput({
9516
9518
  alignItems: "center",
9517
9519
  justifyContent: "center",
9518
9520
  zIndex: focused === index || dragging === index ? 1 : 0,
9519
- ...Platform26.OS === "web" ? {
9521
+ ...Platform27.OS === "web" ? {
9520
9522
  outlineStyle: "solid",
9521
9523
  outlineWidth: 0,
9522
9524
  cursor: blocked ? "default" : dragging === index ? "grabbing" : "grab"
@@ -9533,7 +9535,7 @@ function RangeInput({
9533
9535
  borderWidth: tokens56.border.controlWidth,
9534
9536
  borderColor: !blocked && dragging === index ? colors.brandActive : !blocked && hovered === index ? colors.brandHover : colors.brand,
9535
9537
  backgroundColor: colors.surface,
9536
- ...Platform26.OS === "web" && !blocked && focused === index ? {
9538
+ ...Platform27.OS === "web" && !blocked && focused === index ? {
9537
9539
  outlineStyle: "solid",
9538
9540
  outlineWidth: tokens56.states.focus.width,
9539
9541
  outlineOffset: tokens56.states.focus.choiceOffset,
@@ -9560,8 +9562,8 @@ function KjunRangeSlider(props) {
9560
9562
  }
9561
9563
 
9562
9564
  // packages/native/src/quantity-stepper.tsx
9563
- import { useEffect as useEffect26, useRef as useRef35, useState as useState41 } from "react";
9564
- import { Platform as Platform27, View as View52 } from "react-native";
9565
+ import { useEffect as useEffect27, useRef as useRef35, useState as useState43 } from "react";
9566
+ import { Platform as Platform28, View as View52 } from "react-native";
9565
9567
  import { createNumberDomain, quantityKeyAction, tokens as tokens57 } from "@kjun-ui/tokens";
9566
9568
 
9567
9569
  // shared/package-runtime/component-icons.ts
@@ -9582,16 +9584,16 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9582
9584
  const increaseStyle = { ...buttonStyle, borderRadius: 0, borderTopRightRadius: outer, borderBottomRightRadius: outer };
9583
9585
  const domain = createNumberDomain({ min, max, step, precision }), valid = domain.valid && domain.accepts(value), blocked = disabled || !valid;
9584
9586
  const surface = useFieldSurface(size, { disabled: blocked, invalid });
9585
- const [draft, setDraft] = useState41(String(value));
9587
+ const [draft, setDraft] = useState43(String(value));
9586
9588
  const inputRef = useRef35(null);
9587
9589
  const composing = useRef35(false), dirty = useRef35(false), accepted = useRef35(value);
9588
9590
  const cancelledEscape = useRef35(false);
9589
- useEffect26(() => {
9591
+ useEffect27(() => {
9590
9592
  setDraft(String(value));
9591
9593
  accepted.current = value;
9592
9594
  dirty.current = false;
9593
9595
  }, [value, precision, min, max, step]);
9594
- useEffect26(() => {
9596
+ useEffect27(() => {
9595
9597
  if (!valid) console.warn("KJUN QuantityStepper: \uBC94\uC704\xB7step\xB7\uC815\uBC00\uB3C4\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
9596
9598
  }, [valid]);
9597
9599
  const emit = (next) => {
@@ -9636,8 +9638,8 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9636
9638
  dirty.current = true;
9637
9639
  setDraft(text);
9638
9640
  } };
9639
- useEffect26(() => {
9640
- if (Platform27.OS !== "web") return;
9641
+ useEffect27(() => {
9642
+ if (Platform28.OS !== "web") return;
9641
9643
  const input = inputRef.current;
9642
9644
  const keydown = (event) => handlers.current.key(event);
9643
9645
  const keyup = (event) => {
@@ -9666,7 +9668,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9666
9668
  };
9667
9669
  }, []);
9668
9670
  const surfaceBox = { position: "absolute", left: 0, right: 0, top: "50%", height: tokens57.input[size].height, marginTop: -tokens57.input[size].height / 2, borderRadius: surface.spec.radius };
9669
- const hug = Platform27.OS === "web" && !block ? { width: "fit-content" } : {};
9671
+ const hug = Platform28.OS === "web" && !block ? { width: "fit-content" } : {};
9670
9672
  return /* @__PURE__ */ jsx59(IconFallbacks, { icons: componentIcons, children: /* @__PURE__ */ jsxs45(View52, { style: { flexDirection: "row", alignItems: "center", alignSelf: block ? "stretch" : "flex-start", maxWidth: "100%", borderRadius: surface.spec.radius, ...hug }, children: [
9671
9673
  /* @__PURE__ */ jsx59(View52, { pointerEvents: "none", "aria-hidden": true, style: { ...surfaceBox, backgroundColor: colors.inputBg } }),
9672
9674
  /* @__PURE__ */ jsx59(CompoundTarget, { square: true, children: /* @__PURE__ */ jsx59(KjunButton, { size, style: decreaseStyle, variant: "ghost", ariaLabel: "\uC218\uB7C9 \uC904\uC774\uAE30", disabled: blocked || value <= min, onPress: () => move(-1), prefixIcon: "minus" }) }),
@@ -9697,8 +9699,8 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9697
9699
  surface.setFocused(false);
9698
9700
  commit();
9699
9701
  },
9700
- onSubmitEditing: Platform27.OS === "web" ? void 0 : commit,
9701
- ...Platform27.OS === "web" ? { "aria-valuemin": min, "aria-valuemax": max, "aria-valuenow": value, "aria-labelledby": labelledBy, "aria-describedby": field == null ? void 0 : field.describedBy, "aria-invalid": invalid || void 0, "aria-required": (field == null ? void 0 : field.required) || void 0 } : { onKeyPress: key },
9702
+ onSubmitEditing: Platform28.OS === "web" ? void 0 : commit,
9703
+ ...Platform28.OS === "web" ? { "aria-valuemin": min, "aria-valuemax": max, "aria-valuenow": value, "aria-labelledby": labelledBy, "aria-describedby": field == null ? void 0 : field.describedBy, "aria-invalid": invalid || void 0, "aria-required": (field == null ? void 0 : field.required) || void 0 } : { onKeyPress: key },
9702
9704
  style: { width: "100%", minWidth: 0, ...surface.style, borderRadius: 0, borderTopColor: "transparent", borderBottomColor: "transparent", borderLeftColor: "transparent", borderRightColor: "transparent", backgroundColor: !blocked && (surface.active || surface.hovered) ? colors.hover : "transparent", outlineWidth: 0, color: surface.ink, fontFamily, ...inputTypeStyle(size), textAlign: "center", paddingHorizontal: surface.spec.padding - tokens57.border.controlWidth, paddingVertical: 0 }
9703
9705
  }
9704
9706
  ) }),
@@ -9708,7 +9710,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9708
9710
  }
9709
9711
 
9710
9712
  // packages/native/src/time-picker.tsx
9711
- import { Fragment as Fragment20, useEffect as useEffect27, useId as useId11, useMemo as useMemo3, useRef as useRef36, useState as useState42 } from "react";
9713
+ import { Fragment as Fragment20, useEffect as useEffect28, useId as useId11, useMemo as useMemo3, useRef as useRef36, useState as useState44 } from "react";
9712
9714
  import { View as View53 } from "react-native";
9713
9715
  import { createTimeDomain, tokens as tokens58 } from "@kjun-ui/tokens";
9714
9716
  import { jsx as jsx60, jsxs as jsxs46 } from "react/jsx-runtime";
@@ -9716,11 +9718,11 @@ function KjunTimePicker({ value, precision = "minute", min, max, minuteStep = 1,
9716
9718
  const geometry2 = tokens58.extensions.timePicker, spec = tokens58.input[size];
9717
9719
  const { colors } = useKjunStyles();
9718
9720
  const first = useRef36(null);
9719
- const [focused, setFocused] = useState42(false), [opened, setOpened] = useState42(null);
9721
+ const [focused, setFocused] = useState44(false), [opened, setOpened] = useState44(null);
9720
9722
  const field = useKjunField(), uid = useId11();
9721
9723
  const fieldId = (field == null ? void 0 : field.id) || `kjun-time-${uid}`;
9722
9724
  const domain = useMemo3(() => createTimeDomain({ precision, min, max, minuteStep, secondStep }), [precision, min, max, minuteStep, secondStep]), valid = domain.valid && domain.accepts(value);
9723
- useEffect27(() => {
9725
+ useEffect28(() => {
9724
9726
  if (!valid) console.warn("KJUN TimePicker: \uC2DC\uAC01 \uBC94\uC704\xB7\uAC04\uACA9\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
9725
9727
  }, [valid]);
9726
9728
  const change = (next) => {