@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
@@ -2417,36 +2417,6 @@ function KjunSkeleton({
2417
2417
  ))
2418
2418
  }
2419
2419
  ),
2420
- type === "chart" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native16.View, { style: [frame, { height: height || import_tokens13.tokens.extensions.skeleton.chartHeight }], children: [
2421
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2422
- import_react_native16.View,
2423
- {
2424
- style: {
2425
- flex: 1,
2426
- flexDirection: "row",
2427
- alignItems: "flex-end",
2428
- justifyContent: "space-around",
2429
- gap: import_tokens13.tokens.extensions.skeleton.gap
2430
- // Bars stand on the axis line, like ChartSkeleton.
2431
- },
2432
- children: [40, 65, 50, 80, 60, 90, 75].map((h, i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2433
- import_react_native16.Animated.View,
2434
- {
2435
- style: {
2436
- flex: 1,
2437
- maxWidth: import_tokens13.tokens.extensions.skeleton.chartBarMaxWidth,
2438
- height: `${h}%`,
2439
- backgroundColor: shimmer,
2440
- borderTopLeftRadius: import_tokens13.tokens.radius.radius4,
2441
- borderTopRightRadius: import_tokens13.tokens.radius.radius4
2442
- }
2443
- },
2444
- i
2445
- ))
2446
- }
2447
- ),
2448
- block("100%", import_tokens13.tokens.extensions.skeleton.chartAxisHeight, { backgroundColor: colors.border })
2449
- ] }),
2450
2420
  type === "stat" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
2451
2421
  import_react_native16.View,
