@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.cjs CHANGED
@@ -2322,36 +2322,6 @@ function KjunSkeleton({
2322
2322
  ))
2323
2323
  }
2324
2324
  ),
2325
- type === "chart" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native16.View, { style: [frame, { height: height || import_tokens13.tokens.extensions.skeleton.chartHeight }], children: [
2326
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2327
- import_react_native16.View,
2328
- {
2329
- style: {
2330
- flex: 1,
2331
- flexDirection: "row",
2332
- alignItems: "flex-end",
2333
- justifyContent: "space-around",
2334
- gap: import_tokens13.tokens.extensions.skeleton.gap
2335
- // Bars stand on the axis line, like ChartSkeleton.
2336
- },
2337
- children: [40, 65, 50, 80, 60, 90, 75].map((h, i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2338
- import_react_native16.Animated.View,
2339
- {
2340
- style: {
2341
- flex: 1,
2342
- maxWidth: import_tokens13.tokens.extensions.skeleton.chartBarMaxWidth,
2343
- height: `${h}%`,
2344
- backgroundColor: shimmer,
2345
- borderTopLeftRadius: import_tokens13.tokens.radius.radius4,
2346
- borderTopRightRadius: import_tokens13.tokens.radius.radius4
2347
- }
2348
- },
2349
- i
2350
- ))
2351
- }
2352
- ),
2353
- block("100%", import_tokens13.tokens.extensions.skeleton.chartAxisHeight, { backgroundColor: colors.border })
2354
- ] }),
2355
2325
  type === "stat" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
2356
2326
  import_react_native16.View,
