@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.web.js CHANGED
@@ -2366,36 +2366,6 @@ function KjunSkeleton({
2366
2366
  ))
2367
2367
  }
2368
2368
  ),
2369
- type === "chart" && /* @__PURE__ */ jsxs8(View11, { style: [frame, { height: height || tokens11.extensions.skeleton.chartHeight }], children: [
2370
- /* @__PURE__ */ jsx15(
2371
- View11,
2372
- {
2373
- style: {
2374
- flex: 1,
2375
- flexDirection: "row",
2376
- alignItems: "flex-end",
2377
- justifyContent: "space-around",
2378
- gap: tokens11.extensions.skeleton.gap
2379
- // Bars stand on the axis line, like ChartSkeleton.
2380
- },
2381
- children: [40, 65, 50, 80, 60, 90, 75].map((h, i) => /* @__PURE__ */ jsx15(
2382
- Animated6.View,
2383
- {
2384
- style: {
2385
- flex: 1,
2386
- maxWidth: tokens11.extensions.skeleton.chartBarMaxWidth,
2387
- height: `${h}%`,
2388
- backgroundColor: shimmer,
2389
- borderTopLeftRadius: tokens11.radius.radius4,
2390
- borderTopRightRadius: tokens11.radius.radius4
2391
- }
2392
- },
2393
- i
2394
- ))
2395
- }
2396
- ),
2397
- block("100%", tokens11.extensions.skeleton.chartAxisHeight, { backgroundColor: colors.border })
2398
- ] }),
2399
2369
  type === "stat" && /* @__PURE__ */ jsxs8(
2400
2370
  View11,
2401
2371
  {
@@ -6364,12 +6334,27 @@ import {
6364
6334
  initialQueryDisplay,
6365
6335
  queryDisplayStatus
6366
6336
  } from "@kjun-ui/tokens";
6367
- import { Component, useState as useState30 } from "react";
6337
+ import { Component, useState as useState31 } from "react";
6368
6338
  import { Platform as Platform23, View as View31 } from "react-native";
6339
+
6340
+ // shared/package-runtime/use-loading-reveal.ts
6341
+ import { useEffect as useEffect21, useState as useState30 } from "react";
6342
+ function useLoadingReveal(active, delay = 0) {
6343
+ const [revealed, setRevealed] = useState30(false);
6344
+ useEffect21(() => {
6345
+ setRevealed(false);
6346
+ if (!active || delay <= 0) return;
6347
+ const timer = setTimeout(() => setRevealed(true), delay);
6348
+ return () => clearTimeout(timer);
6349
+ }, [active, delay]);
6350
+ return delay <= 0 || revealed;
6351
+ }
6352
+
6353
+ // packages/native/src/data-state.tsx
6369
6354
  import { jsx as jsx37, jsxs as jsxs25 } from "react/jsx-runtime";
6370
6355
  function useQueryDisplay(props) {
6371
6356
  var _a;
6372
- const [state, setState] = useState30(() => initialQueryDisplay(props));
6357
+ const [state, setState] = useState31(() => initialQueryDisplay(props));
6373
6358
  let next = state;
6374
6359
  if (state.queryKey !== ((_a = props.queryKey) != null ? _a : null) || state.resultKey !== props.resultKey || state.loading !== !!props.loading) {
6375
6360
  next = advanceQueryDisplay(state, props);
@@ -6388,6 +6373,7 @@ function KjunDataState({
6388
6373
  emptyIcon,
6389
6374
  emptyActionText,
6390
6375
  loadingText = "\uB85C\uB529 \uC911...",
6376
+ loadingDelay = 0,
6391
6377
  retryText = "\uB2E4\uC2DC \uC2DC\uB3C4",
6392
6378
  size = "md",
6393
6379
  skeleton = false,
@@ -6401,7 +6387,7 @@ function KjunDataState({
6401
6387
  onEmptyAction,
6402
6388
  ...query
6403
6389
  }) {
6404
- 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 = tokens34.extensions.state.padding[size];
6390
+ 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 = tokens34.extensions.state.padding[size], revealed = useLoadingReveal(state.initialLoading, loadingDelay);
6405
6391
  const { colors } = useKjunStyles();
6406
6392
  return /* @__PURE__ */ jsxs25(
6407
6393
  View31,
@@ -6434,8 +6420,10 @@ function KjunDataState({
6434
6420
  state.initialLoading ? /* @__PURE__ */ jsx37(
6435
6421
  View31,
6436
6422
  {
6437
- accessibilityLabel: loadingText,
6438
- style: { paddingVertical: loadingPadding === "none" ? 0 : padding },
6423
+ accessibilityLabel: revealed ? loadingText : void 0,
6424
+ "aria-hidden": revealed ? void 0 : true,
6425
+ importantForAccessibility: revealed ? void 0 : "no-hide-descendants",
6426
+ style: { paddingVertical: loadingPadding === "none" ? 0 : padding, opacity: revealed ? void 0 : 0 },
6439
6427
  children: loadingContent || (skeleton ? /* @__PURE__ */ jsx37(View31, { style: { gap: tokens34.dimension.value12 }, children: Array.from({ length: skeletonCount }, (_, i) => /* @__PURE__ */ jsx37(KjunSkeleton, { type: skeletonType }, i)) }) : /* @__PURE__ */ jsxs25(View31, { style: { alignItems: "center", gap: tokens34.dimension.value12 }, children: [
6440
6428
  /* @__PURE__ */ jsx37(KjunSpinner, { size: "md" }),
6441
6429
  /* @__PURE__ */ jsx37(KText, { style: { ...typeStyle("body"), color: colors.textSecondary, textAlign: "center", maxWidth: "100%" }, children: loadingText })
@@ -6516,9 +6504,23 @@ var KjunErrorBoundary = class extends Component {
6516
6504
 
6517
6505
  // packages/native/src/skeletons.tsx
6518
6506
  import { tokens as tokens38 } from "@kjun-ui/tokens";
6519
- import { Fragment as Fragment12, useEffect as useEffect21, useRef as useRef30 } from "react";
6520
- import { Animated as Animated17, View as View33, useWindowDimensions as useWindowDimensions6 } from "react-native";
6521
- import Svg5, { G as G2, Path as Path4, Rect as Rect3 } from "react-native-svg";
6507
+ import { useEffect as useEffect22, useRef as useRef30, useState as useState33 } from "react";
6508
+ import { Animated as Animated17, Platform as Platform24, View as View33, useWindowDimensions as useWindowDimensions6 } from "react-native";
6509
+ import Svg5, { Path as Path4 } from "react-native-svg";
6510
+
6511
+ // shared/package-runtime/chart-skeleton.ts
6512
+ var chartFrameViewBox = "0 0 600 240";
6513
+ var chartFramePath = "M20 10V220H590 M20 65H590 M20 120H590 M20 175H590";
6514
+ var chartSkeletonMaxCount = 100;
6515
+ function chartSkeletonCount(value) {
6516
+ return Number.isFinite(value) ? Math.min(chartSkeletonMaxCount, Math.max(1, Math.floor(value))) : 1;
6517
+ }
6518
+ function chartMatrixMuted(row, column) {
6519
+ return (row + column + 2) % 3 === 0;
6520
+ }
6521
+ function chartDonutHole(height, thickness) {
6522
+ return Math.max(0, height * 0.8 - 2 * thickness);
6523
+ }
6522
6524
 
6523
6525
  // packages/native/src/market-skeleton.tsx
6524
6526
  import { tokens as tokens37 } from "@kjun-ui/tokens";
@@ -6545,9 +6547,9 @@ function nativeMarketLayout(columns, showActions, containerWidth) {
6545
6547
 
6546
6548
  // packages/native/src/market-layout.ts
6547
6549
  import { tokens as tokens36 } from "@kjun-ui/tokens";
6548
- import { useState as useState31 } from "react";
6550
+ import { useState as useState32 } from "react";
6549
6551
  function useMarketLayout(columns, showActions) {
6550
- const [containerWidth, setContainerWidth] = useState31(0);
6552
+ const [containerWidth, setContainerWidth] = useState32(0);
6551
6553
  const onLayout = (event) => setContainerWidth(event.nativeEvent.layout.width);
6552
6554
  return { ...nativeMarketLayout(columns, showActions, containerWidth), onLayout };
6553
6555
  }
@@ -6576,7 +6578,7 @@ function KjunMarketTableSkeleton({ columns = [], rows = 8, showActions = false }
6576
6578
  }
6577
6579
 
6578
6580
  // packages/native/src/skeletons.tsx
6579
- import { jsx as jsx39, jsxs as jsxs27 } from "react/jsx-runtime";
6581
+ import { Fragment as Fragment12, jsx as jsx39, jsxs as jsxs27 } from "react/jsx-runtime";
6580
6582
  function KjunListSkeleton({
6581
6583
  rows = 8,
6582
6584
  variant = "market",
@@ -6677,26 +6679,23 @@ function KjunFormSkeleton({
6677
6679
  }
6678
6680
  );
6679
6681
  }
6682
+ var chartPart = (name) => Platform24.OS === "web" ? { dataSet: { kjunChartPart: name } } : {};
6680
6683
  function KjunChartSkeleton({
6684
+ kind = "frame",
6685
+ height = tokens38.extensions.chartSkeleton.height,
6681
6686
  loadingText = "\uCC28\uD2B8\uB97C \uBD88\uB7EC\uC624\uB294 \uC911",
6682
- kind = "line",
6683
- height = tokens38.extensions.chartSkeleton.height
6687
+ skeleton = true,
6688
+ loadingDelay = 0,
6689
+ rows = 8,
6690
+ columns = 14
6684
6691
  }) {
6685
- const { colors } = useKjunStyles(), reduced = useReducedMotion(), opacity = useRef30(new Animated17.Value(1)).current;
6686
- useEffect21(() => {
6687
- if (reduced) return;
6692
+ const { colors } = useKjunStyles(), reduced = useReducedMotion(), opacity = useRef30(new Animated17.Value(1)).current, revealed = useLoadingReveal(true, loadingDelay), [measured, setMeasured] = useState33(0), thickness = tokens38.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" };
6693
+ useEffect22(() => {
6694
+ if (reduced || !skeleton) return;
6688
6695
  const loop = Animated17.loop(
6689
6696
  Animated17.sequence([
6690
- Animated17.timing(opacity, {
6691
- toValue: 0.55,
6692
- duration: tokens38.motion.shimmer / 2,
6693
- useNativeDriver: true
6694
- }),
6695
- Animated17.timing(opacity, {
6696
- toValue: 1,
6697
- duration: tokens38.motion.shimmer / 2,
6698
- useNativeDriver: true
6699
- })
6697
+ Animated17.timing(opacity, { toValue: 0.55, duration: tokens38.motion.shimmer / 2, useNativeDriver: true }),
6698
+ Animated17.timing(opacity, { toValue: 1, duration: tokens38.motion.shimmer / 2, useNativeDriver: true })
6700
6699
  ])
6701
6700
  );
6702
6701
  loop.start();
@@ -6704,98 +6703,73 @@ function KjunChartSkeleton({
6704
6703
  loop.stop();
6705
6704
  opacity.setValue(1);
6706
6705
  };
6707
- }, [opacity, reduced]);
6708
- return /* @__PURE__ */ jsxs27(
6706
+ }, [opacity, reduced, skeleton]);
6707
+ return /* @__PURE__ */ jsx39(
6709
6708
  Animated17.View,
6710
6709
  {
6711
- style: {
6712
- height,
6713
- alignItems: "center",
6714
- justifyContent: "center",
6715
- width: "100%",
6716
- minWidth: 0,
6717
- opacity
6718
- },
6719
- accessibilityElementsHidden: true,
6720
- importantForAccessibility: "no-hide-descendants",
6721
- children: [
6710
+ onLayout: typeof height === "number" ? void 0 : (event) => setMeasured(event.nativeEvent.layout.height),
6711
+ style: { height, alignItems: "center", justifyContent: "center", width: "100%", minWidth: 0, opacity: revealed ? opacity : 0 },
6712
+ "aria-hidden": revealed ? void 0 : true,
6713
+ importantForAccessibility: revealed ? void 0 : "no-hide-descendants",
6714
+ ...Platform24.OS === "web" ? { dataSet: { kjunChartKind: kind, skeleton: skeleton ? void 0 : "false" } } : {},
6715
+ children: !skeleton ? /* @__PURE__ */ jsxs27(View33, { ...chartPart("loading"), role: status, style: { alignItems: "center", gap: tokens38.dimension.value12 }, children: [
6716
+ /* @__PURE__ */ jsx39(View33, { ...hidden, children: /* @__PURE__ */ jsx39(KjunSpinner, { size: "md" }) }),
6717
+ !!loadingText && /* @__PURE__ */ jsx39(KText, { style: { ...typeStyle("body"), color: colors.textSecondary, textAlign: "center", maxWidth: "100%" }, children: loadingText })
6718
+ ] }) : /* @__PURE__ */ jsxs27(Fragment12, { children: [
6722
6719
  kind === "donut" ? /* @__PURE__ */ jsx39(
6723
6720
  View33,
6724
6721
  {
6725
- style: {
6726
- height: "80%",
6727
- aspectRatio: 1,
6728
- borderWidth: tokens38.extensions.chartSkeleton.donutThickness,
6729
- borderColor: colors.skeleton,
6730
- borderRadius: tokens38.radius.radius9999,
6731
- alignItems: "center",
6732
- justifyContent: "center"
6733
- },
6734
- children: /* @__PURE__ */ jsx39(KjunSkeleton, { type: "block", width: tokens38.extensions.chartSkeleton.donutLabelWidth, height: tokens38.extensions.chartSkeleton.donutLabelHeight })
6722
+ ...chartPart("donut"),
6723
+ ...hidden,
6724
+ style: { height: "80%", aspectRatio: 1, borderWidth: thickness, borderColor: colors.skeleton, borderRadius: tokens38.radius.radius9999 }
6735
6725
  }
6736
- ) : /* @__PURE__ */ jsxs27(
6737
- Svg5,
6726
+ ) : kind === "matrix" ? /* @__PURE__ */ jsx39(View33, { ...chartPart("matrix"), ...hidden, style: { alignSelf: "stretch", flex: 1, gap: tokens38.dimension.value4 }, children: Array.from({ length: rowCount }, (_, row) => /* @__PURE__ */ jsx39(View33, { style: { flex: 1, flexDirection: "row", gap: tokens38.dimension.value4 }, children: Array.from({ length: columnCount }, (_2, column) => /* @__PURE__ */ jsx39(
6727
+ View33,
6738
6728
  {
6739
- viewBox: "0 0 600 240",
6740
- preserveAspectRatio: "none",
6741
- width: "100%",
6742
- height: "100%",
6743
- children: [
6744
- /* @__PURE__ */ jsx39(
6745
- Path4,
6746
- {
6747
- d: "M20 10V220H590 M20 65H590 M20 120H590 M20 175H590",
6748
- fill: "none",
6749
- stroke: colors.border,
6750
- strokeWidth: 1
6751
- }
6752
- ),
6753
- 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" }),
6754
- kind === "matrix" ? Array.from({ length: 8 }, (_, row) => /* @__PURE__ */ jsx39(G2, { children: Array.from({ length: 14 }, (_2, col) => /* @__PURE__ */ jsx39(
6755
- Rect3,
6756
- {
6757
- x: 24 + col * 40,
6758
- y: 12 + row * 26,
6759
- width: 36,
6760
- height: 22,
6761
- rx: 2,
6762
- fill: colors.skeleton,
6763
- opacity: (row + col + 2) % 3 === 0 ? 0.55 : 1
6729
+ ...chartPart("cell"),
6730
+ style: {
6731
+ flex: 1,
6732
+ borderRadius: tokens38.extensions.skeleton.lineRadius,
6733
+ backgroundColor: colors.skeleton,
6734
+ opacity: chartMatrixMuted(row, column) ? tokens38.states.opacity.pending : 1
6735
+ }
6736
+ },
6737
+ column
6738
+ )) }, row)) }) : /* @__PURE__ */ jsx39(View33, { ...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: tokens38.border.defaultWidth, vectorEffect: "non-scaling-stroke" }) }) }),
6739
+ !!loadingText && /* @__PURE__ */ jsx39(
6740
+ View33,
6741
+ {
6742
+ ...chartPart("status"),
6743
+ role: status,
6744
+ pointerEvents: "none",
6745
+ style: { position: "absolute", inset: 0, alignItems: "center", justifyContent: "center" },
6746
+ children: /* @__PURE__ */ jsx39(
6747
+ KText,
6748
+ {
6749
+ style: {
6750
+ ...typeStyle("body"),
6751
+ paddingHorizontal: tokens38.dimension.value12,
6752
+ paddingVertical: tokens38.dimension.value4,
6753
+ borderRadius: tokens38.radius.radius6,
6754
+ borderWidth: kind === "donut" ? 0 : tokens38.border.defaultWidth,
6755
+ borderColor: colors.chartGrid,
6756
+ backgroundColor: colors.surface,
6757
+ color: colors.textSecondary,
6758
+ maxWidth: kind === "donut" && hole != null ? hole : "90%",
6759
+ textAlign: "center"
6764
6760
  },
6765
- col
6766
- )) }, row)) : (kind === "bar" || kind === "candle") && [60, 100, 85, 140, 110, 165, 180].map((h, i) => /* @__PURE__ */ jsxs27(Fragment12, { children: [
6767
- kind === "candle" && /* @__PURE__ */ jsx39(
6768
- Path4,
6769
- {
6770
- d: `M${55 + i * 75} ${195 - h}V${225 - h / 2}`,
6771
- fill: "none",
6772
- stroke: colors.skeleton,
6773
- strokeWidth: 4
6774
- }
6775
- ),
6776
- /* @__PURE__ */ jsx39(
6777
- Rect3,
6778
- {
6779
- x: 35 + i * 75,
6780
- y: kind === "candle" ? 210 - h : 220 - h,
6781
- width: kind === "candle" ? 28 : 42,
6782
- height: kind === "candle" ? h / 2 : h,
6783
- rx: 3,
6784
- fill: colors.skeleton
6785
- }
6786
- )
6787
- ] }, i))
6788
- ]
6761
+ children: loadingText
6762
+ }
6763
+ )
6789
6764
  }
6790
- ),
6791
- /* @__PURE__ */ jsx39(View33, { pointerEvents: "none", style: { position: "absolute", inset: 0, alignItems: "center", justifyContent: "center", gap: tokens38.dimension.value12 }, children: !!loadingText && /* @__PURE__ */ jsx39(KText, { style: { ...typeStyle("body"), paddingHorizontal: tokens38.dimension.value12, paddingVertical: tokens38.dimension.value4, borderRadius: tokens38.radius.radius6, backgroundColor: colors.surface, color: colors.textSecondary, maxWidth: "90%", textAlign: "center" }, children: loadingText }) })
6792
- ]
6765
+ )
6766
+ ] })
6793
6767
  }
6794
6768
  );
6795
6769
  }
6796
6770
 
6797
6771
  // packages/native/src/financial-display.tsx
6798
- import { tokens as tokens41 } from "@kjun-ui/tokens";
6772
+ import { deviationLevel, tokens as tokens41 } from "@kjun-ui/tokens";
6799
6773
  import { useId as useId8 } from "react";
6800
6774
  import {
6801
6775
  View as View35
@@ -6813,7 +6787,7 @@ import Svg6, {
6813
6787
  import { tokens as tokens40 } from "@kjun-ui/tokens";
6814
6788
 
6815
6789
  // shared/package-runtime/use-number-motion.ts
6816
- import { useLayoutEffect as useLayoutEffect16, useRef as useRef31, useState as useState32 } from "react";
6790
+ import { useLayoutEffect as useLayoutEffect16, useRef as useRef31, useState as useState34 } from "react";
6817
6791
 
6818
6792
  // shared/package-runtime/number-motion.ts
6819
6793
  import { tokens as tokens39 } from "@kjun-ui/tokens";
@@ -6859,7 +6833,7 @@ function createNumberMotion(apply) {
6859
6833
 
6860
6834
  // shared/package-runtime/use-number-motion.ts
6861
6835
  function useNumberMotion(value, animated, fromPrevious, reduced) {
6862
- const [display, setDisplay] = useState32(typeof value === "string" || fromPrevious || !animated || reduced ? value : 0);
6836
+ const [display, setDisplay] = useState34(typeof value === "string" || fromPrevious || !animated || reduced ? value : 0);
6863
6837
  const motion = useRef31(null);
6864
6838
  if (!motion.current) motion.current = createNumberMotion(setDisplay);
6865
6839
  useLayoutEffect16(() => {
@@ -7045,6 +7019,7 @@ function KjunSignedValue({
7045
7019
 
7046
7020
  // packages/native/src/financial-display.tsx
7047
7021
  import { Fragment as Fragment13, jsx as jsx41, jsxs as jsxs29 } from "react/jsx-runtime";
7022
+ var deviationFill = { neutral: 1, low: tokens41.states.opacity.deviationLow, medium: tokens41.states.opacity.deviationMedium, strong: tokens41.states.opacity.deviationStrong };
7048
7023
  function KjunDeviation({
7049
7024
  value,
7050
7025
  variant = "pill",
@@ -7055,7 +7030,7 @@ function KjunDeviation({
7055
7030
  const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), n = finiteNumber(value);
7056
7031
  if (n === null)
7057
7032
  return /* @__PURE__ */ jsx41(KText, { style: { color: colors.textDisabled }, children: "-" });
7058
- 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) + "%";
7033
+ 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) + "%";
7059
7034
  if (variant === "badge")
7060
7035
  return /* @__PURE__ */ jsxs29(
7061
7036
  View35,
@@ -7124,7 +7099,7 @@ function KjunDeviation({
7124
7099
  inset: 0,
7125
7100
  borderRadius: tokens41.extensions.financial.cellRadius,
7126
7101
  backgroundColor: neutral ? colors.secondary : color,
7127
- opacity: neutral ? 1 : abs < 0.5 ? 0.08 : abs < 1 ? 0.14 : 0.22
7102
+ opacity: deviationFill[level]
7128
7103
  }
7129
7104
  }
7130
7105
  ),
@@ -7137,7 +7112,7 @@ function KjunDeviation({
7137
7112
  fontVariant: ["tabular-nums"],
7138
7113
  ...typeStyle("body"),
7139
7114
  // Like Web: control weight, stronger once the deviation reaches 1%.
7140
- fontWeight: abs >= 1 ? typeStyle("numberLg").fontWeight : typeStyle("control").fontWeight
7115
+ fontWeight: level === "strong" ? typeStyle("numberLg").fontWeight : typeStyle("control").fontWeight
7141
7116
  },
7142
7117
  children: label
7143
7118
  }
@@ -7345,7 +7320,7 @@ function KjunSparkline({
7345
7320
 
7346
7321
  // shared/package-runtime/table.ts
7347
7322
  import { nextTableSort } from "@kjun-ui/tokens";
7348
- import { useEffect as useEffect23, useRef as useRef33, useState as useState34 } from "react";
7323
+ import { useEffect as useEffect24, useRef as useRef33, useState as useState36 } from "react";
7349
7324
 
7350
7325
  // shared/package-runtime/table-model.ts
7351
7326
  function getValue(row, key) {
@@ -7389,11 +7364,11 @@ function useTableModel(props) {
7389
7364
  expandSingle = false,
7390
7365
  onExpandedRowsChange
7391
7366
  } = props;
7392
- const [internalSort, setSort] = useState34({
7367
+ const [internalSort, setSort] = useState36({
7393
7368
  key: "",
7394
7369
  order: "asc"
7395
- }), [internalExpanded, setExpanded] = useState34([]), [search, setSearch] = useState34(""), timer = useRef33(void 0);
7396
- useEffect23(() => () => clearTimeout(timer.current), []);
7370
+ }), [internalExpanded, setExpanded] = useState36([]), [search, setSearch] = useState36(""), timer = useRef33(void 0);
7371
+ useEffect24(() => () => clearTimeout(timer.current), []);
7397
7372
  const sort = props.sort === void 0 ? internalSort : (_a = props.sort) != null ? _a : { key: "", order: "asc" };
7398
7373
  const rows = sortTableRows(data, sort, { sortable, sortMode });
7399
7374
  const key = (row, index = 0) => tableRowKey(row, rowKey, index);
@@ -7547,19 +7522,19 @@ function TableToolbar({
7547
7522
 
7548
7523
  // packages/native/src/table-grid.tsx
7549
7524
  import { tokens as tokens43 } from "@kjun-ui/tokens";
7550
- import { Fragment as Fragment15, useId as useId9, useState as useState35 } from "react";
7551
- import Svg7, { Defs as Defs3, LinearGradient as LinearGradient3, Rect as Rect4, Stop as Stop3 } from "react-native-svg";
7525
+ import { Fragment as Fragment15, useId as useId9, useState as useState37 } from "react";
7526
+ import Svg7, { Defs as Defs3, LinearGradient as LinearGradient3, Rect as Rect3, Stop as Stop3 } from "react-native-svg";
7552
7527
  import { ScrollView as ScrollView9, View as View37 } from "react-native";
7553
7528
  import { jsx as jsx43, jsxs as jsxs31 } from "react/jsx-runtime";
7554
- function TableGrid({ initialLoading, skeletonRows, state, onRowClick, selectable, checkbox, cell, expandable, expand, renderExpand, emptyContent, emptyText, visible, maxHeight, stickyHeader, sortable, compact, striped, hoverable }) {
7529
+ function TableGrid({ initialLoading, skeletonRows, skeletonHidden, state, onRowClick, selectable, checkbox, cell, expandable, expand, renderExpand, emptyContent, emptyText, visible, maxHeight, stickyHeader, sortable, compact, striped, hoverable }) {
7555
7530
  const { colors } = useKjunStyles();
7556
7531
  const geometry2 = tokens43.table;
7557
- const [scroll, setScroll] = useState35({ viewport: 0, x: 0 }), fadeId = "kjun-table-fade-" + useId9().replace(/[^a-z0-9]/gi, "");
7532
+ const [scroll, setScroll] = useState37({ viewport: 0, x: 0 }), fadeId = "kjun-table-fade-" + useId9().replace(/[^a-z0-9]/gi, "");
7558
7533
  const cellPadding = geometry2.cellPadding[compact ? "compact" : "regular"];
7559
7534
  const width = (col) => typeof col.width === "number" ? col.width : typeof col.width === "string" && /^\d+(px)?$/.test(col.width) ? parseFloat(col.width) : geometry2.columnWidth;
7560
7535
  const totalWidth = visible.reduce((sum, col) => sum + width(col), 0) + (selectable ? geometry2.actionColumnWidth : 0) + (expandable ? geometry2.actionColumnWidth : 0);
7561
7536
  const fade = totalWidth - scroll.viewport - scroll.x > 1;
7562
- return /* @__PURE__ */ jsxs31(View37, { style: { position: "relative", minWidth: 0 }, children: [
7537
+ return /* @__PURE__ */ jsxs31(View37, { style: { position: "relative", minWidth: 0, opacity: skeletonHidden ? 0 : void 0 }, "aria-hidden": skeletonHidden || void 0, children: [
7563
7538
  /* @__PURE__ */ jsx43(
7564
7539
  ScrollView9,
7565
7540
  {
@@ -7741,7 +7716,7 @@ function TableGrid({ initialLoading, skeletonRows, state, onRowClick, selectable
7741
7716
  /* @__PURE__ */ jsx43(Stop3, { offset: "0", stopColor: colors.surface, stopOpacity: 0 }),
7742
7717
  /* @__PURE__ */ jsx43(Stop3, { offset: "1", stopColor: colors.surface, stopOpacity: 1 })
7743
7718
  ] }) }),
7744
- /* @__PURE__ */ jsx43(Rect4, { width: "100%", height: "100%", fill: `url(#${fadeId})` })
7719
+ /* @__PURE__ */ jsx43(Rect3, { width: "100%", height: "100%", fill: `url(#${fadeId})` })
7745
7720
  ] }) })
7746
7721
  ] });
7747
7722
  }
@@ -7762,6 +7737,7 @@ import { jsx as jsx44, jsxs as jsxs32 } from "react/jsx-runtime";
7762
7737
  function TableCards({
7763
7738
  initialLoading,
7764
7739
  skeletonRows,
7740
+ skeletonHidden,
7765
7741
  state,
7766
7742
  onRowClick,
7767
7743
  selectable,
@@ -7776,149 +7752,156 @@ function TableCards({
7776
7752
  }) {
7777
7753
  const { colors } = useKjunStyles();
7778
7754
  const { title, subtitle, badges, inline, sections, actions, columnsForSection } = layout;
7779
- return /* @__PURE__ */ jsxs32(View38, { style: { gap: tokens44.extensions.tableCard.gap, padding: tokens44.extensions.tableCard.listPadding }, children: [
7780
- initialLoading ? Array.from({ length: skeletonRows }, (_, i) => /* @__PURE__ */ jsx44(KjunSkeleton, { type: "card" }, i)) : state.rows.map((row, i) => /* @__PURE__ */ jsxs32(
7781
- AccessiblePressable,
7782
- {
7783
- accessible: false,
7784
- onPress: () => onRowClick == null ? void 0 : onRowClick(row, i),
7785
- style: ({ pressed }) => ({
7786
- padding: tokens44.extensions.tableCard.padding,
7787
- borderWidth: tokens44.border.defaultWidth,
7788
- borderColor: colors.border,
7789
- borderRadius: tokens44.extensions.tableCard.radius,
7790
- backgroundColor: pressed ? colors.hover : colors.background
7791
- }),
7792
- children: [
7793
- /* @__PURE__ */ jsxs32(
7794
- View38,
7795
- {
7796
- style: {
7797
- flexDirection: "row",
7798
- alignItems: "center",
7799
- gap: tokens44.extensions.tableCard.gap,
7800
- marginBottom: tokens44.dimension.value8
7801
- },
7802
- children: [
7803
- /* @__PURE__ */ jsxs32(
7755
+ return /* @__PURE__ */ jsxs32(
7756
+ View38,
7757
+ {
7758
+ style: { gap: tokens44.extensions.tableCard.gap, padding: tokens44.extensions.tableCard.listPadding, opacity: skeletonHidden ? 0 : void 0 },
7759
+ "aria-hidden": skeletonHidden || void 0,
7760
+ children: [
7761
+ initialLoading ? Array.from({ length: skeletonRows }, (_, i) => /* @__PURE__ */ jsx44(KjunSkeleton, { type: "card" }, i)) : state.rows.map((row, i) => /* @__PURE__ */ jsxs32(
7762
+ AccessiblePressable,
7763
+ {
7764
+ accessible: false,
7765
+ onPress: () => onRowClick == null ? void 0 : onRowClick(row, i),
7766
+ style: ({ pressed }) => ({
7767
+ padding: tokens44.extensions.tableCard.padding,
7768
+ borderWidth: tokens44.border.defaultWidth,
7769
+ borderColor: colors.border,
7770
+ borderRadius: tokens44.extensions.tableCard.radius,
7771
+ backgroundColor: pressed ? colors.hover : colors.background
7772
+ }),
7773
+ children: [
7774
+ /* @__PURE__ */ jsxs32(
7775
+ View38,
7776
+ {
7777
+ style: {
7778
+ flexDirection: "row",
7779
+ alignItems: "center",
7780
+ gap: tokens44.extensions.tableCard.gap,
7781
+ marginBottom: tokens44.dimension.value8
7782
+ },
7783
+ children: [
7784
+ /* @__PURE__ */ jsxs32(
7785
+ View38,
7786
+ {
7787
+ style: {
7788
+ flexDirection: "row",
7789
+ flex: 1,
7790
+ alignItems: "center",
7791
+ gap: tokens44.extensions.tableCard.gap,
7792
+ minWidth: 0
7793
+ },
7794
+ children: [
7795
+ selectable && checkbox(row, i),
7796
+ title && /* @__PURE__ */ jsx44(View38, { style: { flex: 1, minWidth: 0 }, children: cell(title, row, i, "title") })
7797
+ ]
7798
+ }
7799
+ ),
7800
+ badges.length ? /* @__PURE__ */ jsx44(View38, { style: { flexDirection: "row", gap: tokens44.extensions.tableCard.fieldGap }, children: badges.map((col) => /* @__PURE__ */ jsx44(View38, { children: cell(col, row, i) }, col.key)) }) : subtitle ? cell(subtitle, row, i) : null,
7801
+ inline.length > 0 && /* @__PURE__ */ jsx44(View38, { style: { flexDirection: "row", gap: tokens44.extensions.tableCard.fieldGap }, children: inline.map((col) => /* @__PURE__ */ jsx44(View38, { children: cell(col, row, i) }, col.key)) })
7802
+ ]
7803
+ }
7804
+ ),
7805
+ sections.map((section, index) => {
7806
+ const cols = columnsForSection(row, section);
7807
+ return cols.length ? /* @__PURE__ */ jsxs32(
7804
7808
  View38,
7805
7809
  {
7806
7810
  style: {
7807
- flexDirection: "row",
7808
- flex: 1,
7809
- alignItems: "center",
7810
- gap: tokens44.extensions.tableCard.gap,
7811
- minWidth: 0
7811
+ borderTopWidth: index ? tokens44.border.defaultWidth : 0,
7812
+ borderTopColor: colors.border,
7813
+ paddingTop: index ? tokens44.dimension.value12 : 0,
7814
+ marginTop: index ? tokens44.dimension.value12 : 0
7812
7815
  },
7813
7816
  children: [
7814
- selectable && checkbox(row, i),
7815
- title && /* @__PURE__ */ jsx44(View38, { style: { flex: 1, minWidth: 0 }, children: cell(title, row, i, "title") })
7816
- ]
7817
- }
7818
- ),
7819
- badges.length ? /* @__PURE__ */ jsx44(View38, { style: { flexDirection: "row", gap: tokens44.extensions.tableCard.fieldGap }, children: badges.map((col) => /* @__PURE__ */ jsx44(View38, { children: cell(col, row, i) }, col.key)) }) : subtitle ? cell(subtitle, row, i) : null,
7820
- inline.length > 0 && /* @__PURE__ */ jsx44(View38, { style: { flexDirection: "row", gap: tokens44.extensions.tableCard.fieldGap }, children: inline.map((col) => /* @__PURE__ */ jsx44(View38, { children: cell(col, row, i) }, col.key)) })
7821
- ]
7822
- }
7823
- ),
7824
- sections.map((section, index) => {
7825
- const cols = columnsForSection(row, section);
7826
- return cols.length ? /* @__PURE__ */ jsxs32(
7827
- View38,
7828
- {
7829
- style: {
7830
- borderTopWidth: index ? tokens44.border.defaultWidth : 0,
7831
- borderTopColor: colors.border,
7832
- paddingTop: index ? tokens44.dimension.value12 : 0,
7833
- marginTop: index ? tokens44.dimension.value12 : 0
7834
- },
7835
- children: [
7836
- section.label && /* @__PURE__ */ jsx44(
7837
- KText,
7838
- {
7839
- style: {
7840
- ...typeStyle("controlSmall"),
7841
- color: colors.textTertiary,
7842
- marginBottom: tokens44.dimension.value6
7843
- },
7844
- children: section.label
7845
- }
7846
- ),
7847
- /* @__PURE__ */ jsx44(
7848
- View38,
7849
- {
7850
- style: {
7851
- flexDirection: "row",
7852
- flexWrap: "wrap",
7853
- gap: section.layout === "metrics" ? tokens44.extensions.tableCard.gap : tokens44.extensions.tableCard.metricGap
7854
- },
7855
- children: cols.map((col) => /* @__PURE__ */ jsxs32(
7817
+ section.label && /* @__PURE__ */ jsx44(
7818
+ KText,
7819
+ {
7820
+ style: {
7821
+ ...typeStyle("controlSmall"),
7822
+ color: colors.textTertiary,
7823
+ marginBottom: tokens44.dimension.value6
7824
+ },
7825
+ children: section.label
7826
+ }
7827
+ ),
7828
+ /* @__PURE__ */ jsx44(
7856
7829
  View38,
7857
7830
  {
7858
7831
  style: {
7859
- flexBasis: col.fullWidthInCard || section.layout === "stack" ? "100%" : section.layout === "metrics" ? "30%" : "47%",
7860
- flexGrow: 1,
7861
- minWidth: 0,
7862
- gap: tokens44.extensions.tableCard.valueGap
7832
+ flexDirection: "row",
7833
+ flexWrap: "wrap",
7834
+ gap: section.layout === "metrics" ? tokens44.extensions.tableCard.gap : tokens44.extensions.tableCard.metricGap
7863
7835
  },
7864
- children: [
7865
- /* @__PURE__ */ jsx44(
7866
- KText,
7867
- {
7868
- style: {
7869
- ...typeStyle("meta"),
7870
- color: colors.textTertiary,
7871
- textAlign: section.labelAlign
7872
- },
7873
- children: col.label
7874
- }
7875
- ),
7876
- cell(col, row, i)
7877
- ]
7878
- },
7879
- col.key
7880
- ))
7881
- }
7882
- )
7883
- ]
7884
- },
7885
- section.key || index
7886
- ) : null;
7887
- }),
7888
- expandable && expand(row, i),
7889
- expandable && state.expanded.includes(state.key(row, i)) && /* @__PURE__ */ jsx44(
7890
- View38,
7891
- {
7892
- style: {
7893
- borderTopWidth: tokens44.border.defaultWidth,
7894
- borderTopColor: colors.border,
7895
- paddingTop: tokens44.dimension.value8,
7896
- marginTop: tokens44.dimension.value4
7897
- },
7898
- children: content(renderExpand == null ? void 0 : renderExpand(row, i))
7899
- }
7900
- ),
7901
- actions && !actions.inlineInCard && /* @__PURE__ */ jsx44(
7902
- View38,
7903
- {
7904
- style: {
7905
- borderTopWidth: tokens44.border.defaultWidth,
7906
- borderTopColor: colors.border,
7907
- paddingTop: tokens44.dimension.value8,
7908
- marginTop: tokens44.dimension.value8,
7909
- alignItems: "flex-end"
7910
- },
7911
- children: /* @__PURE__ */ jsx44(View38, { style: { alignSelf: "flex-end" }, children: cell(actions, row, i) })
7912
- }
7913
- )
7914
- ]
7915
- },
7916
- state.key(row, i)
7917
- )),
7918
- !initialLoading && !state.rows.length && /* @__PURE__ */ jsx44(View38, { style: { padding: tokens44.extensions.tableCard.emptyPadding, alignItems: "center" }, children: content(emptyContent || emptyText, {
7919
- color: colors.textTertiary
7920
- }) })
7921
- ] });
7836
+ children: cols.map((col) => /* @__PURE__ */ jsxs32(
7837
+ View38,
7838
+ {
7839
+ style: {
7840
+ flexBasis: col.fullWidthInCard || section.layout === "stack" ? "100%" : section.layout === "metrics" ? "30%" : "47%",
7841
+ flexGrow: 1,
7842
+ minWidth: 0,
7843
+ gap: tokens44.extensions.tableCard.valueGap
7844
+ },
7845
+ children: [
7846
+ /* @__PURE__ */ jsx44(
7847
+ KText,
7848
+ {
7849
+ style: {
7850
+ ...typeStyle("meta"),
7851
+ color: colors.textTertiary,
7852
+ textAlign: section.labelAlign
7853
+ },
7854
+ children: col.label
7855
+ }
7856
+ ),
7857
+ cell(col, row, i)
7858
+ ]
7859
+ },
7860
+ col.key
7861
+ ))
7862
+ }
7863
+ )
7864
+ ]
7865
+ },
7866
+ section.key || index
7867
+ ) : null;
7868
+ }),
7869
+ expandable && expand(row, i),
7870
+ expandable && state.expanded.includes(state.key(row, i)) && /* @__PURE__ */ jsx44(
7871
+ View38,
7872
+ {
7873
+ style: {
7874
+ borderTopWidth: tokens44.border.defaultWidth,
7875
+ borderTopColor: colors.border,
7876
+ paddingTop: tokens44.dimension.value8,
7877
+ marginTop: tokens44.dimension.value4
7878
+ },
7879
+ children: content(renderExpand == null ? void 0 : renderExpand(row, i))
7880
+ }
7881
+ ),
7882
+ actions && !actions.inlineInCard && /* @__PURE__ */ jsx44(
7883
+ View38,
7884
+ {
7885
+ style: {
7886
+ borderTopWidth: tokens44.border.defaultWidth,
7887
+ borderTopColor: colors.border,
7888
+ paddingTop: tokens44.dimension.value8,
7889
+ marginTop: tokens44.dimension.value8,
7890
+ alignItems: "flex-end"
7891
+ },
7892
+ children: /* @__PURE__ */ jsx44(View38, { style: { alignSelf: "flex-end" }, children: cell(actions, row, i) })
7893
+ }
7894
+ )
7895
+ ]
7896
+ },
7897
+ state.key(row, i)
7898
+ )),
7899
+ !initialLoading && !state.rows.length && /* @__PURE__ */ jsx44(View38, { style: { padding: tokens44.extensions.tableCard.emptyPadding, alignItems: "center" }, children: content(emptyContent || emptyText, {
7900
+ color: colors.textTertiary
7901
+ }) })
7902
+ ]
7903
+ }
7904
+ );
7922
7905
  }
7923
7906
 
7924
7907
  // packages/native/src/table-view.tsx
@@ -7926,6 +7909,9 @@ import { jsx as jsx45, jsxs as jsxs33 } from "react/jsx-runtime";
7926
7909
  function KjunTable(props) {
7927
7910
  const {
7928
7911
  skeletonRows = 8,
7912
+ skeleton = true,
7913
+ loadingText = "\uB85C\uB529 \uC911...",
7914
+ loadingDelay = 0,
7929
7915
  emptyText = "\uB370\uC774\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4",
7930
7916
  hoverable = true,
7931
7917
  searchable = false,
@@ -7946,7 +7932,7 @@ function KjunTable(props) {
7946
7932
  onRowClick,
7947
7933
  onRetry
7948
7934
  } = props;
7949
- const { colors, domainColors } = useKjunStyles(), state = useTableModel2(props), query = useQueryDisplay(props), mobile = useWindowDimensions7().width < tokens45.table.mobileBreakpoint;
7935
+ const { colors, domainColors } = useKjunStyles(), state = useTableModel2(props), query = useQueryDisplay(props), revealed = useLoadingReveal(query.initialLoading, skeleton ? loadingDelay : 0), mobile = useWindowDimensions7().width < tokens45.table.mobileBreakpoint;
7950
7936
  const layout = tablePresentation(props, mobile);
7951
7937
  const { card, visible } = layout;
7952
7938
  const cell = (col, row, index, role) => {
@@ -8025,8 +8011,11 @@ function KjunTable(props) {
8025
8011
  refreshing: query.queryRefreshing,
8026
8012
  loadingPadding: "none",
8027
8013
  onRetry,
8014
+ loading: skeleton ? void 0 : props.loading,
8015
+ loadingText,
8016
+ loadingDelay: skeleton ? 0 : loadingDelay,
8028
8017
  children: [
8029
- 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 }),
8018
+ 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 }),
8030
8019
  pagination && /* @__PURE__ */ jsx45(
8031
8020
  KjunPagination,
8032
8021
  {
@@ -8087,12 +8076,13 @@ function KjunKpiHero(props) {
8087
8076
  secondary = [],
8088
8077
  animated = true,
8089
8078
  size = "md",
8079
+ surface = "card",
8090
8080
  aside,
8091
8081
  formatter,
8092
8082
  deltaFormatter,
8093
8083
  renderSecondary
8094
8084
  } = props;
8095
- const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), mobile = useWindowDimensions8().width <= tokens46.responsive.kpiHero, small = size === "sm", numberRole = small ? "displaySm" : mobile ? "displayMd" : "displayLg", font = typeStyle(numberRole).fontSize, unitStyle = typeStyle(small ? "numberSm" : mobile ? "numberMd" : "numberLg");
8085
+ const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), windowWidth = useWindowDimensions8().width, mobile = windowWidth <= tokens46.responsive.kpiHero, small = size === "sm", numberRole = small ? "displaySm" : mobile ? "displayMd" : "displayLg", font = typeStyle(numberRole).fontSize, unitStyle = typeStyle(small ? "numberSm" : mobile ? "numberMd" : "numberLg");
8096
8086
  const geometry2 = tokens46.extensions.kpiHero;
8097
8087
  const spec = small ? geometry2.sm : mobile ? geometry2.mobile : geometry2.md;
8098
8088
  const numeric = {
@@ -8122,10 +8112,13 @@ function KjunKpiHero(props) {
8122
8112
  {
8123
8113
  accessibilityState: { busy: loading },
8124
8114
  style: {
8125
- paddingVertical: spec.paddingY,
8126
- paddingHorizontal: spec.paddingX,
8127
- backgroundColor: colors.surface,
8128
- borderRadius: geometry2.radius
8115
+ // surface="none" sits inside the app's own card or list, so it drops its own card box.
8116
+ ...surface === "none" ? {} : {
8117
+ paddingVertical: spec.paddingY,
8118
+ paddingHorizontal: spec.paddingX,
8119
+ backgroundColor: colors.surface,
8120
+ borderRadius: geometry2.radius
8121
+ }
8129
8122
  },
8130
8123
  children: [
8131
8124
  /* @__PURE__ */ jsxs35(
@@ -8208,70 +8201,75 @@ function KjunKpiHero(props) {
8208
8201
  borderTopWidth: tokens46.border.defaultWidth,
8209
8202
  borderTopColor: colors.border
8210
8203
  },
8211
- children: secondary.map((item, i) => /* @__PURE__ */ jsxs35(
8212
- View41,
8213
- {
8214
- style: mobile && !small ? {
8215
- flex: 1,
8216
- minWidth: 0,
8217
- paddingLeft: i ? geometry2.itemPadding : 0,
8218
- paddingRight: i === secondary.length - 1 ? 0 : geometry2.itemPadding,
8219
- borderLeftWidth: i ? tokens46.border.defaultWidth : 0,
8220
- borderLeftColor: colors.border
8221
- } : { minWidth: 0, maxWidth: "100%", flexShrink: 1 },
8222
- children: [
8223
- /* @__PURE__ */ jsx47(
8224
- KText,
8225
- {
8226
- style: {
8227
- color: colors.textTertiary,
8228
- ...typeStyle("caption"),
8229
- marginBottom: spec.secondaryLabelGap
8230
- },
8231
- children: item.label
8232
- }
8233
- ),
8234
- loading ? /* @__PURE__ */ jsx47(
8235
- KjunSkeleton,
8236
- {
8237
- type: "block",
8238
- height: typeStyle("numberMd").lineHeight,
8239
- width: geometry2.secondarySkeletonWidth
8240
- }
8241
- ) : (renderSecondary == null ? void 0 : renderSecondary(item, i)) || /* @__PURE__ */ jsx47(
8242
- KpiNumberLine,
8243
- {
8244
- prefix: item.prefix,
8245
- suffix: item.suffix,
8246
- prefixGap: geometry2.secondaryAffixGap,
8247
- suffixGap: geometry2.secondaryAffixGap,
8248
- unitStyle: typeStyle("caption"),
8249
- style: { ...numeric, ...typeStyle("numberMd"), color: item.valueColor || (item.semantic === "price" && Number(item.value) !== 0 ? direction(Number(item.value)) : colors.text) },
8250
- children: kpiFormat(item)
8251
- }
8252
- ),
8253
- item.desc && (loading ? /* @__PURE__ */ jsx47(
8254
- KjunSkeleton,
8255
- {
8256
- type: "block",
8257
- width: geometry2.descriptionSkeletonWidth,
8258
- height: typeStyle("caption").lineHeight
8259
- }
8260
- ) : /* @__PURE__ */ jsx47(
8261
- KText,
8262
- {
8263
- style: {
8264
- ...typeStyle("caption"),
8265
- marginTop: small ? geometry2.sm.descriptionGap : geometry2.md.descriptionGap,
8266
- color: item.descColor || colors.textTertiary
8267
- },
8268
- children: item.desc
8269
- }
8270
- ))
8271
- ]
8272
- },
8273
- i
8274
- ))
8204
+ children: secondary.map((item, i) => {
8205
+ const columns = mobile && !small && windowWidth <= tokens46.responsive.kpiHeroSecondary && secondary.length >= 3 ? 2 : 0;
8206
+ const first = columns ? i % columns === 0 : i === 0;
8207
+ const last = columns ? i % columns === columns - 1 || i === secondary.length - 1 : i === secondary.length - 1;
8208
+ return /* @__PURE__ */ jsxs35(
8209
+ View41,
8210
+ {
8211
+ style: mobile && !small ? {
8212
+ ...columns ? { width: `${100 / columns}%`, marginTop: i >= columns ? spec.secondaryPadding : 0 } : { flex: 1 },
8213
+ minWidth: 0,
8214
+ paddingLeft: first ? 0 : geometry2.itemPadding,
8215
+ paddingRight: last ? 0 : geometry2.itemPadding,
8216
+ borderLeftWidth: first ? 0 : tokens46.border.defaultWidth,
8217
+ borderLeftColor: colors.border
8218
+ } : { minWidth: 0, maxWidth: "100%", flexShrink: 1 },
8219
+ children: [
8220
+ /* @__PURE__ */ jsx47(
8221
+ KText,
8222
+ {
8223
+ style: {
8224
+ color: colors.textTertiary,
8225
+ ...typeStyle("caption"),
8226
+ marginBottom: spec.secondaryLabelGap
8227
+ },
8228
+ children: item.label
8229
+ }
8230
+ ),
8231
+ loading ? /* @__PURE__ */ jsx47(
8232
+ KjunSkeleton,
8233
+ {
8234
+ type: "block",
8235
+ height: typeStyle("numberMd").lineHeight,
8236
+ width: geometry2.secondarySkeletonWidth
8237
+ }
8238
+ ) : (renderSecondary == null ? void 0 : renderSecondary(item, i)) || /* @__PURE__ */ jsx47(
8239
+ KpiNumberLine,
8240
+ {
8241
+ prefix: item.prefix,
8242
+ suffix: item.suffix,
8243
+ prefixGap: geometry2.secondaryAffixGap,
8244
+ suffixGap: geometry2.secondaryAffixGap,
8245
+ unitStyle: typeStyle("caption"),
8246
+ style: { ...numeric, ...typeStyle("numberMd"), color: item.valueColor || (item.semantic === "price" && Number(item.value) !== 0 ? direction(Number(item.value)) : colors.text) },
8247
+ children: kpiFormat(item)
8248
+ }
8249
+ ),
8250
+ item.desc && (loading ? /* @__PURE__ */ jsx47(
8251
+ KjunSkeleton,
8252
+ {
8253
+ type: "block",
8254
+ width: geometry2.descriptionSkeletonWidth,
8255
+ height: typeStyle("caption").lineHeight
8256
+ }
8257
+ ) : /* @__PURE__ */ jsx47(
8258
+ KText,
8259
+ {
8260
+ style: {
8261
+ ...typeStyle("caption"),
8262
+ marginTop: small ? geometry2.sm.descriptionGap : geometry2.md.descriptionGap,
8263
+ color: item.descColor || colors.textTertiary
8264
+ },
8265
+ children: item.desc
8266
+ }
8267
+ ))
8268
+ ]
8269
+ },
8270
+ i
8271
+ );
8272
+ })
8275
8273
  }
8276
8274
  )
8277
8275
  ]
@@ -8281,7 +8279,7 @@ function KjunKpiHero(props) {
8281
8279
 
8282
8280
  // packages/native/src/kpi-row.tsx
8283
8281
  import { tokens as tokens47 } from "@kjun-ui/tokens";
8284
- import { useState as useState36 } from "react";
8282
+ import { useState as useState38 } from "react";
8285
8283
  import {
8286
8284
  Text as Text6,
8287
8285
  View as View42,
@@ -8292,6 +8290,7 @@ function KjunKpiRow({
8292
8290
  loading = false,
8293
8291
  items,
8294
8292
  size = "md",
8293
+ surface = "card",
8295
8294
  mobileSummary = true,
8296
8295
  onItemClick,
8297
8296
  renderValue
@@ -8299,7 +8298,7 @@ function KjunKpiRow({
8299
8298
  const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), mobile = useWindowDimensions9().width <= tokens47.responsive.kpiRow, small = size === "sm", summary = mobileSummary && mobile && !small;
8300
8299
  const geometry2 = tokens47.extensions.kpiRow;
8301
8300
  const spec = small ? geometry2.sm : mobile ? geometry2.mobile : geometry2.md;
8302
- const [width, setWidth] = useState36(0);
8301
+ const [width, setWidth] = useState38(0);
8303
8302
  const paddingX = summary ? geometry2.summary.paddingX : spec.paddingX;
8304
8303
  const gapX = summary ? geometry2.summary.gapX : 0;
8305
8304
  const explicit = items.some(
@@ -8340,10 +8339,13 @@ function KjunKpiRow({
8340
8339
  style: {
8341
8340
  flexDirection: "row",
8342
8341
  flexWrap: mobile ? "wrap" : "nowrap",
8343
- paddingVertical: summary ? geometry2.summary.paddingY : spec.paddingY,
8344
- paddingHorizontal: paddingX,
8345
- borderRadius: geometry2.radius,
8346
- backgroundColor: colors.surface,
8342
+ // surface="none" sits inside the app's own card or list, so it drops its own card box.
8343
+ ...surface === "none" ? {} : {
8344
+ paddingVertical: summary ? geometry2.summary.paddingY : spec.paddingY,
8345
+ paddingHorizontal: paddingX,
8346
+ borderRadius: geometry2.radius,
8347
+ backgroundColor: colors.surface
8348
+ },
8347
8349
  rowGap: summary ? geometry2.summary.gapY : 0,
8348
8350
  columnGap: gapX
8349
8351
  },
@@ -8515,7 +8517,7 @@ import { tokens as tokens49 } from "@kjun-ui/tokens";
8515
8517
  import { ScrollView as ScrollView10, View as View44 } from "react-native";
8516
8518
 
8517
8519
  // shared/package-runtime/market-cards.ts
8518
- import { useEffect as useEffect24, useRef as useRef34, useState as useState37 } from "react";
8520
+ import { useEffect as useEffect25, useRef as useRef34, useState as useState39 } from "react";
8519
8521
  function useMarketCardsModel(props, persistence) {
8520
8522
  const {
8521
8523
  columns,
@@ -8531,7 +8533,7 @@ function useMarketCardsModel(props, persistence) {
8531
8533
  sortKey: c.sortable ? metricConfig[c.key].sortKey : null,
8532
8534
  isDefaultSort: c.defaultSort
8533
8535
  }));
8534
- const [key, setKey] = useState37(""), restoreSignature = JSON.stringify([storageNamespace, pills]);
8536
+ const [key, setKey] = useState39(""), restoreSignature = JSON.stringify([storageNamespace, pills]);
8535
8537
  const mounted = useRef34("");
8536
8538
  const save = (key2) => {
8537
8539
  var _a;
@@ -8540,7 +8542,7 @@ function useMarketCardsModel(props, persistence) {
8540
8542
  } catch (e) {
8541
8543
  }
8542
8544
  };
8543
- useEffect24(() => {
8545
+ useEffect25(() => {
8544
8546
  var _a, _b;
8545
8547
  if (mounted.current === restoreSignature) return;
8546
8548
  mounted.current = restoreSignature;
@@ -8782,7 +8784,7 @@ function KjunMarketCards(props) {
8782
8784
  ...props,
8783
8785
  loadingPadding: "none",
8784
8786
  empty: !rows.length,
8785
- loadingContent: /* @__PURE__ */ jsx50(KjunListSkeleton, { variant: "compact" }),
8787
+ loadingContent: props.skeleton === false ? void 0 : /* @__PURE__ */ jsx50(KjunListSkeleton, { variant: "compact" }),
8786
8788
  emptyContent: /* @__PURE__ */ jsx50(KjunEmpty, { text: emptyMessage, description: emptySubMessage }),
8787
8789
  children: /* @__PURE__ */ jsxs38(View44, { children: [
8788
8790
  state.pill && /* @__PURE__ */ jsx50(KText, { style: { textAlign: "right", paddingHorizontal: tokens49.dimension.value16, paddingVertical: tokens49.dimension.value8, ...typeStyle("caption"), color: colors.textSecondary }, children: state.pill.label }),
@@ -8893,7 +8895,7 @@ function KjunMarketSimpleList(props) {
8893
8895
  loadingPadding: "none",
8894
8896
  empty: !rows.length,
8895
8897
  emptyText: emptyMessage,
8896
- loadingContent: /* @__PURE__ */ jsx51(KjunListSkeleton, {}),
8898
+ loadingContent: props.skeleton === false ? void 0 : /* @__PURE__ */ jsx51(KjunListSkeleton, {}),
8897
8899
  emptyContent: /* @__PURE__ */ jsx51(KjunEmpty, { text: emptyMessage, description: emptySubMessage }),
8898
8900
  children: /* @__PURE__ */ jsx51(View45, { children: rows.map((row, i) => {
8899
8901
  var _a, _b;
@@ -9020,9 +9022,9 @@ function KjunMarketListPanel({
9020
9022
 
9021
9023
  // packages/native/src/market-table.tsx
9022
9024
  import { tokens as tokens52 } from "@kjun-ui/tokens";
9023
- import { useId as useId10, useState as useState38 } from "react";
9025
+ import { useId as useId10, useState as useState40 } from "react";
9024
9026
  import { ScrollView as ScrollView11, View as View47 } from "react-native";
9025
- import Svg8, { Defs as Defs4, LinearGradient as LinearGradient4, Rect as Rect5, Stop as Stop4 } from "react-native-svg";
9027
+ import Svg8, { Defs as Defs4, LinearGradient as LinearGradient4, Rect as Rect4, Stop as Stop4 } from "react-native-svg";
9026
9028
  import { jsx as jsx53, jsxs as jsxs41 } from "react/jsx-runtime";
9027
9029
  function KjunMarketTable(props) {
9028
9030
  const {
@@ -9106,7 +9108,7 @@ function KjunMarketTable(props) {
9106
9108
  );
9107
9109
  };
9108
9110
  const layout = useMarketLayout(columns, showActions);
9109
- const [scroll, setScroll] = useState38({ viewport: 0, x: 0 }), fadeId = "kjun-market-fade-" + useId10().replace(/[^a-z0-9]/gi, "");
9111
+ const [scroll, setScroll] = useState40({ viewport: 0, x: 0 }), fadeId = "kjun-market-fade-" + useId10().replace(/[^a-z0-9]/gi, "");
9110
9112
  const tableQuote = { price: {}, change: { fontWeight: typeStyle("body").fontWeight } };
9111
9113
  return /* @__PURE__ */ jsxs41(
9112
9114
  View47,
@@ -9124,7 +9126,7 @@ function KjunMarketTable(props) {
9124
9126
  loadingPadding: "none",
9125
9127
  empty: !rows.length,
9126
9128
  emptyText: emptyMessage,
9127
- loadingContent: /* @__PURE__ */ jsx53(KjunMarketTableSkeleton, { columns, showActions }),
9129
+ loadingContent: props.skeleton === false ? void 0 : /* @__PURE__ */ jsx53(KjunMarketTableSkeleton, { columns, showActions }),
9128
9130
  emptyContent: /* @__PURE__ */ jsx53(KjunEmpty, { text: emptyMessage, description: emptySubMessage }),
9129
9131
  children: /* @__PURE__ */ jsx53(MarketQuoteTypeContext.Provider, { value: tableQuote, children: /* @__PURE__ */ jsxs41(View47, { style: { position: "relative", minWidth: 0 }, children: [
9130
9132
  /* @__PURE__ */ jsx53(
@@ -9316,7 +9318,7 @@ function KjunMarketTable(props) {
9316
9318
  /* @__PURE__ */ jsx53(Stop4, { offset: "0", stopColor: colors.surface, stopOpacity: 0 }),
9317
9319
  /* @__PURE__ */ jsx53(Stop4, { offset: "1", stopColor: colors.surface, stopOpacity: 1 })
9318
9320
  ] }) }),
9319
- /* @__PURE__ */ jsx53(Rect5, { width: "100%", height: "100%", fill: `url(#${fadeId})` })
9321
+ /* @__PURE__ */ jsx53(Rect4, { width: "100%", height: "100%", fill: `url(#${fadeId})` })
9320
9322
  ] }) })
9321
9323
  ] }) })
9322
9324
  }
@@ -9330,7 +9332,7 @@ function KjunMarketTable(props) {
9330
9332
  // packages/native/src/list.tsx
9331
9333
  import { tokens as tokens53 } from "@kjun-ui/tokens";
9332
9334
  import { Children as Children4, createContext as createContext20, useContext as useContext17 } from "react";
9333
- import { View as View48, Platform as Platform24 } from "react-native";
9335
+ import { View as View48, Platform as Platform25 } from "react-native";
9334
9336
  import { Fragment as Fragment19, jsx as jsx54, jsxs as jsxs42 } from "react/jsx-runtime";
9335
9337
  var LastListRow = createContext20(false);
9336
9338
  function KjunListRow({ title, description, leading, trailing, actions, href, disabled = false, onPress }) {
@@ -9345,8 +9347,8 @@ function KjunListRow({ title, description, leading, trailing, actions, href, dis
9345
9347
  ] });
9346
9348
  const style = { flex: 1, minWidth: 0, minHeight: tokens53.extensions.list.minimumHeight, flexDirection: "row", alignItems: "center", gap: tokens53.extensions.list.gap, padding: tokens53.extensions.list.padding };
9347
9349
  return /* @__PURE__ */ jsxs42(View48, { role: "listitem", style: { flexDirection: "row", alignItems: "center", gap: tokens53.extensions.list.gap, borderBottomWidth: last ? 0 : tokens53.border.defaultWidth, borderBottomColor: colors.divider }, children: [
9348
- 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 ? tokens53.states.opacity.disabled : 1, backgroundColor: pressed ? colors.hover : "transparent" }), children: parts }) : /* @__PURE__ */ jsx54(View48, { style, children: parts }),
9349
- actions && /* @__PURE__ */ jsx54(View48, { pointerEvents: disabled ? "none" : "auto", ...disabled && Platform24.OS === "web" ? { inert: true } : {}, style: { maxWidth: "45%", flexDirection: "row", flexWrap: "wrap", gap: tokens53.extensions.list.actionsGap, paddingRight: tokens53.extensions.list.actionsPadding, opacity: disabled ? tokens53.states.opacity.disabled : 1 }, children: content(actions) })
9350
+ 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 ? tokens53.states.opacity.disabled : 1, backgroundColor: pressed ? colors.hover : "transparent" }), children: parts }) : /* @__PURE__ */ jsx54(View48, { style, children: parts }),
9351
+ actions && /* @__PURE__ */ jsx54(View48, { pointerEvents: disabled ? "none" : "auto", ...disabled && Platform25.OS === "web" ? { inert: true } : {}, style: { maxWidth: "45%", flexDirection: "row", flexWrap: "wrap", gap: tokens53.extensions.list.actionsGap, paddingRight: tokens53.extensions.list.actionsPadding, opacity: disabled ? tokens53.states.opacity.disabled : 1 }, children: content(actions) })
9350
9352
  ] });
9351
9353
  }
9352
9354
  function KjunListSection({ title, description, actions, children, ariaLabel }) {
@@ -9365,16 +9367,16 @@ function KjunListSection({ title, description, actions, children, ariaLabel }) {
9365
9367
 
9366
9368
  // packages/native/src/app-navigation.tsx
9367
9369
  import { tokens as tokens54 } from "@kjun-ui/tokens";
9368
- import { Children as Children5, Fragment as Fragment20, isValidElement as isValidElement7, useState as useState39 } from "react";
9369
- import { View as View49, Platform as Platform25 } from "react-native";
9370
+ import { Children as Children5, Fragment as Fragment20, isValidElement as isValidElement7, useState as useState41 } from "react";
9371
+ import { View as View49, Platform as Platform26 } from "react-native";
9370
9372
  import { jsx as jsx55, jsxs as jsxs43 } from "react/jsx-runtime";
9371
9373
  function KjunTopNavigation({ title, description, leading, actions, safeAreaTop = 0 }) {
9372
9374
  const { colors } = useKjunStyles();
9373
9375
  const geometry2 = tokens54.extensions.topNavigation;
9374
9376
  const titleInset = Math.max(0, (geometry2.controlSize - tokens54.typography.sectionTitle.lineHeightPx) / 2);
9375
- const wrapping = Platform25.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : {};
9377
+ const wrapping = Platform26.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : {};
9376
9378
  const edgeInset = (geometry2.controlSize - geometry2.iconSize) / 2;
9377
- const [iconEdges, setIconEdges] = useState39({ leading: false, trailing: false });
9379
+ const [iconEdges, setIconEdges] = useState41({ leading: false, trailing: false });
9378
9380
  const square = (key) => (event) => {
9379
9381
  const { width, height } = event.nativeEvent.layout;
9380
9382
  const next = Math.abs(width - height) < 1 && Math.abs(height - geometry2.controlSize) < 1;
@@ -9419,8 +9421,8 @@ function KjunBottomNavigation({ items, value, ariaLabel = "\uC8FC\uC694 \uD0D0\u
9419
9421
  AccessiblePressable,
9420
9422
  {
9421
9423
  focusRingInset: true,
9422
- ...Platform25.OS === "web" ? { href: item.disabled ? void 0 : item.href, "aria-current": selected ? "page" : void 0 } : {},
9423
- accessibilityRole: Platform25.OS === "web" && item.href ? "link" : "button",
9424
+ ...Platform26.OS === "web" ? { href: item.disabled ? void 0 : item.href, "aria-current": selected ? "page" : void 0 } : {},
9425
+ accessibilityRole: Platform26.OS === "web" && item.href ? "link" : "button",
9424
9426
  accessibilityLabel: item.label + (item.badge != null ? ", " + item.badge : ""),
9425
9427
  accessibilityState: { selected, disabled: !!item.disabled },
9426
9428
  disabled: item.disabled,
@@ -9454,10 +9456,10 @@ function KjunBottomNavigation({ items, value, ariaLabel = "\uC8FC\uC694 \uD0D0\u
9454
9456
  }
9455
9457
  function KjunBottomActionBar({ description, children, safeAreaBottom = 0, keyboardVisible = false, hideOnKeyboard = false }) {
9456
9458
  const { colors } = useKjunStyles();
9457
- const [width, setWidth] = useState39(0);
9459
+ const [width, setWidth] = useState41(0);
9458
9460
  const geometry2 = tokens54.extensions.navigation;
9459
9461
  const wide = width >= tokens54.responsive.bottomAction + geometry2.padding * 2;
9460
- const wrapping = Platform25.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : {};
9462
+ const wrapping = Platform26.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : {};
9461
9463
  if (keyboardVisible && hideOnKeyboard) return null;
9462
9464
  return /* @__PURE__ */ jsx55(BottomActionBarContext.Provider, { value: !wide, children: /* @__PURE__ */ jsxs43(View49, { onLayout: (event) => setWidth(event.nativeEvent.layout.width), style: {
9463
9465
  flexShrink: 0,
@@ -9476,7 +9478,7 @@ function KjunBottomActionBar({ description, children, safeAreaBottom = 0, keyboa
9476
9478
  }
9477
9479
 
9478
9480
  // packages/native/src/media.tsx
9479
- import { useRef as useRef35, useState as useState40 } from "react";
9481
+ import { useRef as useRef35, useState as useState42 } from "react";
9480
9482
  import { Image, View as View50 } from "react-native";
9481
9483
  import { tokens as tokens55 } from "@kjun-ui/tokens";
9482
9484
  import { jsx as jsx56 } from "react/jsx-runtime";
@@ -9485,7 +9487,7 @@ function KjunImage({ src, source, alt, decorative = false, aspectRatio = 1, fit
9485
9487
  const request = JSON.stringify(source != null ? source : src), current = useRef35({ request, version: 0 });
9486
9488
  if (current.current.request !== request) current.current = { request, version: current.current.version + 1 };
9487
9489
  const version = current.current.version;
9488
- const [failed, setFailed] = useState40(null);
9490
+ const [failed, setFailed] = useState42(null);
9489
9491
  const missing = !source && !src;
9490
9492
  return /* @__PURE__ */ jsx56(View50, { 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: () => {
9491
9493
  if (version === current.current.version) onLoad == null ? void 0 : onLoad();
@@ -9515,8 +9517,8 @@ function KjunChip({ label, icon, removable = false, disabled = false, size = "md
9515
9517
  }
9516
9518
 
9517
9519
  // packages/native/src/range-input.tsx
9518
- import { useEffect as useEffect25, useMemo as useMemo2, useRef as useRef36, useState as useState41 } from "react";
9519
- import { PanResponder, Platform as Platform26, Pressable as Pressable3, View as View52 } from "react-native";
9520
+ import { useEffect as useEffect26, useMemo as useMemo2, useRef as useRef36, useState as useState43 } from "react";
9521
+ import { PanResponder, Platform as Platform27, Pressable as Pressable3, View as View52 } from "react-native";
9520
9522
  import { tokens as tokens57, createSliderDomain, sliderKey } from "@kjun-ui/tokens";
9521
9523
  import { jsx as jsx58, jsxs as jsxs45 } from "react/jsx-runtime";
9522
9524
  var geometry = tokens57.extensions.slider;
@@ -9539,18 +9541,18 @@ function RangeInput({
9539
9541
  const valid = domain.valid && (!Array.isArray(value) || value.length === 2) && values.every(domain.accepts) && (values.length === 1 || values[0] <= values[1]);
9540
9542
  const blocked = disabled || !valid;
9541
9543
  const effectiveMax = valid ? domain.snap(max) : min;
9542
- const [hovered, setHovered] = useState41(null);
9543
- const [dragging, setDragging] = useState41(null);
9544
- const [focused, setFocused] = useState41(null);
9544
+ const [hovered, setHovered] = useState43(null);
9545
+ const [dragging, setDragging] = useState43(null);
9546
+ const [focused, setFocused] = useState43(null);
9545
9547
  const track = useRef36(null), measured = useRef36({ x: 0, width: 1 }), selected = useRef36(0), dirty = useRef36(false);
9546
9548
  const latest = useRef36(values);
9547
9549
  latest.current = values;
9548
9550
  const callbacks = useRef36({ onValueChange, onChangeCommit });
9549
9551
  callbacks.current = { onValueChange, onChangeCommit };
9550
- useEffect25(() => {
9552
+ useEffect26(() => {
9551
9553
  if (!valid) console.warn("KJUN Slider: \uBC94\uC704\xB7step\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
9552
9554
  }, [valid]);
9553
- useEffect25(() => {
9555
+ useEffect26(() => {
9554
9556
  if (blocked) {
9555
9557
  setDragging(null);
9556
9558
  setHovered(null);
@@ -9747,7 +9749,7 @@ function RangeInput({
9747
9749
  setFocused(null);
9748
9750
  commit();
9749
9751
  },
9750
- ...Platform26.OS === "web" ? {
9752
+ ...Platform27.OS === "web" ? {
9751
9753
  "aria-valuenow": number,
9752
9754
  "aria-valuemin": index === 1 ? values[0] : min,
9753
9755
  "aria-valuemax": values.length === 2 && index === 0 ? values[1] : domain.snap(max),
@@ -9771,7 +9773,7 @@ function RangeInput({
9771
9773
  alignItems: "center",
9772
9774
  justifyContent: "center",
9773
9775
  zIndex: focused === index || dragging === index ? 1 : 0,
9774
- ...Platform26.OS === "web" ? {
9776
+ ...Platform27.OS === "web" ? {
9775
9777
  outlineStyle: "solid",
9776
9778
  outlineWidth: 0,
9777
9779
  cursor: blocked ? "default" : dragging === index ? "grabbing" : "grab"
@@ -9788,7 +9790,7 @@ function RangeInput({
9788
9790
  borderWidth: tokens57.border.controlWidth,
9789
9791
  borderColor: !blocked && dragging === index ? colors.brandActive : !blocked && hovered === index ? colors.brandHover : colors.brand,
9790
9792
  backgroundColor: colors.surface,
9791
- ...Platform26.OS === "web" && !blocked && focused === index ? {
9793
+ ...Platform27.OS === "web" && !blocked && focused === index ? {
9792
9794
  outlineStyle: "solid",
9793
9795
  outlineWidth: tokens57.states.focus.width,
9794
9796
  outlineOffset: tokens57.states.focus.choiceOffset,
@@ -9815,8 +9817,8 @@ function KjunRangeSlider(props) {
9815
9817
  }
9816
9818
 
9817
9819
  // packages/native/src/quantity-stepper.tsx
9818
- import { useEffect as useEffect26, useRef as useRef37, useState as useState42 } from "react";
9819
- import { Platform as Platform27, View as View53 } from "react-native";
9820
+ import { useEffect as useEffect27, useRef as useRef37, useState as useState44 } from "react";
9821
+ import { Platform as Platform28, View as View53 } from "react-native";
9820
9822
  import { createNumberDomain, quantityKeyAction, tokens as tokens58 } from "@kjun-ui/tokens";
9821
9823
 
9822
9824
  // shared/package-runtime/component-icons.ts
@@ -9837,16 +9839,16 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9837
9839
  const increaseStyle = { ...buttonStyle, borderRadius: 0, borderTopRightRadius: outer, borderBottomRightRadius: outer };
9838
9840
  const domain = createNumberDomain({ min, max, step, precision }), valid = domain.valid && domain.accepts(value), blocked = disabled || !valid;
9839
9841
  const surface = useFieldSurface(size, { disabled: blocked, invalid });
9840
- const [draft, setDraft] = useState42(String(value));
9842
+ const [draft, setDraft] = useState44(String(value));
9841
9843
  const inputRef = useRef37(null);
9842
9844
  const composing = useRef37(false), dirty = useRef37(false), accepted = useRef37(value);
9843
9845
  const cancelledEscape = useRef37(false);
9844
- useEffect26(() => {
9846
+ useEffect27(() => {
9845
9847
  setDraft(String(value));
9846
9848
  accepted.current = value;
9847
9849
  dirty.current = false;
9848
9850
  }, [value, precision, min, max, step]);
9849
- useEffect26(() => {
9851
+ useEffect27(() => {
9850
9852
  if (!valid) console.warn("KJUN QuantityStepper: \uBC94\uC704\xB7step\xB7\uC815\uBC00\uB3C4\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
9851
9853
  }, [valid]);
9852
9854
  const emit = (next) => {
@@ -9891,8 +9893,8 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9891
9893
  dirty.current = true;
9892
9894
  setDraft(text);
9893
9895
  } };
9894
- useEffect26(() => {
9895
- if (Platform27.OS !== "web") return;
9896
+ useEffect27(() => {
9897
+ if (Platform28.OS !== "web") return;
9896
9898
  const input = inputRef.current;
9897
9899
  const keydown = (event) => handlers.current.key(event);
9898
9900
  const keyup = (event) => {
@@ -9921,7 +9923,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9921
9923
  };
9922
9924
  }, []);
9923
9925
  const surfaceBox = { position: "absolute", left: 0, right: 0, top: "50%", height: tokens58.input[size].height, marginTop: -tokens58.input[size].height / 2, borderRadius: surface.spec.radius };
9924
- const hug = Platform27.OS === "web" && !block ? { width: "fit-content" } : {};
9926
+ const hug = Platform28.OS === "web" && !block ? { width: "fit-content" } : {};
9925
9927
  return /* @__PURE__ */ jsx59(IconFallbacks, { icons: componentIcons, children: /* @__PURE__ */ jsxs46(View53, { style: { flexDirection: "row", alignItems: "center", alignSelf: block ? "stretch" : "flex-start", maxWidth: "100%", borderRadius: surface.spec.radius, ...hug }, children: [
9926
9928
  /* @__PURE__ */ jsx59(View53, { pointerEvents: "none", "aria-hidden": true, style: { ...surfaceBox, backgroundColor: colors.inputBg } }),
9927
9929
  /* @__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" }) }),
@@ -9952,8 +9954,8 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9952
9954
  surface.setFocused(false);
9953
9955
  commit();
9954
9956
  },
9955
- onSubmitEditing: Platform27.OS === "web" ? void 0 : commit,
9956
- ...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 },
9957
+ onSubmitEditing: Platform28.OS === "web" ? void 0 : commit,
9958
+ ...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 },
9957
9959
  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 - tokens58.border.controlWidth, paddingVertical: 0 }
9958
9960
  }
9959
9961
  ) }),
@@ -9963,7 +9965,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9963
9965
  }
9964
9966
 
9965
9967
  // packages/native/src/time-picker.tsx
9966
- import { Fragment as Fragment21, useEffect as useEffect27, useId as useId11, useMemo as useMemo3, useRef as useRef38, useState as useState43 } from "react";
9968
+ import { Fragment as Fragment21, useEffect as useEffect28, useId as useId11, useMemo as useMemo3, useRef as useRef38, useState as useState45 } from "react";
9967
9969
  import { View as View54 } from "react-native";
9968
9970
  import { createTimeDomain, tokens as tokens59 } from "@kjun-ui/tokens";
9969
9971
  import { jsx as jsx60, jsxs as jsxs47 } from "react/jsx-runtime";
@@ -9971,11 +9973,11 @@ function KjunTimePicker({ value, precision = "minute", min, max, minuteStep = 1,
9971
9973
  const geometry2 = tokens59.extensions.timePicker, spec = tokens59.input[size];
9972
9974
  const { colors } = useKjunStyles();
9973
9975
  const first = useRef38(null);
9974
- const [focused, setFocused] = useState43(false), [opened, setOpened] = useState43(null);
9976
+ const [focused, setFocused] = useState45(false), [opened, setOpened] = useState45(null);
9975
9977
  const field = useKjunField(), uid = useId11();
9976
9978
  const fieldId = (field == null ? void 0 : field.id) || `kjun-time-${uid}`;
9977
9979
  const domain = useMemo3(() => createTimeDomain({ precision, min, max, minuteStep, secondStep }), [precision, min, max, minuteStep, secondStep]), valid = domain.valid && domain.accepts(value);
9978
- useEffect27(() => {
9980
+ useEffect28(() => {
9979
9981
  if (!valid) console.warn("KJUN TimePicker: \uC2DC\uAC01 \uBC94\uC704\xB7\uAC04\uACA9\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
9980
9982
  }, [valid]);
9981
9983
  const change = (next) => {