2452
2422
  {
@@ -6337,12 +6307,27 @@ function KjunScrollFade({ children }) {
6337
6307
 
6338
6308
  // packages/native/src/data-state.tsx
6339
6309
  var import_tokens36 = require("@kjun-ui/tokens");
6340
- var import_react54 = require("react");
6310
+ var import_react55 = require("react");
6341
6311
  var import_react_native40 = require("react-native");
6312
+
6313
+ // shared/package-runtime/use-loading-reveal.ts
6314
+ var import_react54 = require("react");
6315
+ function useLoadingReveal(active, delay = 0) {
6316
+ const [revealed, setRevealed] = (0, import_react54.useState)(false);
6317
+ (0, import_react54.useEffect)(() => {
6318
+ setRevealed(false);
6319
+ if (!active || delay <= 0) return;
6320
+ const timer = setTimeout(() => setRevealed(true), delay);
6321
+ return () => clearTimeout(timer);
6322
+ }, [active, delay]);
6323
+ return delay <= 0 || revealed;
6324
+ }
6325
+
6326
+ // packages/native/src/data-state.tsx
6342
6327
  var import_jsx_runtime37 = require("react/jsx-runtime");
6343
6328
  function useQueryDisplay(props) {
6344
6329
  var _a;
6345
- const [state, setState] = (0, import_react54.useState)(() => (0, import_tokens36.initialQueryDisplay)(props));
6330
+ const [state, setState] = (0, import_react55.useState)(() => (0, import_tokens36.initialQueryDisplay)(props));
6346
6331
  let next = state;
6347
6332
  if (state.queryKey !== ((_a = props.queryKey) != null ? _a : null) || state.resultKey !== props.resultKey || state.loading !== !!props.loading) {
6348
6333
  next = (0, import_tokens36.advanceQueryDisplay)(state, props);
@@ -6361,6 +6346,7 @@ function KjunDataState({
6361
6346
  emptyIcon,
6362
6347
  emptyActionText,
6363
6348
  loadingText = "\uB85C\uB529 \uC911...",
6349
+ loadingDelay = 0,
6364
6350
  retryText = "\uB2E4\uC2DC \uC2DC\uB3C4",
6365
6351
  size = "md",
6366
6352
  skeleton = false,
@@ -6374,7 +6360,7 @@ function KjunDataState({
6374
6360
  onEmptyAction,
6375
6361
  ...query
6376
6362
  }) {
6377
- 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 = import_tokens36.tokens.extensions.state.padding[size];
6363
+ 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 = import_tokens36.tokens.extensions.state.padding[size], revealed = useLoadingReveal(state.initialLoading, loadingDelay);
6378
6364
  const { colors } = useKjunStyles();
6379
6365
  return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
6380
6366
  import_react_native40.View,
@@ -6407,8 +6393,10 @@ function KjunDataState({
6407
6393
  state.initialLoading ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
6408
6394
  import_react_native40.View,
6409
6395
  {
6410
- accessibilityLabel: loadingText,
6411
- style: { paddingVertical: loadingPadding === "none" ? 0 : padding },
6396
+ accessibilityLabel: revealed ? loadingText : void 0,
6397
+ "aria-hidden": revealed ? void 0 : true,
6398
+ importantForAccessibility: revealed ? void 0 : "no-hide-descendants",
6399
+ style: { paddingVertical: loadingPadding === "none" ? 0 : padding, opacity: revealed ? void 0 : 0 },
6412
6400
  children: loadingContent || (skeleton ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_react_native40.View, { style: { gap: import_tokens36.tokens.dimension.value12 }, children: Array.from({ length: skeletonCount }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(KjunSkeleton, { type: skeletonType }, i)) }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_react_native40.View, { style: { alignItems: "center", gap: import_tokens36.tokens.dimension.value12 }, children: [
6413
6401
  /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(KjunSpinner, { size: "md" }),
6414
6402
  /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(KText, { style: { ...typeStyle("body"), color: colors.textSecondary, textAlign: "center", maxWidth: "100%" }, children: loadingText })
@@ -6442,7 +6430,7 @@ function BoundaryDescription() {
6442
6430
  const { colors } = useKjunStyles();
6443
6431
  return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(KText, { style: { ...typeStyle({ ...import_tokens36.tokens.typography.caption, fontSizePx: import_tokens36.tokens.extensions.state.descriptionSize }), color: colors.textSecondary, textAlign: "center" }, children: "\uB2E4\uC2DC \uC2DC\uB3C4\uD558\uBA74 \uBB38\uC81C\uAC00 \uD574\uACB0\uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4." });
6444
6432
  }
6445
- var KjunErrorBoundary = class extends import_react54.Component {
6433
+ var KjunErrorBoundary = class extends import_react55.Component {
6446
6434
  constructor() {
6447
6435
  super(...arguments);
6448
6436
  this.state = { error: false };
@@ -6489,10 +6477,24 @@ var KjunErrorBoundary = class extends import_react54.Component {
6489
6477
 
6490
6478
  // packages/native/src/skeletons.tsx
6491
6479
  var import_tokens40 = require("@kjun-ui/tokens");
6492
- var import_react56 = require("react");
6480
+ var import_react57 = require("react");
6493
6481
  var import_react_native42 = require("react-native");
6494
6482
  var import_react_native_svg5 = __toESM(require("react-native-svg"), 1);
6495
6483
 
6484
+ // shared/package-runtime/chart-skeleton.ts
6485
+ var chartFrameViewBox = "0 0 600 240";
6486
+ var chartFramePath = "M20 10V220H590 M20 65H590 M20 120H590 M20 175H590";
6487
+ var chartSkeletonMaxCount = 100;
6488
+ function chartSkeletonCount(value) {
6489
+ return Number.isFinite(value) ? Math.min(chartSkeletonMaxCount, Math.max(1, Math.floor(value))) : 1;
6490
+ }
6491
+ function chartMatrixMuted(row, column) {
6492
+ return (row + column + 2) % 3 === 0;
6493
+ }
6494
+ function chartDonutHole(height, thickness) {
6495
+ return Math.max(0, height * 0.8 - 2 * thickness);
6496
+ }
6497
+
6496
6498
  // packages/native/src/market-skeleton.tsx
6497
6499
  var import_tokens39 = require("@kjun-ui/tokens");
6498
6500
  var import_react_native41 = require("react-native");
@@ -6518,9 +6520,9 @@ function nativeMarketLayout(columns, showActions, containerWidth) {
6518
6520
 
6519
6521
  // packages/native/src/market-layout.ts
6520
6522
  var import_tokens38 = require("@kjun-ui/tokens");
6521
- var import_react55 = require("react");
6523
+ var import_react56 = require("react");
6522
6524
  function useMarketLayout(columns, showActions) {
6523
- const [containerWidth, setContainerWidth] = (0, import_react55.useState)(0);
6525
+ const [containerWidth, setContainerWidth] = (0, import_react56.useState)(0);
6524
6526
  const onLayout = (event) => setContainerWidth(event.nativeEvent.layout.width);
6525
6527
  return { ...nativeMarketLayout(columns, showActions, containerWidth), onLayout };
6526
6528
  }
@@ -6650,26 +6652,23 @@ function KjunFormSkeleton({
6650
6652
  }
6651
6653
  );
6652
6654
  }
6655
+ var chartPart = (name) => import_react_native42.Platform.OS === "web" ? { dataSet: { kjunChartPart: name } } : {};
6653
6656
  function KjunChartSkeleton({
6657
+ kind = "frame",
6658
+ height = import_tokens40.tokens.extensions.chartSkeleton.height,
6654
6659
  loadingText = "\uCC28\uD2B8\uB97C \uBD88\uB7EC\uC624\uB294 \uC911",
6655
- kind = "line",
6656
- height = import_tokens40.tokens.extensions.chartSkeleton.height
6660
+ skeleton = true,
6661
+ loadingDelay = 0,
6662
+ rows = 8,
6663
+ columns = 14
6657
6664
  }) {
6658
- const { colors } = useKjunStyles(), reduced = useReducedMotion(), opacity = (0, import_react56.useRef)(new import_react_native42.Animated.Value(1)).current;
6659
- (0, import_react56.useEffect)(() => {
6660
- if (reduced) return;
6665
+ const { colors } = useKjunStyles(), reduced = useReducedMotion(), opacity = (0, import_react57.useRef)(new import_react_native42.Animated.Value(1)).current, revealed = useLoadingReveal(true, loadingDelay), [measured, setMeasured] = (0, import_react57.useState)(0), thickness = import_tokens40.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" };
6666
+ (0, import_react57.useEffect)(() => {
6667
+ if (reduced || !skeleton) return;
6661
6668
  const loop = import_react_native42.Animated.loop(
6662
6669
  import_react_native42.Animated.sequence([
6663
- import_react_native42.Animated.timing(opacity, {
6664
- toValue: 0.55,
6665
- duration: import_tokens40.tokens.motion.shimmer / 2,
6666
- useNativeDriver: true
6667
- }),
6668
- import_react_native42.Animated.timing(opacity, {
6669
- toValue: 1,
6670
- duration: import_tokens40.tokens.motion.shimmer / 2,
6671
- useNativeDriver: true
6672
- })
6670
+ import_react_native42.Animated.timing(opacity, { toValue: 0.55, duration: import_tokens40.tokens.motion.shimmer / 2, useNativeDriver: true }),
6671
+ import_react_native42.Animated.timing(opacity, { toValue: 1, duration: import_tokens40.tokens.motion.shimmer / 2, useNativeDriver: true })
6673
6672
  ])
6674
6673
  );
6675
6674
  loop.start();
@@ -6677,99 +6676,74 @@ function KjunChartSkeleton({
6677
6676
  loop.stop();
6678
6677
  opacity.setValue(1);
6679
6678
  };
6680
- }, [opacity, reduced]);
6681
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
6679
+ }, [opacity, reduced, skeleton]);
6680
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
6682
6681
  import_react_native42.Animated.View,
6683
6682
  {
6684
- style: {
6685
- height,
6686
- alignItems: "center",
6687
- justifyContent: "center",
6688
- width: "100%",
6689
- minWidth: 0,
6690
- opacity
6691
- },
6692
- accessibilityElementsHidden: true,
6693
- importantForAccessibility: "no-hide-descendants",
6694
- children: [
6683
+ onLayout: typeof height === "number" ? void 0 : (event) => setMeasured(event.nativeEvent.layout.height),
6684
+ style: { height, alignItems: "center", justifyContent: "center", width: "100%", minWidth: 0, opacity: revealed ? opacity : 0 },
6685
+ "aria-hidden": revealed ? void 0 : true,
6686
+ importantForAccessibility: revealed ? void 0 : "no-hide-descendants",
6687
+ ...import_react_native42.Platform.OS === "web" ? { dataSet: { kjunChartKind: kind, skeleton: skeleton ? void 0 : "false" } } : {},
6688
+ children: !skeleton ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_react_native42.View, { ...chartPart("loading"), role: status, style: { alignItems: "center", gap: import_tokens40.tokens.dimension.value12 }, children: [
6689
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_react_native42.View, { ...hidden, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(KjunSpinner, { size: "md" }) }),
6690
+ !!loadingText && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(KText, { style: { ...typeStyle("body"), color: colors.textSecondary, textAlign: "center", maxWidth: "100%" }, children: loadingText })
6691
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
6695
6692
  kind === "donut" ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
6696
6693
  import_react_native42.View,
6697
6694
  {
6698
- style: {
6699
- height: "80%",
6700
- aspectRatio: 1,
6701
- borderWidth: import_tokens40.tokens.extensions.chartSkeleton.donutThickness,
6702
- borderColor: colors.skeleton,
6703
- borderRadius: import_tokens40.tokens.radius.radius9999,
6704
- alignItems: "center",
6705
- justifyContent: "center"
6706
- },
6707
- children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(KjunSkeleton, { type: "block", width: import_tokens40.tokens.extensions.chartSkeleton.donutLabelWidth, height: import_tokens40.tokens.extensions.chartSkeleton.donutLabelHeight })
6695
+ ...chartPart("donut"),
6696
+ ...hidden,
6697
+ style: { height: "80%", aspectRatio: 1, borderWidth: thickness, borderColor: colors.skeleton, borderRadius: import_tokens40.tokens.radius.radius9999 }
6708
6698
  }
6709
- ) : /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
6710
- import_react_native_svg5.default,
6699
+ ) : kind === "matrix" ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_react_native42.View, { ...chartPart("matrix"), ...hidden, style: { alignSelf: "stretch", flex: 1, gap: import_tokens40.tokens.dimension.value4 }, children: Array.from({ length: rowCount }, (_, row) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_react_native42.View, { style: { flex: 1, flexDirection: "row", gap: import_tokens40.tokens.dimension.value4 }, children: Array.from({ length: columnCount }, (_2, column) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
6700
+ import_react_native42.View,
6711
6701
  {
6712
- viewBox: "0 0 600 240",
6713
- preserveAspectRatio: "none",
6714
- width: "100%",
6715
- height: "100%",
6716
- children: [
6717
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
6718
- import_react_native_svg5.Path,
6719
- {
6720
- d: "M20 10V220H590 M20 65H590 M20 120H590 M20 175H590",
6721
- fill: "none",
6722
- stroke: colors.border,
6723
- strokeWidth: 1
6724
- }
6725
- ),
6726
- kind === "line" && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_react_native_svg5.Path, { 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" }),
6727
- kind === "matrix" ? Array.from({ length: 8 }, (_, row) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_react_native_svg5.G, { children: Array.from({ length: 14 }, (_2, col) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
6728
- import_react_native_svg5.Rect,
6729
- {
6730
- x: 24 + col * 40,
6731
- y: 12 + row * 26,
6732
- width: 36,
6733
- height: 22,
6734
- rx: 2,
6735
- fill: colors.skeleton,
6736
- opacity: (row + col + 2) % 3 === 0 ? 0.55 : 1
6702
+ ...chartPart("cell"),
6703
+ style: {
6704
+ flex: 1,
6705
+ borderRadius: import_tokens40.tokens.extensions.skeleton.lineRadius,
6706
+ backgroundColor: colors.skeleton,
6707
+ opacity: chartMatrixMuted(row, column) ? import_tokens40.tokens.states.opacity.pending : 1
6708
+ }
6709
+ },
6710
+ column
6711
+ )) }, row)) }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_react_native42.View, { ...chartPart("plot"), ...hidden, style: { alignSelf: "stretch", flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_react_native_svg5.default, { viewBox: chartFrameViewBox, preserveAspectRatio: "none", width: "100%", height: "100%", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_react_native_svg5.Path, { d: chartFramePath, fill: "none", stroke: colors.chartGrid, strokeWidth: import_tokens40.tokens.border.defaultWidth, vectorEffect: "non-scaling-stroke" }) }) }),
6712
+ !!loadingText && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
6713
+ import_react_native42.View,
6714
+ {
6715
+ ...chartPart("status"),
6716
+ role: status,
6717
+ pointerEvents: "none",
6718
+ style: { position: "absolute", inset: 0, alignItems: "center", justifyContent: "center" },
6719
+ children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
6720
+ KText,
6721
+ {
6722
+ style: {
6723
+ ...typeStyle("body"),
6724
+ paddingHorizontal: import_tokens40.tokens.dimension.value12,
6725
+ paddingVertical: import_tokens40.tokens.dimension.value4,
6726
+ borderRadius: import_tokens40.tokens.radius.radius6,
6727
+ borderWidth: kind === "donut" ? 0 : import_tokens40.tokens.border.defaultWidth,
6728
+ borderColor: colors.chartGrid,
6729
+ backgroundColor: colors.surface,
6730
+ color: colors.textSecondary,
6731
+ maxWidth: kind === "donut" && hole != null ? hole : "90%",
6732
+ textAlign: "center"
6737
6733
  },
6738
- col
6739
- )) }, row)) : (kind === "bar" || kind === "candle") && [60, 100, 85, 140, 110, 165, 180].map((h, i) => /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_react56.Fragment, { children: [
6740
- kind === "candle" && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
6741
- import_react_native_svg5.Path,
6742
- {
6743
- d: `M${55 + i * 75} ${195 - h}V${225 - h / 2}`,
6744
- fill: "none",
6745
- stroke: colors.skeleton,
6746
- strokeWidth: 4
6747
- }
6748
- ),
6749
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
6750
- import_react_native_svg5.Rect,
6751
- {
6752
- x: 35 + i * 75,
6753
- y: kind === "candle" ? 210 - h : 220 - h,
6754
- width: kind === "candle" ? 28 : 42,
6755
- height: kind === "candle" ? h / 2 : h,
6756
- rx: 3,
6757
- fill: colors.skeleton
6758
- }
6759
- )
6760
- ] }, i))
6761
- ]
6734
+ children: loadingText
6735
+ }
6736
+ )
6762
6737
  }
6763
- ),
6764
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_react_native42.View, { pointerEvents: "none", style: { position: "absolute", inset: 0, alignItems: "center", justifyContent: "center", gap: import_tokens40.tokens.dimension.value12 }, children: !!loadingText && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(KText, { style: { ...typeStyle("body"), paddingHorizontal: import_tokens40.tokens.dimension.value12, paddingVertical: import_tokens40.tokens.dimension.value4, borderRadius: import_tokens40.tokens.radius.radius6, backgroundColor: colors.surface, color: colors.textSecondary, maxWidth: "90%", textAlign: "center" }, children: loadingText }) })
6765
- ]
6738
+ )
6739
+ ] })
6766
6740
  }
6767
6741
  );
6768
6742
  }
6769
6743
 
6770
6744
  // packages/native/src/financial-display.tsx
6771
6745
  var import_tokens43 = require("@kjun-ui/tokens");
6772
- var import_react59 = require("react");
6746
+ var import_react60 = require("react");
6773
6747
  var import_react_native44 = require("react-native");
6774
6748
  var import_react_native_svg6 = __toESM(require("react-native-svg"), 1);
6775
6749
 
@@ -6777,7 +6751,7 @@ var import_react_native_svg6 = __toESM(require("react-native-svg"), 1);
6777
6751
  var import_tokens42 = require("@kjun-ui/tokens");
6778
6752
 
6779
6753
  // shared/package-runtime/use-number-motion.ts
6780
- var import_react57 = require("react");
6754
+ var import_react58 = require("react");
6781
6755
 
6782
6756
  // shared/package-runtime/number-motion.ts
6783
6757
  var import_tokens41 = require("@kjun-ui/tokens");
@@ -6823,14 +6797,14 @@ function createNumberMotion(apply) {
6823
6797
 
6824
6798
  // shared/package-runtime/use-number-motion.ts
6825
6799
  function useNumberMotion(value, animated, fromPrevious, reduced) {
6826
- const [display, setDisplay] = (0, import_react57.useState)(typeof value === "string" || fromPrevious || !animated || reduced ? value : 0);
6827
- const motion = (0, import_react57.useRef)(null);
6800
+ const [display, setDisplay] = (0, import_react58.useState)(typeof value === "string" || fromPrevious || !animated || reduced ? value : 0);
6801
+ const motion = (0, import_react58.useRef)(null);
6828
6802
  if (!motion.current) motion.current = createNumberMotion(setDisplay);
6829
- (0, import_react57.useLayoutEffect)(() => {
6803
+ (0, import_react58.useLayoutEffect)(() => {
6830
6804
  if (!motion.current) motion.current = createNumberMotion(setDisplay);
6831
6805
  motion.current.update(value, animated, fromPrevious, reduced);
6832
6806
  }, [value, animated, fromPrevious, reduced]);
6833
- (0, import_react57.useLayoutEffect)(() => () => {
6807
+ (0, import_react58.useLayoutEffect)(() => () => {
6834
6808
  var _a;
6835
6809
  (_a = motion.current) == null ? void 0 : _a.cancel();
6836
6810
  motion.current = null;
@@ -6839,7 +6813,7 @@ function useNumberMotion(value, animated, fromPrevious, reduced) {
6839
6813
  }
6840
6814
 
6841
6815
  // packages/native/src/number-display.tsx
6842
- var import_react58 = require("react");
6816
+ var import_react59 = require("react");
6843
6817
  var import_react_native43 = require("react-native");
6844
6818
 
6845
6819
  // shared/package-runtime/relative-time.ts
@@ -6873,7 +6847,7 @@ var format = (value, decimals = 0) => new Intl.NumberFormat(void 0, {
6873
6847
  function useCountUp(value, animated, fromPrevious) {
6874
6848
  return useNumberMotion(value, animated, fromPrevious, useReducedMotion());
6875
6849
  }
6876
- var MarketQuoteTypeContext = (0, import_react58.createContext)(null);
6850
+ var MarketQuoteTypeContext = (0, import_react59.createContext)(null);
6877
6851
  function KjunAnimatedNumber({
6878
6852
  value,
6879
6853
  decimals = 0,
@@ -6933,7 +6907,7 @@ function KjunPriceCell({
6933
6907
  fetchedAt,
6934
6908
  fromPrevious = true
6935
6909
  }) {
6936
- const { colors } = useKjunStyles(), quoteType = (0, import_react58.useContext)(MarketQuoteTypeContext);
6910
+ const { colors } = useKjunStyles(), quoteType = (0, import_react59.useContext)(MarketQuoteTypeContext);
6937
6911
  return value == null ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(KjunSkeleton, { type: "block", height: import_tokens42.tokens.extensions.financial.priceSkeletonHeight, width: import_tokens42.tokens.extensions.financial.priceSkeletonWidth }) : /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native43.View, { style: { flexDirection: "row", alignItems: "center", gap: import_tokens42.tokens.extensions.financial.affixGap }, children: [
6938
6912
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
6939
6913
  import_react_native43.View,
@@ -6967,7 +6941,7 @@ function KjunSignedValue({
6967
6941
  showFreshness = true,
6968
6942
  formatter
6969
6943
  }) {
6970
- const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), quoteType = (0, import_react58.useContext)(MarketQuoteTypeContext), n = finiteNumber(value), role = n === null || n === 0 ? "priceNeutral" : n > 0 ? "priceUp" : "priceDown", color = loading || n === null ? colors.textSecondary : stale ? colors.textTertiary : domainColor(domainColors, role);
6944
+ const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), quoteType = (0, import_react59.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);
6971
6945
  const display = n === null ? "\u2014" : formatter ? formatter(n) : prefix + (showSign && n > 0 ? "+" : "") + format(kind === "percent" && !isRaw ? n * 100 : n, decimals) + (kind === "percent" ? "%" : "") + suffix;
6972
6946
  return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
6973
6947
  import_react_native43.View,
@@ -7006,6 +6980,7 @@ function KjunSignedValue({
7006
6980
 
7007
6981
  // packages/native/src/financial-display.tsx
7008
6982
  var import_jsx_runtime41 = require("react/jsx-runtime");
6983
+ var deviationFill = { neutral: 1, low: import_tokens43.tokens.states.opacity.deviationLow, medium: import_tokens43.tokens.states.opacity.deviationMedium, strong: import_tokens43.tokens.states.opacity.deviationStrong };
7009
6984
  function KjunDeviation({
7010
6985
  value,
7011
6986
  variant = "pill",
@@ -7016,7 +6991,7 @@ function KjunDeviation({
7016
6991
  const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), n = finiteNumber(value);
7017
6992
  if (n === null)
7018
6993
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(KText, { style: { color: colors.textDisabled }, children: "-" });
7019
- 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) + "%";
6994
+ const abs = Math.abs(n), signal = abs >= signalThreshold, level = (0, import_tokens43.deviationLevel)(n), neutral = level === "neutral", color = neutral ? colors.textSecondary : domainColor(domainColors, n > 0 ? "priceUp" : "priceDown"), label = formatter ? formatter(n) : (n > 0 ? "+" : "") + format(n, decimals) + "%";
7020
6995
  if (variant === "badge")
7021
6996
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
7022
6997
  import_react_native44.View,
@@ -7085,7 +7060,7 @@ function KjunDeviation({
7085
7060
  inset: 0,
7086
7061
  borderRadius: import_tokens43.tokens.extensions.financial.cellRadius,
7087
7062
  backgroundColor: neutral ? colors.secondary : color,
7088
- opacity: neutral ? 1 : abs < 0.5 ? 0.08 : abs < 1 ? 0.14 : 0.22
7063
+ opacity: deviationFill[level]
7089
7064
  }
7090
7065
  }
7091
7066
  ),
@@ -7098,7 +7073,7 @@ function KjunDeviation({
7098
7073
  fontVariant: ["tabular-nums"],
7099
7074
  ...typeStyle("body"),
7100
7075
  // Like Web: control weight, stronger once the deviation reaches 1%.
7101
- fontWeight: abs >= 1 ? typeStyle("numberLg").fontWeight : typeStyle("control").fontWeight
7076
+ fontWeight: level === "strong" ? typeStyle("numberLg").fontWeight : typeStyle("control").fontWeight
7102
7077
  },
7103
7078
  children: label
7104
7079
  }
@@ -7260,7 +7235,7 @@ function KjunSparkline({
7260
7235
  stretch = false,
7261
7236
  ariaLabel
7262
7237
  }) {
7263
- const { colors, domainColors } = useKjunStyles(), id = "kjun-" + (0, import_react59.useId)().replace(/:/g, ""), values = data.filter(Number.isFinite), min = Math.min(...values), max = Math.max(...values), range = max - min || 1, points = values.map(
7238
+ const { colors, domainColors } = useKjunStyles(), id = "kjun-" + (0, import_react60.useId)().replace(/:/g, ""), values = data.filter(Number.isFinite), min = Math.min(...values), max = Math.max(...values), range = max - min || 1, points = values.map(
7264
7239
  (v, i) => `${2 + i / Math.max(1, values.length - 1) * (width - 4)},${2 + height - 4 - (v - min) / range * (height - 4)}`
7265
7240
  ).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"));
7266
7241
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
@@ -7306,7 +7281,7 @@ function KjunSparkline({
7306
7281
 
7307
7282
  // shared/package-runtime/table.ts
7308
7283
  var import_tokens44 = require("@kjun-ui/tokens");
7309
- var import_react60 = require("react");
7284
+ var import_react61 = require("react");
7310
7285
 
7311
7286
  // shared/package-runtime/table-model.ts
7312
7287
  function getValue(row, key) {
@@ -7350,11 +7325,11 @@ function useTableModel(props) {
7350
7325
  expandSingle = false,
7351
7326
  onExpandedRowsChange
7352
7327
  } = props;
7353
- const [internalSort, setSort] = (0, import_react60.useState)({
7328
+ const [internalSort, setSort] = (0, import_react61.useState)({
7354
7329
  key: "",
7355
7330
  order: "asc"
7356
- }), [internalExpanded, setExpanded] = (0, import_react60.useState)([]), [search, setSearch] = (0, import_react60.useState)(""), timer = (0, import_react60.useRef)(void 0);
7357
- (0, import_react60.useEffect)(() => () => clearTimeout(timer.current), []);
7331
+ }), [internalExpanded, setExpanded] = (0, import_react61.useState)([]), [search, setSearch] = (0, import_react61.useState)(""), timer = (0, import_react61.useRef)(void 0);
7332
+ (0, import_react61.useEffect)(() => () => clearTimeout(timer.current), []);
7358
7333
  const sort = props.sort === void 0 ? internalSort : (_a = props.sort) != null ? _a : { key: "", order: "asc" };
7359
7334
  const rows = sortTableRows(data, sort, { sortable, sortMode });
7360
7335
  const key = (row, index = 0) => tableRowKey(row, rowKey, index);
@@ -7508,19 +7483,19 @@ function TableToolbar({
7508
7483
 
7509
7484
  // packages/native/src/table-grid.tsx
7510
7485
  var import_tokens46 = require("@kjun-ui/tokens");
7511
- var import_react61 = require("react");
7486
+ var import_react62 = require("react");
7512
7487
  var import_react_native_svg7 = __toESM(require("react-native-svg"), 1);
7513
7488
  var import_react_native46 = require("react-native");
7514
7489
  var import_jsx_runtime43 = require("react/jsx-runtime");
7515
- function TableGrid({ initialLoading, skeletonRows, state, onRowClick, selectable, checkbox, cell, expandable, expand, renderExpand, emptyContent, emptyText, visible, maxHeight, stickyHeader, sortable, compact, striped, hoverable }) {
7490
+ function TableGrid({ initialLoading, skeletonRows, skeletonHidden, state, onRowClick, selectable, checkbox, cell, expandable, expand, renderExpand, emptyContent, emptyText, visible, maxHeight, stickyHeader, sortable, compact, striped, hoverable }) {
7516
7491
  const { colors } = useKjunStyles();
7517
7492
  const geometry2 = import_tokens46.tokens.table;
7518
- const [scroll, setScroll] = (0, import_react61.useState)({ viewport: 0, x: 0 }), fadeId = "kjun-table-fade-" + (0, import_react61.useId)().replace(/[^a-z0-9]/gi, "");
7493
+ const [scroll, setScroll] = (0, import_react62.useState)({ viewport: 0, x: 0 }), fadeId = "kjun-table-fade-" + (0, import_react62.useId)().replace(/[^a-z0-9]/gi, "");
7519
7494
  const cellPadding = geometry2.cellPadding[compact ? "compact" : "regular"];
7520
7495
  const width = (col) => typeof col.width === "number" ? col.width : typeof col.width === "string" && /^\d+(px)?$/.test(col.width) ? parseFloat(col.width) : geometry2.columnWidth;
7521
7496
  const totalWidth = visible.reduce((sum, col) => sum + width(col), 0) + (selectable ? geometry2.actionColumnWidth : 0) + (expandable ? geometry2.actionColumnWidth : 0);
7522
7497
  const fade = totalWidth - scroll.viewport - scroll.x > 1;
7523
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_react_native46.View, { style: { position: "relative", minWidth: 0 }, children: [
7498
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_react_native46.View, { style: { position: "relative", minWidth: 0, opacity: skeletonHidden ? 0 : void 0 }, "aria-hidden": skeletonHidden || void 0, children: [
7524
7499
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
7525
7500
  import_react_native46.ScrollView,
7526
7501
  {
@@ -7629,7 +7604,7 @@ function TableGrid({ initialLoading, skeletonRows, state, onRowClick, selectable
7629
7604
  ))
7630
7605
  ] }, i)) : !state.rows.length ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_react_native46.View, { style: { paddingVertical: geometry2.emptyPadding.y, paddingHorizontal: geometry2.emptyPadding.x, alignItems: "center" }, children: content(emptyContent || emptyText, {
7631
7606
  color: colors.textSecondary
7632
- }) }) : state.rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_react61.Fragment, { children: [
7607
+ }) }) : state.rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_react62.Fragment, { children: [
7633
7608
  /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
7634
7609
  AccessiblePressable,
7635
7610
  {
@@ -7718,6 +7693,7 @@ var import_jsx_runtime44 = require("react/jsx-runtime");
7718
7693
  function TableCards({
7719
7694
  initialLoading,
7720
7695
  skeletonRows,
7696
+ skeletonHidden,
7721
7697
  state,
7722
7698
  onRowClick,
7723
7699
  selectable,
@@ -7732,149 +7708,156 @@ function TableCards({
7732
7708
  }) {
7733
7709
  const { colors } = useKjunStyles();
7734
7710
  const { title, subtitle, badges, inline, sections, actions, columnsForSection } = layout;
7735
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_react_native47.View, { style: { gap: import_tokens47.tokens.extensions.tableCard.gap, padding: import_tokens47.tokens.extensions.tableCard.listPadding }, children: [
7736
- initialLoading ? Array.from({ length: skeletonRows }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(KjunSkeleton, { type: "card" }, i)) : state.rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
7737
- AccessiblePressable,
7738
- {
7739
- accessible: false,
7740
- onPress: () => onRowClick == null ? void 0 : onRowClick(row, i),
7741
- style: ({ pressed }) => ({
7742
- padding: import_tokens47.tokens.extensions.tableCard.padding,
7743
- borderWidth: import_tokens47.tokens.border.defaultWidth,
7744
- borderColor: colors.border,
7745
- borderRadius: import_tokens47.tokens.extensions.tableCard.radius,
7746
- backgroundColor: pressed ? colors.hover : colors.background
7747
- }),
7748
- children: [
7749
- /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
7750
- import_react_native47.View,
7751
- {
7752
- style: {
7753
- flexDirection: "row",
7754
- alignItems: "center",
7755
- gap: import_tokens47.tokens.extensions.tableCard.gap,
7756
- marginBottom: import_tokens47.tokens.dimension.value8
7757
- },
7758
- children: [
7759
- /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
7711
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
7712
+ import_react_native47.View,
7713
+ {
7714
+ style: { gap: import_tokens47.tokens.extensions.tableCard.gap, padding: import_tokens47.tokens.extensions.tableCard.listPadding, opacity: skeletonHidden ? 0 : void 0 },
7715
+ "aria-hidden": skeletonHidden || void 0,
7716
+ children: [
7717
+ initialLoading ? Array.from({ length: skeletonRows }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(KjunSkeleton, { type: "card" }, i)) : state.rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
7718
+ AccessiblePressable,
7719
+ {
7720
+ accessible: false,
7721
+ onPress: () => onRowClick == null ? void 0 : onRowClick(row, i),
7722
+ style: ({ pressed }) => ({
7723
+ padding: import_tokens47.tokens.extensions.tableCard.padding,
7724
+ borderWidth: import_tokens47.tokens.border.defaultWidth,
7725
+ borderColor: colors.border,
7726
+ borderRadius: import_tokens47.tokens.extensions.tableCard.radius,
7727
+ backgroundColor: pressed ? colors.hover : colors.background
7728
+ }),
7729
+ children: [
7730
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
7731
+ import_react_native47.View,
7732
+ {
7733
+ style: {
7734
+ flexDirection: "row",
7735
+ alignItems: "center",
7736
+ gap: import_tokens47.tokens.extensions.tableCard.gap,
7737
+ marginBottom: import_tokens47.tokens.dimension.value8
7738
+ },
7739
+ children: [
7740
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
7741
+ import_react_native47.View,
7742
+ {
7743
+ style: {
7744
+ flexDirection: "row",
7745
+ flex: 1,
7746
+ alignItems: "center",
7747
+ gap: import_tokens47.tokens.extensions.tableCard.gap,
7748
+ minWidth: 0
7749
+ },
7750
+ children: [
7751
+ selectable && checkbox(row, i),
7752
+ title && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { flex: 1, minWidth: 0 }, children: cell(title, row, i, "title") })
7753
+ ]
7754
+ }
7755
+ ),
7756
+ badges.length ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { flexDirection: "row", gap: import_tokens47.tokens.extensions.tableCard.fieldGap }, children: badges.map((col) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { children: cell(col, row, i) }, col.key)) }) : subtitle ? cell(subtitle, row, i) : null,
7757
+ inline.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { flexDirection: "row", gap: import_tokens47.tokens.extensions.tableCard.fieldGap }, children: inline.map((col) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { children: cell(col, row, i) }, col.key)) })
7758
+ ]
7759
+ }
7760
+ ),
7761
+ sections.map((section, index) => {
7762
+ const cols = columnsForSection(row, section);
7763
+ return cols.length ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
7760
7764
  import_react_native47.View,
7761
7765
  {
7762
7766
  style: {
7763
- flexDirection: "row",
7764
- flex: 1,
7765
- alignItems: "center",
7766
- gap: import_tokens47.tokens.extensions.tableCard.gap,
7767
- minWidth: 0
7767
+ borderTopWidth: index ? import_tokens47.tokens.border.defaultWidth : 0,
7768
+ borderTopColor: colors.border,
7769
+ paddingTop: index ? import_tokens47.tokens.dimension.value12 : 0,
7770
+ marginTop: index ? import_tokens47.tokens.dimension.value12 : 0
7768
7771
  },
7769
7772
  children: [
7770
- selectable && checkbox(row, i),
7771
- title && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { flex: 1, minWidth: 0 }, children: cell(title, row, i, "title") })
7772
- ]
7773
- }
7774
- ),
7775
- badges.length ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { flexDirection: "row", gap: import_tokens47.tokens.extensions.tableCard.fieldGap }, children: badges.map((col) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { children: cell(col, row, i) }, col.key)) }) : subtitle ? cell(subtitle, row, i) : null,
7776
- inline.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { flexDirection: "row", gap: import_tokens47.tokens.extensions.tableCard.fieldGap }, children: inline.map((col) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { children: cell(col, row, i) }, col.key)) })
7777
- ]
7778
- }
7779
- ),
7780
- sections.map((section, index) => {
7781
- const cols = columnsForSection(row, section);
7782
- return cols.length ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
7783
- import_react_native47.View,
7784
- {
7785
- style: {
7786
- borderTopWidth: index ? import_tokens47.tokens.border.defaultWidth : 0,
7787
- borderTopColor: colors.border,
7788
- paddingTop: index ? import_tokens47.tokens.dimension.value12 : 0,
7789
- marginTop: index ? import_tokens47.tokens.dimension.value12 : 0
7790
- },
7791
- children: [
7792
- section.label && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7793
- KText,
7794
- {
7795
- style: {
7796
- ...typeStyle("controlSmall"),
7797
- color: colors.textTertiary,
7798
- marginBottom: import_tokens47.tokens.dimension.value6
7799
- },
7800
- children: section.label
7801
- }
7802
- ),
7803
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7804
- import_react_native47.View,
7805
- {
7806
- style: {
7807
- flexDirection: "row",
7808
- flexWrap: "wrap",
7809
- gap: section.layout === "metrics" ? import_tokens47.tokens.extensions.tableCard.gap : import_tokens47.tokens.extensions.tableCard.metricGap
7810
- },
7811
- children: cols.map((col) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
7773
+ section.label && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7774
+ KText,
7775
+ {
7776
+ style: {
7777
+ ...typeStyle("controlSmall"),
7778
+ color: colors.textTertiary,
7779
+ marginBottom: import_tokens47.tokens.dimension.value6
7780
+ },
7781
+ children: section.label
7782
+ }
7783
+ ),
7784
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7812
7785
  import_react_native47.View,
7813
7786
  {
7814
7787
  style: {
7815
- flexBasis: col.fullWidthInCard || section.layout === "stack" ? "100%" : section.layout === "metrics" ? "30%" : "47%",
7816
- flexGrow: 1,
7817
- minWidth: 0,
7818
- gap: import_tokens47.tokens.extensions.tableCard.valueGap
7788
+ flexDirection: "row",
7789
+ flexWrap: "wrap",
7790
+ gap: section.layout === "metrics" ? import_tokens47.tokens.extensions.tableCard.gap : import_tokens47.tokens.extensions.tableCard.metricGap
7819
7791
  },
7820
- children: [
7821
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7822
- KText,
7823
- {
7824
- style: {
7825
- ...typeStyle("meta"),
7826
- color: colors.textTertiary,
7827
- textAlign: section.labelAlign
7828
- },
7829
- children: col.label
7830
- }
7831
- ),
7832
- cell(col, row, i)
7833
- ]
7834
- },
7835
- col.key
7836
- ))
7837
- }
7838
- )
7839
- ]
7840
- },
7841
- section.key || index
7842
- ) : null;
7843
- }),
7844
- expandable && expand(row, i),
7845
- expandable && state.expanded.includes(state.key(row, i)) && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7846
- import_react_native47.View,
7847
- {
7848
- style: {
7849
- borderTopWidth: import_tokens47.tokens.border.defaultWidth,
7850
- borderTopColor: colors.border,
7851
- paddingTop: import_tokens47.tokens.dimension.value8,
7852
- marginTop: import_tokens47.tokens.dimension.value4
7853
- },
7854
- children: content(renderExpand == null ? void 0 : renderExpand(row, i))
7855
- }
7856
- ),
7857
- actions && !actions.inlineInCard && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7858
- import_react_native47.View,
7859
- {
7860
- style: {
7861
- borderTopWidth: import_tokens47.tokens.border.defaultWidth,
7862
- borderTopColor: colors.border,
7863
- paddingTop: import_tokens47.tokens.dimension.value8,
7864
- marginTop: import_tokens47.tokens.dimension.value8,
7865
- alignItems: "flex-end"
7866
- },
7867
- children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { alignSelf: "flex-end" }, children: cell(actions, row, i) })
7868
- }
7869
- )
7870
- ]
7871
- },
7872
- state.key(row, i)
7873
- )),
7874
- !initialLoading && !state.rows.length && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { padding: import_tokens47.tokens.extensions.tableCard.emptyPadding, alignItems: "center" }, children: content(emptyContent || emptyText, {
7875
- color: colors.textTertiary
7876
- }) })
7877
- ] });
7792
+ children: cols.map((col) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
7793
+ import_react_native47.View,
7794
+ {
7795
+ style: {
7796
+ flexBasis: col.fullWidthInCard || section.layout === "stack" ? "100%" : section.layout === "metrics" ? "30%" : "47%",
7797
+ flexGrow: 1,
7798
+ minWidth: 0,
7799
+ gap: import_tokens47.tokens.extensions.tableCard.valueGap
7800
+ },
7801
+ children: [
7802
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7803
+ KText,
7804
+ {
7805
+ style: {
7806
+ ...typeStyle("meta"),
7807
+ color: colors.textTertiary,
7808
+ textAlign: section.labelAlign
7809
+ },
7810
+ children: col.label
7811
+ }
7812
+ ),
7813
+ cell(col, row, i)
7814
+ ]
7815
+ },
7816
+ col.key
7817
+ ))
7818
+ }
7819
+ )
7820
+ ]
7821
+ },
7822
+ section.key || index
7823
+ ) : null;
7824
+ }),
7825
+ expandable && expand(row, i),
7826
+ expandable && state.expanded.includes(state.key(row, i)) && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7827
+ import_react_native47.View,
7828
+ {
7829
+ style: {
7830
+ borderTopWidth: import_tokens47.tokens.border.defaultWidth,
7831
+ borderTopColor: colors.border,
7832
+ paddingTop: import_tokens47.tokens.dimension.value8,
7833
+ marginTop: import_tokens47.tokens.dimension.value4
7834
+ },
7835
+ children: content(renderExpand == null ? void 0 : renderExpand(row, i))
7836
+ }
7837
+ ),
7838
+ actions && !actions.inlineInCard && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7839
+ import_react_native47.View,
7840
+ {
7841
+ style: {
7842
+ borderTopWidth: import_tokens47.tokens.border.defaultWidth,
7843
+ borderTopColor: colors.border,
7844
+ paddingTop: import_tokens47.tokens.dimension.value8,
7845
+ marginTop: import_tokens47.tokens.dimension.value8,
7846
+ alignItems: "flex-end"
7847
+ },
7848
+ children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { alignSelf: "flex-end" }, children: cell(actions, row, i) })
7849
+ }
7850
+ )
7851
+ ]
7852
+ },
7853
+ state.key(row, i)
7854
+ )),
7855
+ !initialLoading && !state.rows.length && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { padding: import_tokens47.tokens.extensions.tableCard.emptyPadding, alignItems: "center" }, children: content(emptyContent || emptyText, {
7856
+ color: colors.textTertiary
7857
+ }) })
7858
+ ]
7859
+ }
7860
+ );
7878
7861
  }