2357
2327
  {
@@ -6082,12 +6052,27 @@ function KjunScrollFade({ children }) {
6082
6052
 
6083
6053
  // packages/native/src/data-state.tsx
6084
6054
  var import_tokens35 = require("@kjun-ui/tokens");
6085
- var import_react50 = require("react");
6055
+ var import_react51 = require("react");
6086
6056
  var import_react_native40 = require("react-native");
6057
+
6058
+ // shared/package-runtime/use-loading-reveal.ts
6059
+ var import_react50 = require("react");
6060
+ function useLoadingReveal(active, delay = 0) {
6061
+ const [revealed, setRevealed] = (0, import_react50.useState)(false);
6062
+ (0, import_react50.useEffect)(() => {
6063
+ setRevealed(false);
6064
+ if (!active || delay <= 0) return;
6065
+ const timer = setTimeout(() => setRevealed(true), delay);
6066
+ return () => clearTimeout(timer);
6067
+ }, [active, delay]);
6068
+ return delay <= 0 || revealed;
6069
+ }
6070
+
6071
+ // packages/native/src/data-state.tsx
6087
6072
  var import_jsx_runtime37 = require("react/jsx-runtime");
6088
6073
  function useQueryDisplay(props) {
6089
6074
  var _a;
6090
- const [state, setState] = (0, import_react50.useState)(() => (0, import_tokens35.initialQueryDisplay)(props));
6075
+ const [state, setState] = (0, import_react51.useState)(() => (0, import_tokens35.initialQueryDisplay)(props));
6091
6076
  let next = state;
6092
6077
  if (state.queryKey !== ((_a = props.queryKey) != null ? _a : null) || state.resultKey !== props.resultKey || state.loading !== !!props.loading) {
6093
6078
  next = (0, import_tokens35.advanceQueryDisplay)(state, props);
@@ -6106,6 +6091,7 @@ function KjunDataState({
6106
6091
  emptyIcon,
6107
6092
  emptyActionText,
6108
6093
  loadingText = "\uB85C\uB529 \uC911...",
6094
+ loadingDelay = 0,
6109
6095
  retryText = "\uB2E4\uC2DC \uC2DC\uB3C4",
6110
6096
  size = "md",
6111
6097
  skeleton = false,
@@ -6119,7 +6105,7 @@ function KjunDataState({
6119
6105
  onEmptyAction,
6120
6106
  ...query
6121
6107
  }) {
6122
- 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_tokens35.tokens.extensions.state.padding[size];
6108
+ 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_tokens35.tokens.extensions.state.padding[size], revealed = useLoadingReveal(state.initialLoading, loadingDelay);
6123
6109
  const { colors } = useKjunStyles();
6124
6110
  return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
6125
6111
  import_react_native40.View,
@@ -6152,8 +6138,10 @@ function KjunDataState({
6152
6138
  state.initialLoading ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
6153
6139
  import_react_native40.View,
6154
6140
  {
6155
- accessibilityLabel: loadingText,
6156
- style: { paddingVertical: loadingPadding === "none" ? 0 : padding },
6141
+ accessibilityLabel: revealed ? loadingText : void 0,
6142
+ "aria-hidden": revealed ? void 0 : true,
6143
+ importantForAccessibility: revealed ? void 0 : "no-hide-descendants",
6144
+ style: { paddingVertical: loadingPadding === "none" ? 0 : padding, opacity: revealed ? void 0 : 0 },
6157
6145
  children: loadingContent || (skeleton ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_react_native40.View, { style: { gap: import_tokens35.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_tokens35.tokens.dimension.value12 }, children: [
6158
6146
  /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(KjunSpinner, { size: "md" }),
6159
6147
  /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(KText, { style: { ...typeStyle("body"), color: colors.textSecondary, textAlign: "center", maxWidth: "100%" }, children: loadingText })
@@ -6187,7 +6175,7 @@ function BoundaryDescription() {
6187
6175
  const { colors } = useKjunStyles();
6188
6176
  return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(KText, { style: { ...typeStyle({ ...import_tokens35.tokens.typography.caption, fontSizePx: import_tokens35.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." });
6189
6177
  }
6190
- var KjunErrorBoundary = class extends import_react50.Component {
6178
+ var KjunErrorBoundary = class extends import_react51.Component {
6191
6179
  constructor() {
6192
6180
  super(...arguments);
6193
6181
  this.state = { error: false };
@@ -6234,10 +6222,24 @@ var KjunErrorBoundary = class extends import_react50.Component {
6234
6222
 
6235
6223
  // packages/native/src/skeletons.tsx
6236
6224
  var import_tokens39 = require("@kjun-ui/tokens");
6237
- var import_react52 = require("react");
6225
+ var import_react53 = require("react");
6238
6226
  var import_react_native42 = require("react-native");
6239
6227
  var import_react_native_svg5 = __toESM(require("react-native-svg"), 1);
6240
6228
 
6229
+ // shared/package-runtime/chart-skeleton.ts
6230
+ var chartFrameViewBox = "0 0 600 240";
6231
+ var chartFramePath = "M20 10V220H590 M20 65H590 M20 120H590 M20 175H590";
6232
+ var chartSkeletonMaxCount = 100;
6233
+ function chartSkeletonCount(value) {
6234
+ return Number.isFinite(value) ? Math.min(chartSkeletonMaxCount, Math.max(1, Math.floor(value))) : 1;
6235
+ }
6236
+ function chartMatrixMuted(row, column) {
6237
+ return (row + column + 2) % 3 === 0;
6238
+ }
6239
+ function chartDonutHole(height, thickness) {
6240
+ return Math.max(0, height * 0.8 - 2 * thickness);
6241
+ }
6242
+
6241
6243
  // packages/native/src/market-skeleton.tsx
6242
6244
  var import_tokens38 = require("@kjun-ui/tokens");
6243
6245
  var import_react_native41 = require("react-native");
@@ -6263,9 +6265,9 @@ function nativeMarketLayout(columns, showActions, containerWidth) {
6263
6265
 
6264
6266
  // packages/native/src/market-layout.ts
6265
6267
  var import_tokens37 = require("@kjun-ui/tokens");
6266
- var import_react51 = require("react");
6268
+ var import_react52 = require("react");
6267
6269
  function useMarketLayout(columns, showActions) {
6268
- const [containerWidth, setContainerWidth] = (0, import_react51.useState)(0);
6270
+ const [containerWidth, setContainerWidth] = (0, import_react52.useState)(0);
6269
6271
  const onLayout = (event) => setContainerWidth(event.nativeEvent.layout.width);
6270
6272
  return { ...nativeMarketLayout(columns, showActions, containerWidth), onLayout };
6271
6273
  }
@@ -6395,26 +6397,23 @@ function KjunFormSkeleton({
6395
6397
  }
6396
6398
  );
6397
6399
  }
6400
+ var chartPart = (name) => import_react_native42.Platform.OS === "web" ? { dataSet: { kjunChartPart: name } } : {};
6398
6401
  function KjunChartSkeleton({
6402
+ kind = "frame",
6403
+ height = import_tokens39.tokens.extensions.chartSkeleton.height,
6399
6404
  loadingText = "\uCC28\uD2B8\uB97C \uBD88\uB7EC\uC624\uB294 \uC911",
6400
- kind = "line",
6401
- height = import_tokens39.tokens.extensions.chartSkeleton.height
6405
+ skeleton = true,
6406
+ loadingDelay = 0,
6407
+ rows = 8,
6408
+ columns = 14
6402
6409
  }) {
6403
- const { colors } = useKjunStyles(), reduced = useReducedMotion(), opacity = (0, import_react52.useRef)(new import_react_native42.Animated.Value(1)).current;
6404
- (0, import_react52.useEffect)(() => {
6405
- if (reduced) return;
6410
+ const { colors } = useKjunStyles(), reduced = useReducedMotion(), opacity = (0, import_react53.useRef)(new import_react_native42.Animated.Value(1)).current, revealed = useLoadingReveal(true, loadingDelay), [measured, setMeasured] = (0, import_react53.useState)(0), thickness = import_tokens39.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" };
6411
+ (0, import_react53.useEffect)(() => {
6412
+ if (reduced || !skeleton) return;
6406
6413
  const loop = import_react_native42.Animated.loop(
6407
6414
  import_react_native42.Animated.sequence([
6408
- import_react_native42.Animated.timing(opacity, {
6409
- toValue: 0.55,
6410
- duration: import_tokens39.tokens.motion.shimmer / 2,
6411
- useNativeDriver: true
6412
- }),
6413
- import_react_native42.Animated.timing(opacity, {
6414
- toValue: 1,
6415
- duration: import_tokens39.tokens.motion.shimmer / 2,
6416
- useNativeDriver: true
6417
- })
6415
+ import_react_native42.Animated.timing(opacity, { toValue: 0.55, duration: import_tokens39.tokens.motion.shimmer / 2, useNativeDriver: true }),
6416
+ import_react_native42.Animated.timing(opacity, { toValue: 1, duration: import_tokens39.tokens.motion.shimmer / 2, useNativeDriver: true })
6418
6417
  ])
6419
6418
  );
6420
6419
  loop.start();
@@ -6422,99 +6421,74 @@ function KjunChartSkeleton({
6422
6421
  loop.stop();
6423
6422
  opacity.setValue(1);
6424
6423
  };
6425
- }, [opacity, reduced]);
6426
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
6424
+ }, [opacity, reduced, skeleton]);
6425
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
6427
6426
  import_react_native42.Animated.View,
6428
6427
  {
6429
- style: {
6430
- height,
6431
- alignItems: "center",
6432
- justifyContent: "center",
6433
- width: "100%",
6434
- minWidth: 0,
6435
- opacity
6436
- },
6437
- accessibilityElementsHidden: true,
6438
- importantForAccessibility: "no-hide-descendants",
6439
- children: [
6428
+ onLayout: typeof height === "number" ? void 0 : (event) => setMeasured(event.nativeEvent.layout.height),
6429
+ style: { height, alignItems: "center", justifyContent: "center", width: "100%", minWidth: 0, opacity: revealed ? opacity : 0 },
6430
+ "aria-hidden": revealed ? void 0 : true,
6431
+ importantForAccessibility: revealed ? void 0 : "no-hide-descendants",
6432
+ ...import_react_native42.Platform.OS === "web" ? { dataSet: { kjunChartKind: kind, skeleton: skeleton ? void 0 : "false" } } : {},
6433
+ children: !skeleton ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_react_native42.View, { ...chartPart("loading"), role: status, style: { alignItems: "center", gap: import_tokens39.tokens.dimension.value12 }, children: [
6434
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_react_native42.View, { ...hidden, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(KjunSpinner, { size: "md" }) }),
6435
+ !!loadingText && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(KText, { style: { ...typeStyle("body"), color: colors.textSecondary, textAlign: "center", maxWidth: "100%" }, children: loadingText })
6436
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
6440
6437
  kind === "donut" ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
6441
6438
  import_react_native42.View,
6442
6439
  {
6443
- style: {
6444
- height: "80%",
6445
- aspectRatio: 1,
6446
- borderWidth: import_tokens39.tokens.extensions.chartSkeleton.donutThickness,
6447
- borderColor: colors.skeleton,
6448
- borderRadius: import_tokens39.tokens.radius.radius9999,
6449
- alignItems: "center",
6450
- justifyContent: "center"
6451
- },
6452
- children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(KjunSkeleton, { type: "block", width: import_tokens39.tokens.extensions.chartSkeleton.donutLabelWidth, height: import_tokens39.tokens.extensions.chartSkeleton.donutLabelHeight })
6440
+ ...chartPart("donut"),
6441
+ ...hidden,
6442
+ style: { height: "80%", aspectRatio: 1, borderWidth: thickness, borderColor: colors.skeleton, borderRadius: import_tokens39.tokens.radius.radius9999 }
6453
6443
  }
6454
- ) : /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
6455
- import_react_native_svg5.default,
6444
+ ) : kind === "matrix" ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_react_native42.View, { ...chartPart("matrix"), ...hidden, style: { alignSelf: "stretch", flex: 1, gap: import_tokens39.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_tokens39.tokens.dimension.value4 }, children: Array.from({ length: columnCount }, (_2, column) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
6445
+ import_react_native42.View,
6456
6446
  {
6457
- viewBox: "0 0 600 240",
6458
- preserveAspectRatio: "none",
6459
- width: "100%",
6460
- height: "100%",
6461
- children: [
6462
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
6463
- import_react_native_svg5.Path,
6464
- {
6465
- d: "M20 10V220H590 M20 65H590 M20 120H590 M20 175H590",
6466
- fill: "none",
6467
- stroke: colors.border,
6468
- strokeWidth: 1
6469
- }
6470
- ),
6471
- 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" }),
6472
- 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)(
6473
- import_react_native_svg5.Rect,
6474
- {
6475
- x: 24 + col * 40,
6476
- y: 12 + row * 26,
6477
- width: 36,
6478
- height: 22,
6479
- rx: 2,
6480
- fill: colors.skeleton,
6481
- opacity: (row + col + 2) % 3 === 0 ? 0.55 : 1
6447
+ ...chartPart("cell"),
6448
+ style: {
6449
+ flex: 1,
6450
+ borderRadius: import_tokens39.tokens.extensions.skeleton.lineRadius,
6451
+ backgroundColor: colors.skeleton,
6452
+ opacity: chartMatrixMuted(row, column) ? import_tokens39.tokens.states.opacity.pending : 1
6453
+ }
6454
+ },
6455
+ column
6456
+ )) }, 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_tokens39.tokens.border.defaultWidth, vectorEffect: "non-scaling-stroke" }) }) }),
6457
+ !!loadingText && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
6458
+ import_react_native42.View,
6459
+ {
6460
+ ...chartPart("status"),
6461
+ role: status,
6462
+ pointerEvents: "none",
6463
+ style: { position: "absolute", inset: 0, alignItems: "center", justifyContent: "center" },
6464
+ children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
6465
+ KText,
6466
+ {
6467
+ style: {
6468
+ ...typeStyle("body"),
6469
+ paddingHorizontal: import_tokens39.tokens.dimension.value12,
6470
+ paddingVertical: import_tokens39.tokens.dimension.value4,
6471
+ borderRadius: import_tokens39.tokens.radius.radius6,
6472
+ borderWidth: kind === "donut" ? 0 : import_tokens39.tokens.border.defaultWidth,
6473
+ borderColor: colors.chartGrid,
6474
+ backgroundColor: colors.surface,
6475
+ color: colors.textSecondary,
6476
+ maxWidth: kind === "donut" && hole != null ? hole : "90%",
6477
+ textAlign: "center"
6482
6478
  },
6483
- col
6484
- )) }, row)) : (kind === "bar" || kind === "candle") && [60, 100, 85, 140, 110, 165, 180].map((h, i) => /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_react52.Fragment, { children: [
6485
- kind === "candle" && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
6486
- import_react_native_svg5.Path,
6487
- {
6488
- d: `M${55 + i * 75} ${195 - h}V${225 - h / 2}`,
6489
- fill: "none",
6490
- stroke: colors.skeleton,
6491
- strokeWidth: 4
6492
- }
6493
- ),
6494
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
6495
- import_react_native_svg5.Rect,
6496
- {
6497
- x: 35 + i * 75,
6498
- y: kind === "candle" ? 210 - h : 220 - h,
6499
- width: kind === "candle" ? 28 : 42,
6500
- height: kind === "candle" ? h / 2 : h,
6501
- rx: 3,
6502
- fill: colors.skeleton
6503
- }
6504
- )
6505
- ] }, i))
6506
- ]
6479
+ children: loadingText
6480
+ }
6481
+ )
6507
6482
  }
6508
- ),
6509
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_react_native42.View, { pointerEvents: "none", style: { position: "absolute", inset: 0, alignItems: "center", justifyContent: "center", gap: import_tokens39.tokens.dimension.value12 }, children: !!loadingText && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(KText, { style: { ...typeStyle("body"), paddingHorizontal: import_tokens39.tokens.dimension.value12, paddingVertical: import_tokens39.tokens.dimension.value4, borderRadius: import_tokens39.tokens.radius.radius6, backgroundColor: colors.surface, color: colors.textSecondary, maxWidth: "90%", textAlign: "center" }, children: loadingText }) })
6510
- ]
6483
+ )
6484
+ ] })
6511
6485
  }
6512
6486
  );
6513
6487
  }
6514
6488
 
6515
6489
  // packages/native/src/financial-display.tsx
6516
6490
  var import_tokens42 = require("@kjun-ui/tokens");
6517
- var import_react55 = require("react");
6491
+ var import_react56 = require("react");
6518
6492
  var import_react_native44 = require("react-native");
6519
6493
  var import_react_native_svg6 = __toESM(require("react-native-svg"), 1);
6520
6494
 
@@ -6522,7 +6496,7 @@ var import_react_native_svg6 = __toESM(require("react-native-svg"), 1);
6522
6496
  var import_tokens41 = require("@kjun-ui/tokens");
6523
6497
 
6524
6498
  // shared/package-runtime/use-number-motion.ts
6525
- var import_react53 = require("react");
6499
+ var import_react54 = require("react");
6526
6500
 
6527
6501
  // shared/package-runtime/number-motion.ts