7879
7862
 
7880
7863
  // packages/native/src/table-view.tsx
@@ -7882,6 +7865,9 @@ var import_jsx_runtime45 = require("react/jsx-runtime");
7882
7865
  function KjunTable(props) {
7883
7866
  const {
7884
7867
  skeletonRows = 8,
7868
+ skeleton = true,
7869
+ loadingText = "\uB85C\uB529 \uC911...",
7870
+ loadingDelay = 0,
7885
7871
  emptyText = "\uB370\uC774\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4",
7886
7872
  hoverable = true,
7887
7873
  searchable = false,
@@ -7902,7 +7888,7 @@ function KjunTable(props) {
7902
7888
  onRowClick,
7903
7889
  onRetry
7904
7890
  } = props;
7905
- const { colors, domainColors } = useKjunStyles(), state = useTableModel2(props), query = useQueryDisplay(props), mobile = (0, import_react_native48.useWindowDimensions)().width < import_tokens48.tokens.table.mobileBreakpoint;
7891
+ const { colors, domainColors } = useKjunStyles(), state = useTableModel2(props), query = useQueryDisplay(props), revealed = useLoadingReveal(query.initialLoading, skeleton ? loadingDelay : 0), mobile = (0, import_react_native48.useWindowDimensions)().width < import_tokens48.tokens.table.mobileBreakpoint;
7906
7892
  const layout = tablePresentation(props, mobile);
7907
7893
  const { card, visible } = layout;
7908
7894
  const cell = (col, row, index, role) => {
@@ -7981,8 +7967,11 @@ function KjunTable(props) {
7981
7967
  refreshing: query.queryRefreshing,
7982
7968
  loadingPadding: "none",
7983
7969
  onRetry,
7970
+ loading: skeleton ? void 0 : props.loading,
7971
+ loadingText,
7972
+ loadingDelay: skeleton ? 0 : loadingDelay,
7984
7973
  children: [
7985
- card ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(TableCards, { ...{ initialLoading: query.initialLoading, skeletonRows, state, onRowClick, selectable, checkbox, cell, expandable, expand, renderExpand, emptyContent, emptyText, layout } }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(TableGrid, { ...{ initialLoading: query.initialLoading, skeletonRows, state, onRowClick, selectable, checkbox, cell, expandable, expand, renderExpand, emptyContent, emptyText, visible, maxHeight, stickyHeader, compact, striped, hoverable }, sortable: props.sortable }),
7974
+ card ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(TableCards, { ...{ initialLoading: query.initialLoading, skeletonRows, skeletonHidden: query.initialLoading && !revealed, state, onRowClick, selectable, checkbox, cell, expandable, expand, renderExpand, emptyContent, emptyText, layout } }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(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 }),
7986
7975
  pagination && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
7987
7976
  KjunPagination,
7988
7977
  {
@@ -8040,12 +8029,13 @@ function KjunKpiHero(props) {
8040
8029
  secondary = [],
8041
8030
  animated = true,
8042
8031
  size = "md",
8032
+ surface = "card",
8043
8033
  aside,
8044
8034
  formatter,
8045
8035
  deltaFormatter,
8046
8036
  renderSecondary
8047
8037
  } = props;
8048
- const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), mobile = (0, import_react_native50.useWindowDimensions)().width <= import_tokens49.tokens.responsive.kpiHero, small = size === "sm", numberRole = small ? "displaySm" : mobile ? "displayMd" : "displayLg", font = typeStyle(numberRole).fontSize, unitStyle = typeStyle(small ? "numberSm" : mobile ? "numberMd" : "numberLg");
8038
+ const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), windowWidth = (0, import_react_native50.useWindowDimensions)().width, mobile = windowWidth <= import_tokens49.tokens.responsive.kpiHero, small = size === "sm", numberRole = small ? "displaySm" : mobile ? "displayMd" : "displayLg", font = typeStyle(numberRole).fontSize, unitStyle = typeStyle(small ? "numberSm" : mobile ? "numberMd" : "numberLg");
8049
8039
  const geometry2 = import_tokens49.tokens.extensions.kpiHero;
8050
8040
  const spec = small ? geometry2.sm : mobile ? geometry2.mobile : geometry2.md;
8051
8041
  const numeric = {
@@ -8075,10 +8065,13 @@ function KjunKpiHero(props) {
8075
8065
  {
8076
8066
  accessibilityState: { busy: loading },
8077
8067
  style: {
8078
- paddingVertical: spec.paddingY,
8079
- paddingHorizontal: spec.paddingX,
8080
- backgroundColor: colors.surface,
8081
- borderRadius: geometry2.radius
8068
+ // surface="none" sits inside the app's own card or list, so it drops its own card box.
8069
+ ...surface === "none" ? {} : {
8070
+ paddingVertical: spec.paddingY,
8071
+ paddingHorizontal: spec.paddingX,
8072
+ backgroundColor: colors.surface,
8073
+ borderRadius: geometry2.radius
8074
+ }
8082
8075
  },
8083
8076
  children: [
8084
8077
  /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
@@ -8161,70 +8154,75 @@ function KjunKpiHero(props) {
8161
8154
  borderTopWidth: import_tokens49.tokens.border.defaultWidth,
8162
8155
  borderTopColor: colors.border
8163
8156
  },
8164
- children: secondary.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
8165
- import_react_native50.View,
8166
- {
8167
- style: mobile && !small ? {
8168
- flex: 1,
8169
- minWidth: 0,
8170
- paddingLeft: i ? geometry2.itemPadding : 0,
8171
- paddingRight: i === secondary.length - 1 ? 0 : geometry2.itemPadding,
8172
- borderLeftWidth: i ? import_tokens49.tokens.border.defaultWidth : 0,
8173
- borderLeftColor: colors.border
8174
- } : { minWidth: 0, maxWidth: "100%", flexShrink: 1 },
8175
- children: [
8176
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
8177
- KText,
8178
- {
8179
- style: {
8180
- color: colors.textTertiary,
8181
- ...typeStyle("caption"),
8182
- marginBottom: spec.secondaryLabelGap
8183
- },
8184
- children: item.label
8185
- }
8186
- ),
8187
- loading ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
8188
- KjunSkeleton,
8189
- {
8190
- type: "block",
8191
- height: typeStyle("numberMd").lineHeight,
8192
- width: geometry2.secondarySkeletonWidth
8193
- }
8194
- ) : (renderSecondary == null ? void 0 : renderSecondary(item, i)) || /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
8195
- KpiNumberLine,
8196
- {
8197
- prefix: item.prefix,
8198
- suffix: item.suffix,
8199
- prefixGap: geometry2.secondaryAffixGap,
8200
- suffixGap: geometry2.secondaryAffixGap,
8201
- unitStyle: typeStyle("caption"),
8202
- style: { ...numeric, ...typeStyle("numberMd"), color: item.valueColor || (item.semantic === "price" && Number(item.value) !== 0 ? direction(Number(item.value)) : colors.text) },
8203
- children: kpiFormat(item)
8204
- }
8205
- ),
8206
- item.desc && (loading ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
8207
- KjunSkeleton,
8208
- {
8209
- type: "block",
8210
- width: geometry2.descriptionSkeletonWidth,
8211
- height: typeStyle("caption").lineHeight
8212
- }
8213
- ) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
8214
- KText,
8215
- {
8216
- style: {
8217
- ...typeStyle("caption"),
8218
- marginTop: small ? geometry2.sm.descriptionGap : geometry2.md.descriptionGap,
8219
- color: item.descColor || colors.textTertiary
8220
- },
8221
- children: item.desc
8222
- }
8223
- ))
8224
- ]
8225
- },
8226
- i
8227
- ))
8157
+ children: secondary.map((item, i) => {
8158
+ const columns = mobile && !small && windowWidth <= import_tokens49.tokens.responsive.kpiHeroSecondary && secondary.length >= 3 ? 2 : 0;
8159
+ const first = columns ? i % columns === 0 : i === 0;
8160
+ const last = columns ? i % columns === columns - 1 || i === secondary.length - 1 : i === secondary.length - 1;
8161
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
8162
+ import_react_native50.View,
8163
+ {
8164
+ style: mobile && !small ? {
8165
+ ...columns ? { width: `${100 / columns}%`, marginTop: i >= columns ? spec.secondaryPadding : 0 } : { flex: 1 },
8166
+ minWidth: 0,
8167
+ paddingLeft: first ? 0 : geometry2.itemPadding,
8168
+ paddingRight: last ? 0 : geometry2.itemPadding,
8169
+ borderLeftWidth: first ? 0 : import_tokens49.tokens.border.defaultWidth,
8170
+ borderLeftColor: colors.border
8171
+ } : { minWidth: 0, maxWidth: "100%", flexShrink: 1 },
8172
+ children: [
8173
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
8174
+ KText,
8175
+ {
8176
+ style: {
8177
+ color: colors.textTertiary,
8178
+ ...typeStyle("caption"),
8179
+ marginBottom: spec.secondaryLabelGap
8180
+ },
8181
+ children: item.label
8182
+ }
8183
+ ),
8184
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
8185
+ KjunSkeleton,
8186
+ {
8187
+ type: "block",
8188
+ height: typeStyle("numberMd").lineHeight,
8189
+ width: geometry2.secondarySkeletonWidth
8190
+ }
8191
+ ) : (renderSecondary == null ? void 0 : renderSecondary(item, i)) || /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
8192
+ KpiNumberLine,
8193
+ {
8194
+ prefix: item.prefix,
8195
+ suffix: item.suffix,
8196
+ prefixGap: geometry2.secondaryAffixGap,
8197
+ suffixGap: geometry2.secondaryAffixGap,
8198
+ unitStyle: typeStyle("caption"),
8199
+ style: { ...numeric, ...typeStyle("numberMd"), color: item.valueColor || (item.semantic === "price" && Number(item.value) !== 0 ? direction(Number(item.value)) : colors.text) },
8200
+ children: kpiFormat(item)
8201
+ }
8202
+ ),
8203
+ item.desc && (loading ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
8204
+ KjunSkeleton,
8205
+ {
8206
+ type: "block",
8207
+ width: geometry2.descriptionSkeletonWidth,
8208
+ height: typeStyle("caption").lineHeight
8209
+ }
8210
+ ) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
8211
+ KText,
8212
+ {
8213
+ style: {
8214
+ ...typeStyle("caption"),
8215
+ marginTop: small ? geometry2.sm.descriptionGap : geometry2.md.descriptionGap,
8216
+ color: item.descColor || colors.textTertiary
8217
+ },
8218
+ children: item.desc
8219
+ }
8220
+ ))
8221
+ ]
8222
+ },
8223
+ i
8224
+ );
8225
+ })
8228
8226
  }
8229
8227
  )
8230
8228
  ]
@@ -8234,13 +8232,14 @@ function KjunKpiHero(props) {
8234
8232
 
8235
8233
  // packages/native/src/kpi-row.tsx
8236
8234
  var import_tokens50 = require("@kjun-ui/tokens");
8237
- var import_react62 = require("react");
8235
+ var import_react63 = require("react");
8238
8236
  var import_react_native51 = require("react-native");
8239
8237
  var import_jsx_runtime48 = require("react/jsx-runtime");
8240
8238
  function KjunKpiRow({
8241
8239
  loading = false,
8242
8240
  items,
8243
8241
  size = "md",
8242
+ surface = "card",
8244
8243
  mobileSummary = true,
8245
8244
  onItemClick,
8246
8245
  renderValue
@@ -8248,7 +8247,7 @@ function KjunKpiRow({
8248
8247
  const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), mobile = (0, import_react_native51.useWindowDimensions)().width <= import_tokens50.tokens.responsive.kpiRow, small = size === "sm", summary = mobileSummary && mobile && !small;
8249
8248
  const geometry2 = import_tokens50.tokens.extensions.kpiRow;
8250
8249
  const spec = small ? geometry2.sm : mobile ? geometry2.mobile : geometry2.md;
8251
- const [width, setWidth] = (0, import_react62.useState)(0);
8250
+ const [width, setWidth] = (0, import_react63.useState)(0);
8252
8251
  const paddingX = summary ? geometry2.summary.paddingX : spec.paddingX;
8253
8252
  const gapX = summary ? geometry2.summary.gapX : 0;
8254
8253
  const explicit = items.some(
@@ -8289,10 +8288,13 @@ function KjunKpiRow({
8289
8288
  style: {
8290
8289
  flexDirection: "row",
8291
8290
  flexWrap: mobile ? "wrap" : "nowrap",
8292
- paddingVertical: summary ? geometry2.summary.paddingY : spec.paddingY,
8293
- paddingHorizontal: paddingX,
8294
- borderRadius: geometry2.radius,
8295
- backgroundColor: colors.surface,
8291
+ // surface="none" sits inside the app's own card or list, so it drops its own card box.
8292
+ ...surface === "none" ? {} : {
8293
+ paddingVertical: summary ? geometry2.summary.paddingY : spec.paddingY,
8294
+ paddingHorizontal: paddingX,
8295
+ borderRadius: geometry2.radius,
8296
+ backgroundColor: colors.surface
8297
+ },
8296
8298
  rowGap: summary ? geometry2.summary.gapY : 0,
8297
8299
  columnGap: gapX
8298
8300
  },
@@ -8464,7 +8466,7 @@ var import_tokens52 = require("@kjun-ui/tokens");
8464
8466
  var import_react_native53 = require("react-native");
8465
8467
 
8466
8468
  // shared/package-runtime/market-cards.ts
8467
- var import_react63 = require("react");
8469
+ var import_react64 = require("react");
8468
8470
  function useMarketCardsModel(props, persistence) {
8469
8471
  const {
8470
8472
  columns,
@@ -8480,8 +8482,8 @@ function useMarketCardsModel(props, persistence) {
8480
8482
  sortKey: c.sortable ? metricConfig[c.key].sortKey : null,
8481
8483
  isDefaultSort: c.defaultSort
8482
8484
  }));
8483
- const [key, setKey] = (0, import_react63.useState)(""), restoreSignature = JSON.stringify([storageNamespace, pills]);
8484
- const mounted = (0, import_react63.useRef)("");
8485
+ const [key, setKey] = (0, import_react64.useState)(""), restoreSignature = JSON.stringify([storageNamespace, pills]);
8486
+ const mounted = (0, import_react64.useRef)("");
8485
8487
  const save = (key2) => {
8486
8488
  var _a;
8487
8489
  try {
@@ -8489,7 +8491,7 @@ function useMarketCardsModel(props, persistence) {
8489
8491
  } catch (e) {
8490
8492
  }
8491
8493
  };
8492
- (0, import_react63.useEffect)(() => {
8494
+ (0, import_react64.useEffect)(() => {
8493
8495
  var _a, _b;
8494
8496
  if (mounted.current === restoreSignature) return;
8495
8497
  mounted.current = restoreSignature;
@@ -8731,7 +8733,7 @@ function KjunMarketCards(props) {
8731
8733
  ...props,
8732
8734
  loadingPadding: "none",
8733
8735
  empty: !rows.length,
8734
- loadingContent: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(KjunListSkeleton, { variant: "compact" }),
8736
+ loadingContent: props.skeleton === false ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(KjunListSkeleton, { variant: "compact" }),
8735
8737
  emptyContent: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(KjunEmpty, { text: emptyMessage, description: emptySubMessage }),
8736
8738
  children: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(import_react_native53.View, { children: [
8737
8739
  state.pill && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(KText, { style: { textAlign: "right", paddingHorizontal: import_tokens52.tokens.dimension.value16, paddingVertical: import_tokens52.tokens.dimension.value8, ...typeStyle("caption"), color: colors.textSecondary }, children: state.pill.label }),
@@ -8842,7 +8844,7 @@ function KjunMarketSimpleList(props) {
8842
8844
  loadingPadding: "none",
8843
8845
  empty: !rows.length,
8844
8846
  emptyText: emptyMessage,
8845
- loadingContent: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(KjunListSkeleton, {}),
8847
+ loadingContent: props.skeleton === false ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(KjunListSkeleton, {}),
8846
8848
  emptyContent: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(KjunEmpty, { text: emptyMessage, description: emptySubMessage }),
8847
8849
  children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_react_native54.View, { children: rows.map((row, i) => {
8848
8850
  var _a, _b;
@@ -8969,7 +8971,7 @@ function KjunMarketListPanel({
8969
8971
 
8970
8972
  // packages/native/src/market-table.tsx
8971
8973
  var import_tokens55 = require("@kjun-ui/tokens");
8972
- var import_react64 = require("react");
8974
+ var import_react65 = require("react");
8973
8975
  var import_react_native56 = require("react-native");
8974
8976
  var import_react_native_svg8 = __toESM(require("react-native-svg"), 1);
8975
8977
  var import_jsx_runtime53 = require("react/jsx-runtime");
@@ -9055,7 +9057,7 @@ function KjunMarketTable(props) {
9055
9057
  );
9056
9058
  };
9057
9059
  const layout = useMarketLayout(columns, showActions);
9058
- const [scroll, setScroll] = (0, import_react64.useState)({ viewport: 0, x: 0 }), fadeId = "kjun-market-fade-" + (0, import_react64.useId)().replace(/[^a-z0-9]/gi, "");
9060
+ const [scroll, setScroll] = (0, import_react65.useState)({ viewport: 0, x: 0 }), fadeId = "kjun-market-fade-" + (0, import_react65.useId)().replace(/[^a-z0-9]/gi, "");
9059
9061
  const tableQuote = { price: {}, change: { fontWeight: typeStyle("body").fontWeight } };
9060
9062
  return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
9061
9063
  import_react_native56.View,
@@ -9073,7 +9075,7 @@ function KjunMarketTable(props) {
9073
9075
  loadingPadding: "none",
9074
9076
  empty: !rows.length,
9075
9077
  emptyText: emptyMessage,
9076
- loadingContent: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(KjunMarketTableSkeleton, { columns, showActions }),
9078
+ loadingContent: props.skeleton === false ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(KjunMarketTableSkeleton, { columns, showActions }),
9077
9079
  emptyContent: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(KjunEmpty, { text: emptyMessage, description: emptySubMessage }),
9078
9080
  children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(MarketQuoteTypeContext.Provider, { value: tableQuote, children: /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_react_native56.View, { style: { position: "relative", minWidth: 0 }, children: [
9079
9081
  /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
@@ -9278,12 +9280,12 @@ function KjunMarketTable(props) {
9278
9280
 
9279
9281
  // packages/native/src/list.tsx
9280
9282
  var import_tokens56 = require("@kjun-ui/tokens");
9281
- var import_react65 = require("react");
9283
+ var import_react66 = require("react");
9282
9284
  var import_react_native57 = require("react-native");
9283
9285
  var import_jsx_runtime54 = require("react/jsx-runtime");
9284
- var LastListRow = (0, import_react65.createContext)(false);
9286
+ var LastListRow = (0, import_react66.createContext)(false);
9285
9287
  function KjunListRow({ title, description, leading, trailing, actions, href, disabled = false, onPress }) {
9286
- const { colors } = useKjunStyles(), last = (0, import_react65.useContext)(LastListRow);
9288
+ const { colors } = useKjunStyles(), last = (0, import_react66.useContext)(LastListRow);
9287
9289
  const parts = /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_jsx_runtime54.Fragment, { children: [
9288
9290
  leading && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_react_native57.View, { children: content(leading) }),
9289
9291
  /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_react_native57.View, { style: { flex: 1, minWidth: 0 }, children: [
@@ -9308,13 +9310,13 @@ function KjunListSection({ title, description, actions, children, ariaLabel }) {
9308
9310
  ] }),
9309
9311
  content(actions)
9310
9312
  ] }),
9311
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_react_native57.View, { role: "list", accessibilityLabel: ariaLabel || title, children: import_react65.Children.toArray(children).map((row, i, rows) => /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(LastListRow.Provider, { value: i === rows.length - 1, children: row }, i)) })
9313
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_react_native57.View, { role: "list", accessibilityLabel: ariaLabel || title, children: import_react66.Children.toArray(children).map((row, i, rows) => /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(LastListRow.Provider, { value: i === rows.length - 1, children: row }, i)) })
9312
9314
  ] });
9313
9315
  }
9314
9316
 
9315
9317
  // packages/native/src/app-navigation.tsx
9316
9318
  var import_tokens57 = require("@kjun-ui/tokens");
9317
- var import_react66 = require("react");
9319
+ var import_react67 = require("react");
9318
9320
  var import_react_native58 = require("react-native");
9319
9321
  var import_jsx_runtime55 = require("react/jsx-runtime");
9320
9322
  function KjunTopNavigation({ title, description, leading, actions, safeAreaTop = 0 }) {
@@ -9323,13 +9325,13 @@ function KjunTopNavigation({ title, description, leading, actions, safeAreaTop =
9323
9325
  const titleInset = Math.max(0, (geometry2.controlSize - import_tokens57.tokens.typography.sectionTitle.lineHeightPx) / 2);
9324
9326
  const wrapping = import_react_native58.Platform.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : {};
9325
9327
  const edgeInset = (geometry2.controlSize - geometry2.iconSize) / 2;
9326
- const [iconEdges, setIconEdges] = (0, import_react66.useState)({ leading: false, trailing: false });
9328
+ const [iconEdges, setIconEdges] = (0, import_react67.useState)({ leading: false, trailing: false });
9327
9329
  const square = (key) => (event) => {
9328
9330
  const { width, height } = event.nativeEvent.layout;
9329
9331
  const next = Math.abs(width - height) < 1 && Math.abs(height - geometry2.controlSize) < 1;
9330
9332
  setIconEdges((current) => current[key] === next ? current : { ...current, [key]: next });
9331
9333
  };
9332
- const flatten = (node) => import_react66.Children.toArray(node).flatMap((child) => (0, import_react66.isValidElement)(child) && child.type === import_react66.Fragment ? flatten(child.props.children) : [child]);
9334
+ const flatten = (node) => import_react67.Children.toArray(node).flatMap((child) => (0, import_react67.isValidElement)(child) && child.type === import_react67.Fragment ? flatten(child.props.children) : [child]);
9333
9335
  const actionList = flatten(actions);
9334
9336
  return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(TopNavigationContext.Provider, { value: geometry2, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_react_native58.View, { style: { flexShrink: 0, paddingTop: Math.max(0, safeAreaTop), backgroundColor: colors.surface }, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(import_react_native58.View, { style: { minHeight: geometry2.minimumHeight, flexDirection: "row", flexWrap: "wrap", alignItems: "flex-start", gap: geometry2.gap, paddingVertical: geometry2.paddingY, paddingHorizontal: geometry2.paddingX }, children: [
9335
9337
  leading && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(TopNavigationContext.Provider, { value: { ...geometry2, slot: "leading" }, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_react_native58.View, { onLayout: square("leading"), style: { minHeight: geometry2.controlSize, minWidth: 0, maxWidth: "40%", flexShrink: 1, justifyContent: "center", marginLeft: iconEdges.leading ? -edgeInset : 0 }, children: content(leading) }) }),
@@ -9403,7 +9405,7 @@ function KjunBottomNavigation({ items, value, ariaLabel = "\uC8FC\uC694 \uD0D0\u
9403
9405
  }
9404
9406
  function KjunBottomActionBar({ description, children, safeAreaBottom = 0, keyboardVisible = false, hideOnKeyboard = false }) {
9405
9407
  const { colors } = useKjunStyles();
9406
- const [width, setWidth] = (0, import_react66.useState)(0);
9408
+ const [width, setWidth] = (0, import_react67.useState)(0);
9407
9409
  const geometry2 = import_tokens57.tokens.extensions.navigation;
9408
9410
  const wide = width >= import_tokens57.tokens.responsive.bottomAction + geometry2.padding * 2;
9409
9411
  const wrapping = import_react_native58.Platform.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : {};
@@ -9425,16 +9427,16 @@ function KjunBottomActionBar({ description, children, safeAreaBottom = 0, keyboa
9425
9427
  }
9426
9428
 
9427
9429
  // packages/native/src/media.tsx
9428
- var import_react67 = require("react");
9430
+ var import_react68 = require("react");
9429
9431
  var import_react_native59 = require("react-native");
9430
9432
  var import_tokens58 = require("@kjun-ui/tokens");
9431
9433
  var import_jsx_runtime56 = require("react/jsx-runtime");
9432
9434
  function KjunImage({ src, source, alt, decorative = false, aspectRatio = 1, fit = "cover", fallback: fallback2, onLoad, onError }) {
9433
9435
  const { colors } = useKjunStyles();
9434
- const request = JSON.stringify(source != null ? source : src), current = (0, import_react67.useRef)({ request, version: 0 });
9436
+ const request = JSON.stringify(source != null ? source : src), current = (0, import_react68.useRef)({ request, version: 0 });
9435
9437
  if (current.current.request !== request) current.current = { request, version: current.current.version + 1 };
9436
9438
  const version = current.current.version;
9437
- const [failed, setFailed] = (0, import_react67.useState)(null);
9439
+ const [failed, setFailed] = (0, import_react68.useState)(null);
9438
9440
  const missing = !source && !src;
9439
9441
  return /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native59.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: colors.secondary }, children: !missing && failed !== version ? /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native59.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: () => {
9440
9442
  if (version === current.current.version) onLoad == null ? void 0 : onLoad();
@@ -9464,7 +9466,7 @@ function KjunChip({ label, icon, removable = false, disabled = false, size = "md
9464
9466
  }
9465
9467
 
9466
9468
  // packages/native/src/range-input.tsx
9467
- var import_react68 = require("react");
9469
+ var import_react69 = require("react");
9468
9470
  var import_react_native61 = require("react-native");
9469
9471
  var import_tokens60 = require("@kjun-ui/tokens");
9470
9472
  var import_jsx_runtime58 = require("react/jsx-runtime");
@@ -9488,18 +9490,18 @@ function RangeInput({
9488
9490
  const valid = domain.valid && (!Array.isArray(value) || value.length === 2) && values.every(domain.accepts) && (values.length === 1 || values[0] <= values[1]);
9489
9491
  const blocked = disabled || !valid;
9490
9492
  const effectiveMax = valid ? domain.snap(max) : min;
9491
- const [hovered, setHovered] = (0, import_react68.useState)(null);
9492
- const [dragging, setDragging] = (0, import_react68.useState)(null);
9493
- const [focused, setFocused] = (0, import_react68.useState)(null);
9494
- const track = (0, import_react68.useRef)(null), measured = (0, import_react68.useRef)({ x: 0, width: 1 }), selected = (0, import_react68.useRef)(0), dirty = (0, import_react68.useRef)(false);
9495
- const latest = (0, import_react68.useRef)(values);
9493
+ const [hovered, setHovered] = (0, import_react69.useState)(null);
9494
+ const [dragging, setDragging] = (0, import_react69.useState)(null);
9495
+ const [focused, setFocused] = (0, import_react69.useState)(null);
9496
+ const track = (0, import_react69.useRef)(null), measured = (0, import_react69.useRef)({ x: 0, width: 1 }), selected = (0, import_react69.useRef)(0), dirty = (0, import_react69.useRef)(false);
9497
+ const latest = (0, import_react69.useRef)(values);
9496
9498
  latest.current = values;
9497
- const callbacks = (0, import_react68.useRef)({ onValueChange, onChangeCommit });
9499
+ const callbacks = (0, import_react69.useRef)({ onValueChange, onChangeCommit });
9498
9500
  callbacks.current = { onValueChange, onChangeCommit };
9499
- (0, import_react68.useEffect)(() => {
9501
+ (0, import_react69.useEffect)(() => {
9500
9502
  if (!valid) console.warn("KJUN Slider: \uBC94\uC704\xB7step\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
9501
9503
  }, [valid]);
9502
- (0, import_react68.useEffect)(() => {
9504
+ (0, import_react69.useEffect)(() => {
9503
9505
  if (blocked) {
9504
9506
  setDragging(null);
9505
9507
  setHovered(null);
@@ -9539,7 +9541,7 @@ function RangeInput({
9539
9541
  measured.current = { x: pageX, width: Math.max(1, width) };
9540
9542
  });
9541
9543
  };
9542
- const responder = (0, import_react68.useMemo)(
9544
+ const responder = (0, import_react69.useMemo)(
9543
9545
  () => import_react_native61.PanResponder.create({
9544
9546
  onStartShouldSetPanResponderCapture: () => !disabled && valid,
9545
9547
  onMoveShouldSetPanResponderCapture: (_event, gesture) => !disabled && valid && Math.abs(gesture.dx) > 3 && Math.abs(gesture.dx) > Math.abs(gesture.dy),
@@ -9764,7 +9766,7 @@ function KjunRangeSlider(props) {
9764
9766
  }
9765
9767
 
9766
9768
  // packages/native/src/quantity-stepper.tsx
9767
- var import_react69 = require("react");
9769
+ var import_react70 = require("react");
9768
9770
  var import_react_native62 = require("react-native");
9769
9771
  var import_tokens61 = require("@kjun-ui/tokens");
9770
9772
 
@@ -9786,16 +9788,16 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9786
9788
  const increaseStyle = { ...buttonStyle, borderRadius: 0, borderTopRightRadius: outer, borderBottomRightRadius: outer };
9787
9789
  const domain = (0, import_tokens61.createNumberDomain)({ min, max, step, precision }), valid = domain.valid && domain.accepts(value), blocked = disabled || !valid;
9788
9790
  const surface = useFieldSurface(size, { disabled: blocked, invalid });
9789
- const [draft, setDraft] = (0, import_react69.useState)(String(value));
9790
- const inputRef = (0, import_react69.useRef)(null);
9791
- const composing = (0, import_react69.useRef)(false), dirty = (0, import_react69.useRef)(false), accepted = (0, import_react69.useRef)(value);
9792
- const cancelledEscape = (0, import_react69.useRef)(false);
9793
- (0, import_react69.useEffect)(() => {
9791
+ const [draft, setDraft] = (0, import_react70.useState)(String(value));
9792
+ const inputRef = (0, import_react70.useRef)(null);
9793
+ const composing = (0, import_react70.useRef)(false), dirty = (0, import_react70.useRef)(false), accepted = (0, import_react70.useRef)(value);
9794
+ const cancelledEscape = (0, import_react70.useRef)(false);
9795
+ (0, import_react70.useEffect)(() => {
9794
9796
  setDraft(String(value));
9795
9797
  accepted.current = value;
9796
9798
  dirty.current = false;
9797
9799
  }, [value, precision, min, max, step]);
9798
- (0, import_react69.useEffect)(() => {
9800
+ (0, import_react70.useEffect)(() => {
9799
9801
  if (!valid) console.warn("KJUN QuantityStepper: \uBC94\uC704\xB7step\xB7\uC815\uBC00\uB3C4\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
9800
9802
  }, [valid]);
9801
9803
  const emit = (next) => {
@@ -9832,7 +9834,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9832
9834
  } else if (action === "commit") commit();
9833
9835
  else move(action === "increment" ? 1 : -1);
9834
9836
  };
9835
- const handlers = (0, import_react69.useRef)({ key, edit: (text) => {
9837
+ const handlers = (0, import_react70.useRef)({ key, edit: (text) => {
9836
9838
  dirty.current = true;
9837
9839
  setDraft(text);
9838
9840
  } });
@@ -9840,7 +9842,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9840
9842
  dirty.current = true;
9841
9843
  setDraft(text);
9842
9844
  } };
9843
- (0, import_react69.useEffect)(() => {
9845
+ (0, import_react70.useEffect)(() => {
9844
9846
  if (import_react_native62.Platform.OS !== "web") return;
9845
9847
  const input = inputRef.current;
9846
9848
  const keydown = (event) => handlers.current.key(event);
@@ -9912,19 +9914,19 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9912
9914
  }
9913
9915
 
9914
9916
  // packages/native/src/time-picker.tsx
9915
- var import_react70 = require("react");
9917
+ var import_react71 = require("react");
9916
9918
  var import_react_native63 = require("react-native");
9917
9919
  var import_tokens62 = require("@kjun-ui/tokens");
9918
9920
  var import_jsx_runtime60 = require("react/jsx-runtime");
9919
9921
  function KjunTimePicker({ value, precision = "minute", min, max, minuteStep = 1, secondStep = 1, disabled = false, error = false, clearable = true, size = "md", ariaLabel = "\uC2DC\uAC04", onValueChange, onChangeCommit }) {
9920
9922
  const geometry2 = import_tokens62.tokens.extensions.timePicker, spec = import_tokens62.tokens.input[size];
9921
9923
  const { colors } = useKjunStyles();
9922
- const first = (0, import_react70.useRef)(null);
9923
- const [focused, setFocused] = (0, import_react70.useState)(false), [opened, setOpened] = (0, import_react70.useState)(null);
9924
- const field = useKjunField(), uid = (0, import_react70.useId)();
9924
+ const first = (0, import_react71.useRef)(null);
9925
+ const [focused, setFocused] = (0, import_react71.useState)(false), [opened, setOpened] = (0, import_react71.useState)(null);
9926
+ const field = useKjunField(), uid = (0, import_react71.useId)();
9925
9927
  const fieldId = (field == null ? void 0 : field.id) || `kjun-time-${uid}`;
9926
- const domain = (0, import_react70.useMemo)(() => (0, import_tokens62.createTimeDomain)({ precision, min, max, minuteStep, secondStep }), [precision, min, max, minuteStep, secondStep]), valid = domain.valid && domain.accepts(value);
9927
- (0, import_react70.useEffect)(() => {
9928
+ const domain = (0, import_react71.useMemo)(() => (0, import_tokens62.createTimeDomain)({ precision, min, max, minuteStep, secondStep }), [precision, min, max, minuteStep, secondStep]), valid = domain.valid && domain.accepts(value);
9929
+ (0, import_react71.useEffect)(() => {
9928
9930
  if (!valid) console.warn("KJUN TimePicker: \uC2DC\uAC01 \uBC94\uC704\xB7\uAC04\uACA9\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
9929
9931
  }, [valid]);
9930
9932
  const change = (next) => {
@@ -9935,7 +9937,7 @@ function KjunTimePicker({ value, precision = "minute", min, max, minuteStep = 1,
9935
9937
  };
9936
9938
  const time = value === null ? null : domain.parse(value);
9937
9939
  return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(import_react_native63.View, { accessibilityLabel: ariaLabel, style: { flexDirection: "row", alignItems: "center", maxWidth: "100%", minWidth: 0, backgroundColor: colors.inputBg, borderRadius: spec.radius }, children: [
9938
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(import_react_native63.View, { style: { flex: 1, flexDirection: "row", alignItems: "center", minWidth: 0 }, children: ["\uC2DC", "\uBD84", "\uCD08"].slice(0, precision === "second" ? 3 : 2).map((label, index) => /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(import_react70.Fragment, { children: [
9940
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(import_react_native63.View, { style: { flex: 1, flexDirection: "row", alignItems: "center", minWidth: 0 }, children: ["\uC2DC", "\uBD84", "\uCD08"].slice(0, precision === "second" ? 3 : 2).map((label, index) => /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(import_react71.Fragment, { children: [
9939
9941
  index > 0 && /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(KText, { "aria-hidden": true, style: { ...inputTypeStyle(size), width: geometry2.separatorWidth, textAlign: "center", color: disabled || !valid ? colors.textDisabled : colors.textSecondary }, children: ":" }),
9940
9942
  /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(FieldContext.Provider, { value: { ...field, id: index === 0 ? fieldId : `${fieldId}-${index}` }, children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(CompoundControlContext.Provider, { value: { size, focusRef: index === 0 ? first : void 0, onFocusChange: setFocused, leading: index === 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(import_react_native63.View, { style: { flexShrink: 0, minWidth: geometry2.segmentMinimumWidth }, children: /* @__PURE__ */ (0, import_jsx_runtime60.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) => {
9941
9943
  if (typeof next === "number") change(domain.select(value, index, next));