6528
6502
  var import_tokens40 = require("@kjun-ui/tokens");
@@ -6568,14 +6542,14 @@ function createNumberMotion(apply) {
6568
6542
 
6569
6543
  // shared/package-runtime/use-number-motion.ts
6570
6544
  function useNumberMotion(value, animated, fromPrevious, reduced) {
6571
- const [display, setDisplay] = (0, import_react53.useState)(typeof value === "string" || fromPrevious || !animated || reduced ? value : 0);
6572
- const motion = (0, import_react53.useRef)(null);
6545
+ const [display, setDisplay] = (0, import_react54.useState)(typeof value === "string" || fromPrevious || !animated || reduced ? value : 0);
6546
+ const motion = (0, import_react54.useRef)(null);
6573
6547
  if (!motion.current) motion.current = createNumberMotion(setDisplay);
6574
- (0, import_react53.useLayoutEffect)(() => {
6548
+ (0, import_react54.useLayoutEffect)(() => {
6575
6549
  if (!motion.current) motion.current = createNumberMotion(setDisplay);
6576
6550
  motion.current.update(value, animated, fromPrevious, reduced);
6577
6551
  }, [value, animated, fromPrevious, reduced]);
6578
- (0, import_react53.useLayoutEffect)(() => () => {
6552
+ (0, import_react54.useLayoutEffect)(() => () => {
6579
6553
  var _a;
6580
6554
  (_a = motion.current) == null ? void 0 : _a.cancel();
6581
6555
  motion.current = null;
@@ -6584,7 +6558,7 @@ function useNumberMotion(value, animated, fromPrevious, reduced) {
6584
6558
  }
6585
6559
 
6586
6560
  // packages/native/src/number-display.tsx
6587
- var import_react54 = require("react");
6561
+ var import_react55 = require("react");
6588
6562
  var import_react_native43 = require("react-native");
6589
6563
 
6590
6564
  // shared/package-runtime/relative-time.ts
@@ -6618,7 +6592,7 @@ var format = (value, decimals = 0) => new Intl.NumberFormat(void 0, {
6618
6592
  function useCountUp(value, animated, fromPrevious) {
6619
6593
  return useNumberMotion(value, animated, fromPrevious, useReducedMotion());
6620
6594
  }
6621
- var MarketQuoteTypeContext = (0, import_react54.createContext)(null);
6595
+ var MarketQuoteTypeContext = (0, import_react55.createContext)(null);
6622
6596
  function KjunAnimatedNumber({
6623
6597
  value,
6624
6598
  decimals = 0,
@@ -6678,7 +6652,7 @@ function KjunPriceCell({
6678
6652
  fetchedAt,
6679
6653
  fromPrevious = true
6680
6654
  }) {
6681
- const { colors } = useKjunStyles(), quoteType = (0, import_react54.useContext)(MarketQuoteTypeContext);
6655
+ const { colors } = useKjunStyles(), quoteType = (0, import_react55.useContext)(MarketQuoteTypeContext);
6682
6656
  return value == null ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(KjunSkeleton, { type: "block", height: import_tokens41.tokens.extensions.financial.priceSkeletonHeight, width: import_tokens41.tokens.extensions.financial.priceSkeletonWidth }) : /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native43.View, { style: { flexDirection: "row", alignItems: "center", gap: import_tokens41.tokens.extensions.financial.affixGap }, children: [
6683
6657
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
6684
6658
  import_react_native43.View,
@@ -6712,7 +6686,7 @@ function KjunSignedValue({
6712
6686
  showFreshness = true,
6713
6687
  formatter
6714
6688
  }) {
6715
- const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), quoteType = (0, import_react54.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);
6689
+ const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), quoteType = (0, import_react55.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);
6716
6690
  const display = n === null ? "\u2014" : formatter ? formatter(n) : prefix + (showSign && n > 0 ? "+" : "") + format(kind === "percent" && !isRaw ? n * 100 : n, decimals) + (kind === "percent" ? "%" : "") + suffix;
6717
6691
  return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
6718
6692
  import_react_native43.View,
@@ -6751,6 +6725,7 @@ function KjunSignedValue({
6751
6725
 
6752
6726
  // packages/native/src/financial-display.tsx
6753
6727
  var import_jsx_runtime41 = require("react/jsx-runtime");
6728
+ var deviationFill = { neutral: 1, low: import_tokens42.tokens.states.opacity.deviationLow, medium: import_tokens42.tokens.states.opacity.deviationMedium, strong: import_tokens42.tokens.states.opacity.deviationStrong };
6754
6729
  function KjunDeviation({
6755
6730
  value,
6756
6731
  variant = "pill",
@@ -6761,7 +6736,7 @@ function KjunDeviation({
6761
6736
  const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), n = finiteNumber(value);
6762
6737
  if (n === null)
6763
6738
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(KText, { style: { color: colors.textDisabled }, children: "-" });
6764
- 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) + "%";
6739
+ const abs = Math.abs(n), signal = abs >= signalThreshold, level = (0, import_tokens42.deviationLevel)(n), neutral = level === "neutral", color = neutral ? colors.textSecondary : domainColor(domainColors, n > 0 ? "priceUp" : "priceDown"), label = formatter ? formatter(n) : (n > 0 ? "+" : "") + format(n, decimals) + "%";
6765
6740
  if (variant === "badge")
6766
6741
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
6767
6742
  import_react_native44.View,
@@ -6830,7 +6805,7 @@ function KjunDeviation({
6830
6805
  inset: 0,
6831
6806
  borderRadius: import_tokens42.tokens.extensions.financial.cellRadius,
6832
6807
  backgroundColor: neutral ? colors.secondary : color,
6833
- opacity: neutral ? 1 : abs < 0.5 ? 0.08 : abs < 1 ? 0.14 : 0.22
6808
+ opacity: deviationFill[level]
6834
6809
  }
6835
6810
  }
6836
6811
  ),
@@ -6843,7 +6818,7 @@ function KjunDeviation({
6843
6818
  fontVariant: ["tabular-nums"],
6844
6819
  ...typeStyle("body"),
6845
6820
  // Like Web: control weight, stronger once the deviation reaches 1%.
6846
- fontWeight: abs >= 1 ? typeStyle("numberLg").fontWeight : typeStyle("control").fontWeight
6821
+ fontWeight: level === "strong" ? typeStyle("numberLg").fontWeight : typeStyle("control").fontWeight
6847
6822
  },
6848
6823
  children: label
6849
6824
  }
@@ -7005,7 +6980,7 @@ function KjunSparkline({
7005
6980
  stretch = false,
7006
6981
  ariaLabel
7007
6982
  }) {
7008
- const { colors, domainColors } = useKjunStyles(), id = "kjun-" + (0, import_react55.useId)().replace(/:/g, ""), values = data.filter(Number.isFinite), min = Math.min(...values), max = Math.max(...values), range = max - min || 1, points = values.map(
6983
+ const { colors, domainColors } = useKjunStyles(), id = "kjun-" + (0, import_react56.useId)().replace(/:/g, ""), values = data.filter(Number.isFinite), min = Math.min(...values), max = Math.max(...values), range = max - min || 1, points = values.map(
7009
6984
  (v, i) => `${2 + i / Math.max(1, values.length - 1) * (width - 4)},${2 + height - 4 - (v - min) / range * (height - 4)}`
7010
6985
  ).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"));
7011
6986
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
@@ -7051,7 +7026,7 @@ function KjunSparkline({
7051
7026
 
7052
7027
  // shared/package-runtime/table.ts
7053
7028
  var import_tokens43 = require("@kjun-ui/tokens");
7054
- var import_react56 = require("react");
7029
+ var import_react57 = require("react");
7055
7030
 
7056
7031
  // shared/package-runtime/table-model.ts
7057
7032
  function getValue(row, key) {
@@ -7095,11 +7070,11 @@ function useTableModel(props) {
7095
7070
  expandSingle = false,
7096
7071
  onExpandedRowsChange
7097
7072
  } = props;
7098
- const [internalSort, setSort] = (0, import_react56.useState)({
7073
+ const [internalSort, setSort] = (0, import_react57.useState)({
7099
7074
  key: "",
7100
7075
  order: "asc"
7101
- }), [internalExpanded, setExpanded] = (0, import_react56.useState)([]), [search, setSearch] = (0, import_react56.useState)(""), timer = (0, import_react56.useRef)(void 0);
7102
- (0, import_react56.useEffect)(() => () => clearTimeout(timer.current), []);
7076
+ }), [internalExpanded, setExpanded] = (0, import_react57.useState)([]), [search, setSearch] = (0, import_react57.useState)(""), timer = (0, import_react57.useRef)(void 0);
7077
+ (0, import_react57.useEffect)(() => () => clearTimeout(timer.current), []);
7103
7078
  const sort = props.sort === void 0 ? internalSort : (_a = props.sort) != null ? _a : { key: "", order: "asc" };
7104
7079
  const rows = sortTableRows(data, sort, { sortable, sortMode });
7105
7080
  const key = (row, index = 0) => tableRowKey(row, rowKey, index);
@@ -7253,19 +7228,19 @@ function TableToolbar({
7253
7228
 
7254
7229
  // packages/native/src/table-grid.tsx
7255
7230
  var import_tokens45 = require("@kjun-ui/tokens");
7256
- var import_react57 = require("react");
7231
+ var import_react58 = require("react");
7257
7232
  var import_react_native_svg7 = __toESM(require("react-native-svg"), 1);
7258
7233
  var import_react_native46 = require("react-native");
7259
7234
  var import_jsx_runtime43 = require("react/jsx-runtime");
7260
- function TableGrid({ initialLoading, skeletonRows, state, onRowClick, selectable, checkbox, cell, expandable, expand, renderExpand, emptyContent, emptyText, visible, maxHeight, stickyHeader, sortable, compact, striped, hoverable }) {
7235
+ function TableGrid({ initialLoading, skeletonRows, skeletonHidden, state, onRowClick, selectable, checkbox, cell, expandable, expand, renderExpand, emptyContent, emptyText, visible, maxHeight, stickyHeader, sortable, compact, striped, hoverable }) {
7261
7236
  const { colors } = useKjunStyles();
7262
7237
  const geometry2 = import_tokens45.tokens.table;
7263
- const [scroll, setScroll] = (0, import_react57.useState)({ viewport: 0, x: 0 }), fadeId = "kjun-table-fade-" + (0, import_react57.useId)().replace(/[^a-z0-9]/gi, "");
7238
+ const [scroll, setScroll] = (0, import_react58.useState)({ viewport: 0, x: 0 }), fadeId = "kjun-table-fade-" + (0, import_react58.useId)().replace(/[^a-z0-9]/gi, "");
7264
7239
  const cellPadding = geometry2.cellPadding[compact ? "compact" : "regular"];
7265
7240
  const width = (col) => typeof col.width === "number" ? col.width : typeof col.width === "string" && /^\d+(px)?$/.test(col.width) ? parseFloat(col.width) : geometry2.columnWidth;
7266
7241
  const totalWidth = visible.reduce((sum, col) => sum + width(col), 0) + (selectable ? geometry2.actionColumnWidth : 0) + (expandable ? geometry2.actionColumnWidth : 0);
7267
7242
  const fade = totalWidth - scroll.viewport - scroll.x > 1;
7268
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_react_native46.View, { style: { position: "relative", minWidth: 0 }, children: [
7243
+ 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: [
7269
7244
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
7270
7245
  import_react_native46.ScrollView,
7271
7246
  {
@@ -7374,7 +7349,7 @@ function TableGrid({ initialLoading, skeletonRows, state, onRowClick, selectable
7374
7349
  ))
7375
7350
  ] }, 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, {
7376
7351
  color: colors.textSecondary
7377
- }) }) : state.rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_react57.Fragment, { children: [
7352
+ }) }) : state.rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_react58.Fragment, { children: [
7378
7353
  /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
7379
7354
  AccessiblePressable,
7380
7355
  {
@@ -7463,6 +7438,7 @@ var import_jsx_runtime44 = require("react/jsx-runtime");
7463
7438
  function TableCards({
7464
7439
  initialLoading,
7465
7440
  skeletonRows,
7441
+ skeletonHidden,
7466
7442
  state,
7467
7443
  onRowClick,
7468
7444
  selectable,
@@ -7477,149 +7453,156 @@ function TableCards({
7477
7453
  }) {
7478
7454
  const { colors } = useKjunStyles();
7479
7455
  const { title, subtitle, badges, inline, sections, actions, columnsForSection } = layout;
7480
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_react_native47.View, { style: { gap: import_tokens46.tokens.extensions.tableCard.gap, padding: import_tokens46.tokens.extensions.tableCard.listPadding }, children: [
7481
- 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)(
7482
- AccessiblePressable,
7483
- {
7484
- accessible: false,
7485
- onPress: () => onRowClick == null ? void 0 : onRowClick(row, i),
7486
- style: ({ pressed }) => ({
7487
- padding: import_tokens46.tokens.extensions.tableCard.padding,
7488
- borderWidth: import_tokens46.tokens.border.defaultWidth,
7489
- borderColor: colors.border,
7490
- borderRadius: import_tokens46.tokens.extensions.tableCard.radius,
7491
- backgroundColor: pressed ? colors.hover : colors.background
7492
- }),
7493
- children: [
7494
- /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
7495
- import_react_native47.View,
7496
- {
7497
- style: {
7498
- flexDirection: "row",
7499
- alignItems: "center",
7500
- gap: import_tokens46.tokens.extensions.tableCard.gap,
7501
- marginBottom: import_tokens46.tokens.dimension.value8
7502
- },
7503
- children: [
7504
- /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
7456
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
7457
+ import_react_native47.View,
7458
+ {
7459
+ style: { gap: import_tokens46.tokens.extensions.tableCard.gap, padding: import_tokens46.tokens.extensions.tableCard.listPadding, opacity: skeletonHidden ? 0 : void 0 },
7460
+ "aria-hidden": skeletonHidden || void 0,
7461
+ children: [
7462
+ 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)(
7463
+ AccessiblePressable,
7464
+ {
7465
+ accessible: false,
7466
+ onPress: () => onRowClick == null ? void 0 : onRowClick(row, i),
7467
+ style: ({ pressed }) => ({
7468
+ padding: import_tokens46.tokens.extensions.tableCard.padding,
7469
+ borderWidth: import_tokens46.tokens.border.defaultWidth,
7470
+ borderColor: colors.border,
7471
+ borderRadius: import_tokens46.tokens.extensions.tableCard.radius,
7472
+ backgroundColor: pressed ? colors.hover : colors.background
7473
+ }),
7474
+ children: [
7475
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
7476
+ import_react_native47.View,
7477
+ {
7478
+ style: {
7479
+ flexDirection: "row",
7480
+ alignItems: "center",
7481
+ gap: import_tokens46.tokens.extensions.tableCard.gap,
7482
+ marginBottom: import_tokens46.tokens.dimension.value8
7483
+ },
7484
+ children: [
7485
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
7486
+ import_react_native47.View,
7487
+ {
7488
+ style: {
7489
+ flexDirection: "row",
7490
+ flex: 1,
7491
+ alignItems: "center",
7492
+ gap: import_tokens46.tokens.extensions.tableCard.gap,
7493
+ minWidth: 0
7494
+ },
7495
+ children: [
7496
+ selectable && checkbox(row, i),
7497
+ title && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { flex: 1, minWidth: 0 }, children: cell(title, row, i, "title") })
7498
+ ]
7499
+ }
7500
+ ),
7501
+ badges.length ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { flexDirection: "row", gap: import_tokens46.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,
7502
+ inline.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { flexDirection: "row", gap: import_tokens46.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)) })
7503
+ ]
7504
+ }
7505
+ ),
7506
+ sections.map((section, index) => {
7507
+ const cols = columnsForSection(row, section);
7508
+ return cols.length ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
7505
7509
  import_react_native47.View,
7506
7510
  {
7507
7511
  style: {
7508
- flexDirection: "row",
7509
- flex: 1,
7510
- alignItems: "center",
7511
- gap: import_tokens46.tokens.extensions.tableCard.gap,
7512
- minWidth: 0
7512
+ borderTopWidth: index ? import_tokens46.tokens.border.defaultWidth : 0,
7513
+ borderTopColor: colors.border,
7514
+ paddingTop: index ? import_tokens46.tokens.dimension.value12 : 0,
7515
+ marginTop: index ? import_tokens46.tokens.dimension.value12 : 0
7513
7516
  },
7514
7517
  children: [
7515
- selectable && checkbox(row, i),
7516
- title && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { flex: 1, minWidth: 0 }, children: cell(title, row, i, "title") })
7517
- ]
7518
- }
7519
- ),
7520
- badges.length ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { flexDirection: "row", gap: import_tokens46.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,
7521
- inline.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { flexDirection: "row", gap: import_tokens46.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)) })
7522
- ]
7523
- }
7524
- ),
7525
- sections.map((section, index) => {
7526
- const cols = columnsForSection(row, section);
7527
- return cols.length ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
7528
- import_react_native47.View,
7529
- {
7530
- style: {
7531
- borderTopWidth: index ? import_tokens46.tokens.border.defaultWidth : 0,
7532
- borderTopColor: colors.border,
7533
- paddingTop: index ? import_tokens46.tokens.dimension.value12 : 0,
7534
- marginTop: index ? import_tokens46.tokens.dimension.value12 : 0
7535
- },
7536
- children: [
7537
- section.label && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7538
- KText,
7539
- {
7540
- style: {
7541
- ...typeStyle("controlSmall"),
7542
- color: colors.textTertiary,
7543
- marginBottom: import_tokens46.tokens.dimension.value6
7544
- },
7545
- children: section.label
7546
- }
7547
- ),
7548
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7549
- import_react_native47.View,
7550
- {
7551
- style: {
7552
- flexDirection: "row",
7553
- flexWrap: "wrap",
7554
- gap: section.layout === "metrics" ? import_tokens46.tokens.extensions.tableCard.gap : import_tokens46.tokens.extensions.tableCard.metricGap
7555
- },
7556
- children: cols.map((col) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
7518
+ section.label && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7519
+ KText,
7520
+ {
7521
+ style: {
7522
+ ...typeStyle("controlSmall"),
7523
+ color: colors.textTertiary,
7524
+ marginBottom: import_tokens46.tokens.dimension.value6
7525
+ },
7526
+ children: section.label
7527
+ }
7528
+ ),
7529
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7557
7530
  import_react_native47.View,
7558
7531
  {
7559
7532
  style: {
7560
- flexBasis: col.fullWidthInCard || section.layout === "stack" ? "100%" : section.layout === "metrics" ? "30%" : "47%",
7561
- flexGrow: 1,
7562
- minWidth: 0,
7563
- gap: import_tokens46.tokens.extensions.tableCard.valueGap
7533
+ flexDirection: "row",
7534
+ flexWrap: "wrap",
7535
+ gap: section.layout === "metrics" ? import_tokens46.tokens.extensions.tableCard.gap : import_tokens46.tokens.extensions.tableCard.metricGap
7564
7536
  },
7565
- children: [
7566
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7567
- KText,
7568
- {
7569
- style: {
7570
- ...typeStyle("meta"),
7571
- color: colors.textTertiary,
7572
- textAlign: section.labelAlign
7573
- },
7574
- children: col.label
7575
- }
7576
- ),
7577
- cell(col, row, i)
7578
- ]
7579
- },
7580
- col.key
7581
- ))
7582
- }
7583
- )
7584
- ]
7585
- },
7586
- section.key || index
7587
- ) : null;
7588
- }),
7589
- expandable && expand(row, i),
7590
- expandable && state.expanded.includes(state.key(row, i)) && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7591
- import_react_native47.View,
7592
- {
7593
- style: {
7594
- borderTopWidth: import_tokens46.tokens.border.defaultWidth,
7595
- borderTopColor: colors.border,
7596
- paddingTop: import_tokens46.tokens.dimension.value8,
7597
- marginTop: import_tokens46.tokens.dimension.value4
7598
- },
7599
- children: content(renderExpand == null ? void 0 : renderExpand(row, i))
7600
- }
7601
- ),
7602
- actions && !actions.inlineInCard && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7603
- import_react_native47.View,
7604
- {
7605
- style: {
7606
- borderTopWidth: import_tokens46.tokens.border.defaultWidth,
7607
- borderTopColor: colors.border,
7608
- paddingTop: import_tokens46.tokens.dimension.value8,
7609
- marginTop: import_tokens46.tokens.dimension.value8,
7610
- alignItems: "flex-end"
7611
- },
7612
- children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { alignSelf: "flex-end" }, children: cell(actions, row, i) })
7613
- }
7614
- )
7615
- ]
7616
- },
7617
- state.key(row, i)
7618
- )),
7619
- !initialLoading && !state.rows.length && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { padding: import_tokens46.tokens.extensions.tableCard.emptyPadding, alignItems: "center" }, children: content(emptyContent || emptyText, {
7620
- color: colors.textTertiary
7621
- }) })
7622
- ] });
7537
+ children: cols.map((col) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
7538
+ import_react_native47.View,
7539
+ {
7540
+ style: {
7541
+ flexBasis: col.fullWidthInCard || section.layout === "stack" ? "100%" : section.layout === "metrics" ? "30%" : "47%",
7542
+ flexGrow: 1,
7543
+ minWidth: 0,
7544
+ gap: import_tokens46.tokens.extensions.tableCard.valueGap
7545
+ },
7546
+ children: [
7547
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7548
+ KText,
7549
+ {
7550
+ style: {
7551
+ ...typeStyle("meta"),
7552
+ color: colors.textTertiary,
7553
+ textAlign: section.labelAlign
7554
+ },
7555
+ children: col.label
7556
+ }
7557
+ ),
7558
+ cell(col, row, i)
7559
+ ]
7560
+ },
7561
+ col.key
7562
+ ))
7563
+ }
7564
+ )
7565
+ ]
7566
+ },
7567
+ section.key || index
7568
+ ) : null;
7569
+ }),
7570
+ expandable && expand(row, i),
7571
+ expandable && state.expanded.includes(state.key(row, i)) && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7572
+ import_react_native47.View,
7573
+ {
7574
+ style: {
7575
+ borderTopWidth: import_tokens46.tokens.border.defaultWidth,
7576
+ borderTopColor: colors.border,
7577
+ paddingTop: import_tokens46.tokens.dimension.value8,
7578
+ marginTop: import_tokens46.tokens.dimension.value4
7579
+ },
7580
+ children: content(renderExpand == null ? void 0 : renderExpand(row, i))
7581
+ }
7582
+ ),
7583
+ actions && !actions.inlineInCard && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7584
+ import_react_native47.View,
7585
+ {
7586
+ style: {
7587
+ borderTopWidth: import_tokens46.tokens.border.defaultWidth,
7588
+ borderTopColor: colors.border,
7589
+ paddingTop: import_tokens46.tokens.dimension.value8,
7590
+ marginTop: import_tokens46.tokens.dimension.value8,
7591
+ alignItems: "flex-end"
7592
+ },
7593
+ children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { alignSelf: "flex-end" }, children: cell(actions, row, i) })
7594
+ }
7595
+ )
7596
+ ]
7597
+ },
7598
+ state.key(row, i)
7599
+ )),
7600
+ !initialLoading && !state.rows.length && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { padding: import_tokens46.tokens.extensions.tableCard.emptyPadding, alignItems: "center" }, children: content(emptyContent || emptyText, {
7601
+ color: colors.textTertiary
7602
+ }) })
7603
+ ]
7604
+ }
7605
+ );
7623
7606
  }
7624
7607
 
7625
7608
  // packages/native/src/table-view.tsx
@@ -7627,6 +7610,9 @@ var import_jsx_runtime45 = require("react/jsx-runtime");
7627
7610
  function KjunTable(props) {
7628
7611
  const {
7629
7612
  skeletonRows = 8,
7613
+ skeleton = true,
7614
+ loadingText = "\uB85C\uB529 \uC911...",
7615
+ loadingDelay = 0,
7630
7616
  emptyText = "\uB370\uC774\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4",
7631
7617
  hoverable = true,
7632
7618
  searchable = false,
@@ -7647,7 +7633,7 @@ function KjunTable(props) {
7647
7633
  onRowClick,
7648
7634
  onRetry
7649
7635
  } = props;
7650
- const { colors, domainColors } = useKjunStyles(), state = useTableModel2(props), query = useQueryDisplay(props), mobile = (0, import_react_native48.useWindowDimensions)().width < import_tokens47.tokens.table.mobileBreakpoint;
7636
+ 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_tokens47.tokens.table.mobileBreakpoint;
7651
7637
  const layout = tablePresentation(props, mobile);
7652
7638
  const { card, visible } = layout;
7653
7639
  const cell = (col, row, index, role) => {
@@ -7726,8 +7712,11 @@ function KjunTable(props) {
7726
7712
  refreshing: query.queryRefreshing,
7727
7713
  loadingPadding: "none",
7728
7714
  onRetry,
7715
+ loading: skeleton ? void 0 : props.loading,
7716
+ loadingText,
7717
+ loadingDelay: skeleton ? 0 : loadingDelay,
7729
7718
  children: [
7730
- 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 }),
7719
+ 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 }),
7731
7720
  pagination && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
7732
7721
  KjunPagination,
7733
7722
  {
@@ -7785,12 +7774,13 @@ function KjunKpiHero(props) {
7785
7774
  secondary = [],
7786
7775
  animated = true,
7787
7776
  size = "md",
7777
+ surface = "card",
7788
7778
  aside,
7789
7779
  formatter,
7790
7780
  deltaFormatter,
7791
7781
  renderSecondary
7792
7782
  } = props;
7793
- const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), mobile = (0, import_react_native50.useWindowDimensions)().width <= import_tokens48.tokens.responsive.kpiHero, small = size === "sm", numberRole = small ? "displaySm" : mobile ? "displayMd" : "displayLg", font = typeStyle(numberRole).fontSize, unitStyle = typeStyle(small ? "numberSm" : mobile ? "numberMd" : "numberLg");
7783
+ const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), windowWidth = (0, import_react_native50.useWindowDimensions)().width, mobile = windowWidth <= import_tokens48.tokens.responsive.kpiHero, small = size === "sm", numberRole = small ? "displaySm" : mobile ? "displayMd" : "displayLg", font = typeStyle(numberRole).fontSize, unitStyle = typeStyle(small ? "numberSm" : mobile ? "numberMd" : "numberLg");
7794
7784
  const geometry2 = import_tokens48.tokens.extensions.kpiHero;
7795
7785
  const spec = small ? geometry2.sm : mobile ? geometry2.mobile : geometry2.md;
7796
7786
  const numeric = {
@@ -7820,10 +7810,13 @@ function KjunKpiHero(props) {
7820
7810
  {
7821
7811
  accessibilityState: { busy: loading },
7822
7812
  style: {
7823
- paddingVertical: spec.paddingY,
7824
- paddingHorizontal: spec.paddingX,
7825
- backgroundColor: colors.surface,
7826
- borderRadius: geometry2.radius
7813
+ // surface="none" sits inside the app's own card or list, so it drops its own card box.
7814
+ ...surface === "none" ? {} : {
7815
+ paddingVertical: spec.paddingY,
7816
+ paddingHorizontal: spec.paddingX,
7817
+ backgroundColor: colors.surface,
7818
+ borderRadius: geometry2.radius
7819
+ }
7827
7820
  },
7828
7821
  children: [
7829
7822
  /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
@@ -7906,70 +7899,75 @@ function KjunKpiHero(props) {
7906
7899
  borderTopWidth: import_tokens48.tokens.border.defaultWidth,
7907
7900
  borderTopColor: colors.border
7908
7901
  },
7909
- children: secondary.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
7910
- import_react_native50.View,
7911
- {
7912
- style: mobile && !small ? {
7913
- flex: 1,
7914
- minWidth: 0,
7915
- paddingLeft: i ? geometry2.itemPadding : 0,
7916
- paddingRight: i === secondary.length - 1 ? 0 : geometry2.itemPadding,
7917
- borderLeftWidth: i ? import_tokens48.tokens.border.defaultWidth : 0,
7918
- borderLeftColor: colors.border
7919
- } : { minWidth: 0, maxWidth: "100%", flexShrink: 1 },
7920
- children: [
7921
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
7922
- KText,
7923
- {
7924
- style: {
7925
- color: colors.textTertiary,
7926
- ...typeStyle("caption"),
7927
- marginBottom: spec.secondaryLabelGap
7928
- },
7929
- children: item.label
7930
- }
7931
- ),
7932
- loading ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
7933
- KjunSkeleton,
7934
- {
7935
- type: "block",
7936
- height: typeStyle("numberMd").lineHeight,
7937
- width: geometry2.secondarySkeletonWidth
7938
- }
7939
- ) : (renderSecondary == null ? void 0 : renderSecondary(item, i)) || /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
7940
- KpiNumberLine,
7941
- {
7942
- prefix: item.prefix,
7943
- suffix: item.suffix,
7944
- prefixGap: geometry2.secondaryAffixGap,
7945
- suffixGap: geometry2.secondaryAffixGap,
7946
- unitStyle: typeStyle("caption"),
7947
- style: { ...numeric, ...typeStyle("numberMd"), color: item.valueColor || (item.semantic === "price" && Number(item.value) !== 0 ? direction(Number(item.value)) : colors.text) },
7948
- children: kpiFormat(item)
7949
- }
7950
- ),
7951
- item.desc && (loading ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
7952
- KjunSkeleton,
7953
- {
7954
- type: "block",
7955
- width: geometry2.descriptionSkeletonWidth,
7956
- height: typeStyle("caption").lineHeight
7957
- }
7958
- ) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
7959
- KText,
7960
- {
7961
- style: {
7962
- ...typeStyle("caption"),
7963
- marginTop: small ? geometry2.sm.descriptionGap : geometry2.md.descriptionGap,
7964
- color: item.descColor || colors.textTertiary
7965
- },
7966
- children: item.desc
7967
- }
7968
- ))
7969
- ]
7970
- },
7971
- i
7972
- ))
7902
+ children: secondary.map((item, i) => {
7903
+ const columns = mobile && !small && windowWidth <= import_tokens48.tokens.responsive.kpiHeroSecondary && secondary.length >= 3 ? 2 : 0;
7904
+ const first = columns ? i % columns === 0 : i === 0;
7905
+ const last = columns ? i % columns === columns - 1 || i === secondary.length - 1 : i === secondary.length - 1;
7906
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
7907
+ import_react_native50.View,
7908
+ {
7909
+ style: mobile && !small ? {
7910
+ ...columns ? { width: `${100 / columns}%`, marginTop: i >= columns ? spec.secondaryPadding : 0 } : { flex: 1 },
7911
+ minWidth: 0,
7912
+ paddingLeft: first ? 0 : geometry2.itemPadding,
7913
+ paddingRight: last ? 0 : geometry2.itemPadding,
7914
+ borderLeftWidth: first ? 0 : import_tokens48.tokens.border.defaultWidth,
7915
+ borderLeftColor: colors.border
7916
+ } : { minWidth: 0, maxWidth: "100%", flexShrink: 1 },
7917
+ children: [
7918
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
7919
+ KText,
7920
+ {
7921
+ style: {
7922
+ color: colors.textTertiary,
7923
+ ...typeStyle("caption"),
7924
+ marginBottom: spec.secondaryLabelGap
7925
+ },
7926
+ children: item.label
7927
+ }
7928
+ ),
7929
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
7930
+ KjunSkeleton,
7931
+ {
7932
+ type: "block",
7933
+ height: typeStyle("numberMd").lineHeight,
7934
+ width: geometry2.secondarySkeletonWidth
7935
+ }
7936
+ ) : (renderSecondary == null ? void 0 : renderSecondary(item, i)) || /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
7937
+ KpiNumberLine,
7938
+ {
7939
+ prefix: item.prefix,
7940
+ suffix: item.suffix,
7941
+ prefixGap: geometry2.secondaryAffixGap,
7942
+ suffixGap: geometry2.secondaryAffixGap,
7943
+ unitStyle: typeStyle("caption"),
7944
+ style: { ...numeric, ...typeStyle("numberMd"), color: item.valueColor || (item.semantic === "price" && Number(item.value) !== 0 ? direction(Number(item.value)) : colors.text) },
7945
+ children: kpiFormat(item)
7946
+ }
7947
+ ),
7948
+ item.desc && (loading ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
7949
+ KjunSkeleton,
7950
+ {
7951
+ type: "block",
7952
+ width: geometry2.descriptionSkeletonWidth,
7953
+ height: typeStyle("caption").lineHeight
7954
+ }
7955
+ ) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
7956
+ KText,
7957
+ {
7958
+ style: {
7959
+ ...typeStyle("caption"),
7960
+ marginTop: small ? geometry2.sm.descriptionGap : geometry2.md.descriptionGap,
7961
+ color: item.descColor || colors.textTertiary
7962
+ },
7963
+ children: item.desc
7964
+ }
7965
+ ))
7966
+ ]
7967
+ },
7968
+ i
7969
+ );
7970
+ })
7973
7971
  }
7974
7972
  )
7975
7973
  ]
@@ -7979,13 +7977,14 @@ function KjunKpiHero(props) {
7979
7977
 
7980
7978
  // packages/native/src/kpi-row.tsx
7981
7979
  var import_tokens49 = require("@kjun-ui/tokens");
7982
- var import_react58 = require("react");
7980
+ var import_react59 = require("react");
7983
7981
  var import_react_native51 = require("react-native");
7984
7982
  var import_jsx_runtime48 = require("react/jsx-runtime");
7985
7983
  function KjunKpiRow({
7986
7984
  loading = false,
7987
7985
  items,
7988
7986
  size = "md",
7987
+ surface = "card",
7989
7988
  mobileSummary = true,
7990
7989
  onItemClick,
7991
7990
  renderValue
@@ -7993,7 +7992,7 @@ function KjunKpiRow({
7993
7992
  const { colors, domainColors, numericFontFamily, fontFamily } = useKjunStyles(), mobile = (0, import_react_native51.useWindowDimensions)().width <= import_tokens49.tokens.responsive.kpiRow, small = size === "sm", summary = mobileSummary && mobile && !small;
7994
7993
  const geometry2 = import_tokens49.tokens.extensions.kpiRow;
7995
7994
  const spec = small ? geometry2.sm : mobile ? geometry2.mobile : geometry2.md;
7996
- const [width, setWidth] = (0, import_react58.useState)(0);
7995
+ const [width, setWidth] = (0, import_react59.useState)(0);
7997
7996
  const paddingX = summary ? geometry2.summary.paddingX : spec.paddingX;
7998
7997
  const gapX = summary ? geometry2.summary.gapX : 0;
7999
7998
  const explicit = items.some(
@@ -8034,10 +8033,13 @@ function KjunKpiRow({
8034
8033
  style: {
8035
8034
  flexDirection: "row",
8036
8035
  flexWrap: mobile ? "wrap" : "nowrap",
8037
- paddingVertical: summary ? geometry2.summary.paddingY : spec.paddingY,
8038
- paddingHorizontal: paddingX,
8039
- borderRadius: geometry2.radius,
8040
- backgroundColor: colors.surface,
8036
+ // surface="none" sits inside the app's own card or list, so it drops its own card box.
8037
+ ...surface === "none" ? {} : {
8038
+ paddingVertical: summary ? geometry2.summary.paddingY : spec.paddingY,
8039
+ paddingHorizontal: paddingX,
8040
+ borderRadius: geometry2.radius,
8041
+ backgroundColor: colors.surface
8042
+ },
8041
8043
  rowGap: summary ? geometry2.summary.gapY : 0,
8042
8044
  columnGap: gapX
8043
8045
  },
@@ -8209,7 +8211,7 @@ var import_tokens51 = require("@kjun-ui/tokens");
8209
8211
  var import_react_native53 = require("react-native");
8210
8212
 
8211
8213
  // shared/package-runtime/market-cards.ts
8212
- var import_react59 = require("react");
8214
+ var import_react60 = require("react");
8213
8215
  function useMarketCardsModel(props, persistence) {
8214
8216
  const {
8215
8217
  columns,
@@ -8225,8 +8227,8 @@ function useMarketCardsModel(props, persistence) {
8225
8227
  sortKey: c.sortable ? metricConfig[c.key].sortKey : null,
8226
8228
  isDefaultSort: c.defaultSort
8227
8229
  }));
8228
- const [key, setKey] = (0, import_react59.useState)(""), restoreSignature = JSON.stringify([storageNamespace, pills]);
8229
- const mounted = (0, import_react59.useRef)("");
8230
+ const [key, setKey] = (0, import_react60.useState)(""), restoreSignature = JSON.stringify([storageNamespace, pills]);
8231
+ const mounted = (0, import_react60.useRef)("");
8230
8232
  const save = (key2) => {
8231
8233
  var _a;
8232
8234
  try {
@@ -8234,7 +8236,7 @@ function useMarketCardsModel(props, persistence) {
8234
8236
  } catch (e) {
8235
8237
  }
8236
8238
  };
8237
- (0, import_react59.useEffect)(() => {
8239
+ (0, import_react60.useEffect)(() => {
8238
8240
  var _a, _b;
8239
8241
  if (mounted.current === restoreSignature) return;
8240
8242
  mounted.current = restoreSignature;
@@ -8476,7 +8478,7 @@ function KjunMarketCards(props) {
8476
8478
  ...props,
8477
8479
  loadingPadding: "none",
8478
8480
  empty: !rows.length,
8479
- loadingContent: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(KjunListSkeleton, { variant: "compact" }),
8481
+ loadingContent: props.skeleton === false ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(KjunListSkeleton, { variant: "compact" }),
8480
8482
  emptyContent: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(KjunEmpty, { text: emptyMessage, description: emptySubMessage }),
8481
8483
  children: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(import_react_native53.View, { children: [
8482
8484
  state.pill && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(KText, { style: { textAlign: "right", paddingHorizontal: import_tokens51.tokens.dimension.value16, paddingVertical: import_tokens51.tokens.dimension.value8, ...typeStyle("caption"), color: colors.textSecondary }, children: state.pill.label }),
@@ -8587,7 +8589,7 @@ function KjunMarketSimpleList(props) {
8587
8589
  loadingPadding: "none",
8588
8590
  empty: !rows.length,
8589
8591
  emptyText: emptyMessage,
8590
- loadingContent: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(KjunListSkeleton, {}),
8592
+ loadingContent: props.skeleton === false ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(KjunListSkeleton, {}),
8591
8593
  emptyContent: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(KjunEmpty, { text: emptyMessage, description: emptySubMessage }),
8592
8594
  children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_react_native54.View, { children: rows.map((row, i) => {
8593
8595
  var _a, _b;
@@ -8714,7 +8716,7 @@ function KjunMarketListPanel({
8714
8716
 
8715
8717
  // packages/native/src/market-table.tsx
8716
8718
  var import_tokens54 = require("@kjun-ui/tokens");
8717
- var import_react60 = require("react");
8719
+ var import_react61 = require("react");
8718
8720
  var import_react_native56 = require("react-native");
8719
8721
  var import_react_native_svg8 = __toESM(require("react-native-svg"), 1);
8720
8722
  var import_jsx_runtime53 = require("react/jsx-runtime");
@@ -8800,7 +8802,7 @@ function KjunMarketTable(props) {
8800
8802
  );
8801
8803
  };
8802
8804
  const layout = useMarketLayout(columns, showActions);
8803
- const [scroll, setScroll] = (0, import_react60.useState)({ viewport: 0, x: 0 }), fadeId = "kjun-market-fade-" + (0, import_react60.useId)().replace(/[^a-z0-9]/gi, "");
8805
+ const [scroll, setScroll] = (0, import_react61.useState)({ viewport: 0, x: 0 }), fadeId = "kjun-market-fade-" + (0, import_react61.useId)().replace(/[^a-z0-9]/gi, "");
8804
8806
  const tableQuote = { price: {}, change: { fontWeight: typeStyle("body").fontWeight } };
8805
8807
  return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
8806
8808
  import_react_native56.View,
@@ -8818,7 +8820,7 @@ function KjunMarketTable(props) {
8818
8820
  loadingPadding: "none",
8819
8821
  empty: !rows.length,
8820
8822
  emptyText: emptyMessage,
8821
- loadingContent: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(KjunMarketTableSkeleton, { columns, showActions }),
8823
+ loadingContent: props.skeleton === false ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(KjunMarketTableSkeleton, { columns, showActions }),
8822
8824
  emptyContent: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(KjunEmpty, { text: emptyMessage, description: emptySubMessage }),
8823
8825
  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: [
8824
8826
  /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
@@ -9023,12 +9025,12 @@ function KjunMarketTable(props) {
9023
9025
 
9024
9026
  // packages/native/src/list.tsx
9025
9027
  var import_tokens55 = require("@kjun-ui/tokens");
9026
- var import_react61 = require("react");
9028
+ var import_react62 = require("react");
9027
9029
  var import_react_native57 = require("react-native");
9028
9030
  var import_jsx_runtime54 = require("react/jsx-runtime");
9029
- var LastListRow = (0, import_react61.createContext)(false);
9031
+ var LastListRow = (0, import_react62.createContext)(false);
9030
9032
  function KjunListRow({ title, description, leading, trailing, actions, href, disabled = false, onPress }) {
9031
- const { colors } = useKjunStyles(), last = (0, import_react61.useContext)(LastListRow);
9033
+ const { colors } = useKjunStyles(), last = (0, import_react62.useContext)(LastListRow);
9032
9034
  const parts = /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_jsx_runtime54.Fragment, { children: [
9033
9035
  leading && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_react_native57.View, { children: content(leading) }),
9034
9036
  /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_react_native57.View, { style: { flex: 1, minWidth: 0 }, children: [
@@ -9053,13 +9055,13 @@ function KjunListSection({ title, description, actions, children, ariaLabel }) {
9053
9055
  ] }),
9054
9056
  content(actions)
9055
9057
  ] }),
9056
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_react_native57.View, { role: "list", accessibilityLabel: ariaLabel || title, children: import_react61.Children.toArray(children).map((row, i, rows) => /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(LastListRow.Provider, { value: i === rows.length - 1, children: row }, i)) })
9058
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_react_native57.View, { role: "list", accessibilityLabel: ariaLabel || title, children: import_react62.Children.toArray(children).map((row, i, rows) => /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(LastListRow.Provider, { value: i === rows.length - 1, children: row }, i)) })
9057
9059
  ] });
9058
9060
  }
9059
9061
 
9060
9062
  // packages/native/src/app-navigation.tsx
9061
9063
  var import_tokens56 = require("@kjun-ui/tokens");
9062
- var import_react62 = require("react");
9064
+ var import_react63 = require("react");
9063
9065
  var import_react_native58 = require("react-native");
9064
9066
  var import_jsx_runtime55 = require("react/jsx-runtime");
9065
9067
  function KjunTopNavigation({ title, description, leading, actions, safeAreaTop = 0 }) {
@@ -9068,13 +9070,13 @@ function KjunTopNavigation({ title, description, leading, actions, safeAreaTop =
9068
9070
  const titleInset = Math.max(0, (geometry2.controlSize - import_tokens56.tokens.typography.sectionTitle.lineHeightPx) / 2);
9069
9071
  const wrapping = import_react_native58.Platform.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : {};
9070
9072
  const edgeInset = (geometry2.controlSize - geometry2.iconSize) / 2;
9071
- const [iconEdges, setIconEdges] = (0, import_react62.useState)({ leading: false, trailing: false });
9073
+ const [iconEdges, setIconEdges] = (0, import_react63.useState)({ leading: false, trailing: false });
9072
9074
  const square = (key) => (event) => {
9073
9075
  const { width, height } = event.nativeEvent.layout;
9074
9076
  const next = Math.abs(width - height) < 1 && Math.abs(height - geometry2.controlSize) < 1;
9075
9077
  setIconEdges((current) => current[key] === next ? current : { ...current, [key]: next });
9076
9078
  };
9077
- const flatten = (node) => import_react62.Children.toArray(node).flatMap((child) => (0, import_react62.isValidElement)(child) && child.type === import_react62.Fragment ? flatten(child.props.children) : [child]);
9079
+ const flatten = (node) => import_react63.Children.toArray(node).flatMap((child) => (0, import_react63.isValidElement)(child) && child.type === import_react63.Fragment ? flatten(child.props.children) : [child]);
9078
9080
  const actionList = flatten(actions);
9079
9081
  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: [
9080
9082
  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) }) }),
@@ -9148,7 +9150,7 @@ function KjunBottomNavigation({ items, value, ariaLabel = "\uC8FC\uC694 \uD0D0\u
9148
9150
  }
9149
9151
  function KjunBottomActionBar({ description, children, safeAreaBottom = 0, keyboardVisible = false, hideOnKeyboard = false }) {
9150
9152
  const { colors } = useKjunStyles();
9151
- const [width, setWidth] = (0, import_react62.useState)(0);
9153
+ const [width, setWidth] = (0, import_react63.useState)(0);
9152
9154
  const geometry2 = import_tokens56.tokens.extensions.navigation;
9153
9155
  const wide = width >= import_tokens56.tokens.responsive.bottomAction + geometry2.padding * 2;
9154
9156
  const wrapping = import_react_native58.Platform.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : {};
@@ -9170,16 +9172,16 @@ function KjunBottomActionBar({ description, children, safeAreaBottom = 0, keyboa
9170
9172
  }
9171
9173
 
9172
9174
  // packages/native/src/media.tsx
9173
- var import_react63 = require("react");
9175
+ var import_react64 = require("react");
9174
9176
  var import_react_native59 = require("react-native");
9175
9177
  var import_tokens57 = require("@kjun-ui/tokens");
9176
9178
  var import_jsx_runtime56 = require("react/jsx-runtime");
9177
9179
  function KjunImage({ src, source, alt, decorative = false, aspectRatio = 1, fit = "cover", fallback: fallback2, onLoad, onError }) {
9178
9180
  const { colors } = useKjunStyles();
9179
- const request = JSON.stringify(source != null ? source : src), current = (0, import_react63.useRef)({ request, version: 0 });
9181
+ const request = JSON.stringify(source != null ? source : src), current = (0, import_react64.useRef)({ request, version: 0 });
9180
9182
  if (current.current.request !== request) current.current = { request, version: current.current.version + 1 };
9181
9183
  const version = current.current.version;
9182
- const [failed, setFailed] = (0, import_react63.useState)(null);
9184
+ const [failed, setFailed] = (0, import_react64.useState)(null);
9183
9185
  const missing = !source && !src;
9184
9186
  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: () => {
9185
9187
  if (version === current.current.version) onLoad == null ? void 0 : onLoad();
@@ -9209,7 +9211,7 @@ function KjunChip({ label, icon, removable = false, disabled = false, size = "md
9209
9211
  }
9210
9212
 
9211
9213
  // packages/native/src/range-input.tsx
9212
- var import_react64 = require("react");
9214
+ var import_react65 = require("react");
9213
9215
  var import_react_native61 = require("react-native");
9214
9216
  var import_tokens59 = require("@kjun-ui/tokens");
9215
9217
  var import_jsx_runtime58 = require("react/jsx-runtime");
@@ -9233,18 +9235,18 @@ function RangeInput({
9233
9235
  const valid = domain.valid && (!Array.isArray(value) || value.length === 2) && values.every(domain.accepts) && (values.length === 1 || values[0] <= values[1]);
9234
9236
  const blocked = disabled || !valid;
9235
9237
  const effectiveMax = valid ? domain.snap(max) : min;
9236
- const [hovered, setHovered] = (0, import_react64.useState)(null);
9237
- const [dragging, setDragging] = (0, import_react64.useState)(null);
9238
- const [focused, setFocused] = (0, import_react64.useState)(null);
9239
- const track = (0, import_react64.useRef)(null), measured = (0, import_react64.useRef)({ x: 0, width: 1 }), selected = (0, import_react64.useRef)(0), dirty = (0, import_react64.useRef)(false);
9240
- const latest = (0, import_react64.useRef)(values);
9238
+ const [hovered, setHovered] = (0, import_react65.useState)(null);
9239
+ const [dragging, setDragging] = (0, import_react65.useState)(null);
9240
+ const [focused, setFocused] = (0, import_react65.useState)(null);
9241
+ const track = (0, import_react65.useRef)(null), measured = (0, import_react65.useRef)({ x: 0, width: 1 }), selected = (0, import_react65.useRef)(0), dirty = (0, import_react65.useRef)(false);
9242
+ const latest = (0, import_react65.useRef)(values);
9241
9243
  latest.current = values;
9242
- const callbacks = (0, import_react64.useRef)({ onValueChange, onChangeCommit });
9244
+ const callbacks = (0, import_react65.useRef)({ onValueChange, onChangeCommit });
9243
9245
  callbacks.current = { onValueChange, onChangeCommit };
9244
- (0, import_react64.useEffect)(() => {
9246
+ (0, import_react65.useEffect)(() => {
9245
9247
  if (!valid) console.warn("KJUN Slider: \uBC94\uC704\xB7step\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
9246
9248
  }, [valid]);
9247
- (0, import_react64.useEffect)(() => {
9249
+ (0, import_react65.useEffect)(() => {
9248
9250
  if (blocked) {
9249
9251
  setDragging(null);
9250
9252
  setHovered(null);
@@ -9284,7 +9286,7 @@ function RangeInput({
9284
9286
  measured.current = { x: pageX, width: Math.max(1, width) };
9285
9287
  });
9286
9288
  };
9287
- const responder = (0, import_react64.useMemo)(
9289
+ const responder = (0, import_react65.useMemo)(
9288
9290
  () => import_react_native61.PanResponder.create({
9289
9291
  onStartShouldSetPanResponderCapture: () => !disabled && valid,
9290
9292
  onMoveShouldSetPanResponderCapture: (_event, gesture) => !disabled && valid && Math.abs(gesture.dx) > 3 && Math.abs(gesture.dx) > Math.abs(gesture.dy),
@@ -9509,7 +9511,7 @@ function KjunRangeSlider(props) {
9509
9511
  }
9510
9512
 
9511
9513
  // packages/native/src/quantity-stepper.tsx
9512
- var import_react65 = require("react");
9514
+ var import_react66 = require("react");
9513
9515
  var import_react_native62 = require("react-native");
9514
9516
  var import_tokens60 = require("@kjun-ui/tokens");
9515
9517
 
@@ -9531,16 +9533,16 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9531
9533
  const increaseStyle = { ...buttonStyle, borderRadius: 0, borderTopRightRadius: outer, borderBottomRightRadius: outer };
9532
9534
  const domain = (0, import_tokens60.createNumberDomain)({ min, max, step, precision }), valid = domain.valid && domain.accepts(value), blocked = disabled || !valid;
9533
9535
  const surface = useFieldSurface(size, { disabled: blocked, invalid });
9534
- const [draft, setDraft] = (0, import_react65.useState)(String(value));
9535
- const inputRef = (0, import_react65.useRef)(null);
9536
- const composing = (0, import_react65.useRef)(false), dirty = (0, import_react65.useRef)(false), accepted = (0, import_react65.useRef)(value);
9537
- const cancelledEscape = (0, import_react65.useRef)(false);
9538
- (0, import_react65.useEffect)(() => {
9536
+ const [draft, setDraft] = (0, import_react66.useState)(String(value));
9537
+ const inputRef = (0, import_react66.useRef)(null);
9538
+ const composing = (0, import_react66.useRef)(false), dirty = (0, import_react66.useRef)(false), accepted = (0, import_react66.useRef)(value);
9539
+ const cancelledEscape = (0, import_react66.useRef)(false);
9540
+ (0, import_react66.useEffect)(() => {
9539
9541
  setDraft(String(value));
9540
9542
  accepted.current = value;
9541
9543
  dirty.current = false;
9542
9544
  }, [value, precision, min, max, step]);
9543
- (0, import_react65.useEffect)(() => {
9545
+ (0, import_react66.useEffect)(() => {
9544
9546
  if (!valid) console.warn("KJUN QuantityStepper: \uBC94\uC704\xB7step\xB7\uC815\uBC00\uB3C4\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
9545
9547
  }, [valid]);
9546
9548
  const emit = (next) => {
@@ -9577,7 +9579,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9577
9579
  } else if (action === "commit") commit();
9578
9580
  else move(action === "increment" ? 1 : -1);
9579
9581
  };
9580
- const handlers = (0, import_react65.useRef)({ key, edit: (text) => {
9582
+ const handlers = (0, import_react66.useRef)({ key, edit: (text) => {
9581
9583
  dirty.current = true;
9582
9584
  setDraft(text);
9583
9585
  } });
@@ -9585,7 +9587,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9585
9587
  dirty.current = true;
9586
9588
  setDraft(text);
9587
9589
  } };
9588
- (0, import_react65.useEffect)(() => {
9590
+ (0, import_react66.useEffect)(() => {
9589
9591
  if (import_react_native62.Platform.OS !== "web") return;
9590
9592
  const input = inputRef.current;
9591
9593
  const keydown = (event) => handlers.current.key(event);
@@ -9657,19 +9659,19 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9657
9659
  }
9658
9660
 
9659
9661
  // packages/native/src/time-picker.tsx
9660
- var import_react66 = require("react");
9662
+ var import_react67 = require("react");
9661
9663
  var import_react_native63 = require("react-native");
9662
9664
  var import_tokens61 = require("@kjun-ui/tokens");
9663
9665
  var import_jsx_runtime60 = require("react/jsx-runtime");
9664
9666
  function KjunTimePicker({ value, precision = "minute", min, max, minuteStep = 1, secondStep = 1, disabled = false, error = false, clearable = true, size = "md", ariaLabel = "\uC2DC\uAC04", onValueChange, onChangeCommit }) {
9665
9667
  const geometry2 = import_tokens61.tokens.extensions.timePicker, spec = import_tokens61.tokens.input[size];
9666
9668
  const { colors } = useKjunStyles();
9667
- const first = (0, import_react66.useRef)(null);
9668
- const [focused, setFocused] = (0, import_react66.useState)(false), [opened, setOpened] = (0, import_react66.useState)(null);
9669
- const field = useKjunField(), uid = (0, import_react66.useId)();
9669
+ const first = (0, import_react67.useRef)(null);
9670
+ const [focused, setFocused] = (0, import_react67.useState)(false), [opened, setOpened] = (0, import_react67.useState)(null);
9671
+ const field = useKjunField(), uid = (0, import_react67.useId)();
9670
9672
  const fieldId = (field == null ? void 0 : field.id) || `kjun-time-${uid}`;
9671
- const domain = (0, import_react66.useMemo)(() => (0, import_tokens61.createTimeDomain)({ precision, min, max, minuteStep, secondStep }), [precision, min, max, minuteStep, secondStep]), valid = domain.valid && domain.accepts(value);
9672
- (0, import_react66.useEffect)(() => {
9673
+ const domain = (0, import_react67.useMemo)(() => (0, import_tokens61.createTimeDomain)({ precision, min, max, minuteStep, secondStep }), [precision, min, max, minuteStep, secondStep]), valid = domain.valid && domain.accepts(value);
9674
+ (0, import_react67.useEffect)(() => {
9673
9675
  if (!valid) console.warn("KJUN TimePicker: \uC2DC\uAC01 \uBC94\uC704\xB7\uAC04\uACA9\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
9674
9676
  }, [valid]);
9675
9677
  const change = (next) => {
@@ -9680,7 +9682,7 @@ function KjunTimePicker({ value, precision = "minute", min, max, minuteStep = 1,
9680
9682
  };
9681
9683
  const time = value === null ? null : domain.parse(value);
9682
9684
  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: [
9683
- /* @__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_react66.Fragment, { children: [
9685
+ /* @__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_react67.Fragment, { children: [
9684
9686
  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: ":" }),
9685
9687
  /* @__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) => {
9686
9688
  if (typeof next === "number") change(domain.select(value, index, next));