@kjun-ui/native 0.9.0 → 0.10.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.
package/dist/index.js CHANGED
@@ -26,9 +26,23 @@ function mergeIcons(parent, own) {
26
26
  function withFallbackIcons(registry, fallbacks) {
27
27
  return mergeIcons(fallbacks, registry);
28
28
  }
29
+ var warnedIcons = /* @__PURE__ */ new Set();
30
+ function developmentMode() {
31
+ try {
32
+ return true;
33
+ } catch (e) {
34
+ return false;
35
+ }
36
+ }
37
+ function warnMissingIcon(name) {
38
+ if (!developmentMode() || warnedIcons.has(name)) return;
39
+ warnedIcons.add(name);
40
+ console.warn(`KJUN Icon: "${name}" \uC544\uC774\uCF58\uC774 \uB4F1\uB85D\uB418\uC9C0 \uC54A\uC544 help-circle\uB85C \uB300\uC2E0\uD569\uB2C8\uB2E4. @kjun-ui/icons/icons/${name}\uB97C import\uD574 KjunProvider\uC758 icons\uC5D0 \uB4F1\uB85D\uD558\uC138\uC694.`);
41
+ }
29
42
  function resolveIcon(registry, name, filled = false) {
30
43
  var _a;
31
44
  const definition = Object.prototype.hasOwnProperty.call(registry, name) ? registry[name] : void 0;
45
+ if (!definition && name) warnMissingIcon(name);
32
46
  const filledNodes = filled && (definition == null ? void 0 : definition.filled);
33
47
  return { nodes: filledNodes || (definition == null ? void 0 : definition.outline) || ((_a = registry["help-circle"]) == null ? void 0 : _a.outline) || defaultIcons["help-circle"].outline, filled: !!filledNodes };
34
48
  }
@@ -693,6 +707,7 @@ function KjunButton(input) {
693
707
  size: declaredSize = "md",
694
708
  loading = false,
695
709
  block = false,
710
+ wrap = false,
696
711
  disabled = false,
697
712
  prefixIcon,
698
713
  suffixIcon,
@@ -713,7 +728,7 @@ function KjunButton(input) {
713
728
  const formActions = useContext5(FormActionsContext);
714
729
  const cardActions = useContext5(CardActionsContext);
715
730
  const stateContent = useContext5(DataStateContentContext);
716
- const multiline = !!formActions || cardActions || !!stateContent || (navigation == null ? void 0 : navigation.slot) === "actions";
731
+ const multiline = wrap || !!formActions || cardActions || !!stateContent || (navigation == null ? void 0 : navigation.slot) === "actions";
717
732
  const height = Math.max(tokens3.button.heights[size], (_a = navigation == null ? void 0 : navigation.controlSize) != null ? _a : 0);
718
733
  const iconSize = (_b = navigation == null ? void 0 : navigation.iconSize) != null ? _b : tokens3.button.iconSizes[size];
719
734
  const hasContent2 = children !== void 0 && children !== null && children !== false && children !== "";
@@ -874,6 +889,16 @@ function FieldClear({ size, disabled, label, onPress }) {
874
889
  );
875
890
  }
876
891
 
892
+ // shared/package-runtime/aria-ids.ts
893
+ function mergeIds(...lists) {
894
+ const ids = [...new Set(lists.flatMap((list) => (list != null ? list : "").split(/\s+/)).filter(Boolean))];
895
+ return ids.length ? ids.join(" ") : void 0;
896
+ }
897
+ function joinHints(...parts) {
898
+ const text = parts.filter(Boolean);
899
+ return text.length ? text.join(". ") : void 0;
900
+ }
901
+
877
902
  // packages/native/src/field-surface.ts
878
903
  import { useState as useState6 } from "react";
879
904
  import { Animated as Animated3, Platform as Platform5, TextInput } from "react-native";
@@ -1061,6 +1086,7 @@ function KjunFormGroup({
1061
1086
  required = false,
1062
1087
  error = "",
1063
1088
  hint = "",
1089
+ labelHidden = false,
1064
1090
  children,
1065
1091
  style
1066
1092
  }) {
@@ -1070,9 +1096,17 @@ function KjunFormGroup({
1070
1096
  return /* @__PURE__ */ jsx8(
1071
1097
  FieldContext.Provider,
1072
1098
  {
1073
- value: { id: fieldId, label, error, hint, required, describedBy: error ? fieldId + "-error" : hint ? fieldId + "-hint" : void 0 },
1099
+ value: {
1100
+ id: fieldId,
1101
+ label,
1102
+ error,
1103
+ hint,
1104
+ required,
1105
+ labelHidden,
1106
+ describedBy: mergeIds(hint ? fieldId + "-hint" : void 0, error ? fieldId + "-error" : void 0)
1107
+ },
1074
1108
  children: /* @__PURE__ */ jsxs2(View4, { style: [{ minWidth: 0, marginBottom: tokens7.extensions.form.itemGap }, style], children: [
1075
- label && /* @__PURE__ */ jsxs2(
1109
+ label && !labelHidden && /* @__PURE__ */ jsxs2(
1076
1110
  Text3,
1077
1111
  {
1078
1112
  nativeID: fieldId + "-label",
@@ -1089,7 +1123,15 @@ function KjunFormGroup({
1089
1123
  }
1090
1124
  ),
1091
1125
  children,
1092
- (error || hint) && /* @__PURE__ */ jsxs2(
1126
+ !!hint && /* @__PURE__ */ jsx8(
1127
+ Text3,
1128
+ {
1129
+ nativeID: fieldId + "-hint",
1130
+ style: { fontFamily, ...typeStyle("caption"), marginTop: tokens7.extensions.form.messageGap, color: c.textSecondary },
1131
+ children: hint
1132
+ }
1133
+ ),
1134
+ !!error && /* @__PURE__ */ jsxs2(
1093
1135
  View4,
1094
1136
  {
1095
1137
  style: {
@@ -1099,19 +1141,14 @@ function KjunFormGroup({
1099
1141
  marginTop: tokens7.extensions.form.messageGap
1100
1142
  },
1101
1143
  children: [
1102
- !!error && /* @__PURE__ */ jsx8(KjunIcon, { name: "alert-circle", size: tokens7.extensions.form.messageIconSize, color: c.danger }),
1144
+ /* @__PURE__ */ jsx8(KjunIcon, { name: "alert-circle", size: tokens7.extensions.form.messageIconSize, color: c.danger }),
1103
1145
  /* @__PURE__ */ jsx8(
1104
1146
  Text3,
1105
1147
  {
1106
- nativeID: fieldId + (error ? "-error" : "-hint"),
1107
- accessibilityRole: error ? "alert" : void 0,
1108
- style: {
1109
- fontFamily,
1110
- ...typeStyle("caption"),
1111
- flexShrink: 1,
1112
- color: error ? c.danger : c.textSecondary
1113
- },
1114
- children: error || hint
1148
+ nativeID: fieldId + "-error",
1149
+ accessibilityRole: "alert",
1150
+ style: { fontFamily, ...typeStyle("caption"), flexShrink: 1, color: c.danger },
1151
+ children: error
1115
1152
  }
1116
1153
  )
1117
1154
  ]
@@ -1185,8 +1222,8 @@ var KjunInput = forwardRef2(function KjunInput2({
1185
1222
  editable: canEdit,
1186
1223
  readOnly,
1187
1224
  accessibilityLabel: ariaLabel || props.accessibilityLabel || (field == null ? void 0 : field.label),
1188
- accessibilityLabelledBy: (field == null ? void 0 : field.label) ? field.id + "-label" : void 0,
1189
- accessibilityHint: errorMessage || (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint) || props.accessibilityHint,
1225
+ accessibilityLabelledBy: (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0,
1226
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, errorMessage || (field == null ? void 0 : field.error)) || props.accessibilityHint,
1190
1227
  accessibilityState: { disabled },
1191
1228
  ...Platform6.OS === "web" ? {
1192
1229
  // Native Web TextInput does not translate accessibilityState.
@@ -1784,6 +1821,13 @@ function KjunCard({
1784
1821
  ] });
1785
1822
  }
1786
1823
 
1824
+ // shared/package-runtime/alert-live.ts
1825
+ function alertRole(tone, live) {
1826
+ if (live === "off") return void 0;
1827
+ if (live) return live === "assertive" ? "alert" : "status";
1828
+ return tone === "danger" ? "alert" : "status";
1829
+ }
1830
+
1787
1831
  // packages/native/src/display-primitives.tsx
1788
1832
  import { tokens as tokens10 } from "@kjun-ui/tokens";
1789
1833
  import { useContext as useContext8, useEffect as useEffect5, useRef as useRef6, useState as useState9 } from "react";
@@ -1981,6 +2025,7 @@ function KjunAlert({
1981
2025
  size = "md",
1982
2026
  title,
1983
2027
  closable = false,
2028
+ live,
1984
2029
  children,
1985
2030
  actions,
1986
2031
  onClose
@@ -1997,7 +2042,7 @@ function KjunAlert({
1997
2042
  return /* @__PURE__ */ jsxs6(
1998
2043
  View9,
1999
2044
  {
2000
- role: tone === "danger" ? "alert" : "status",
2045
+ role: alertRole(tone, live),
2001
2046
  style: {
2002
2047
  flexDirection: "row",
2003
2048
  alignItems: "flex-start",
@@ -3447,13 +3492,13 @@ var KjunTextarea = forwardRef3(
3447
3492
  editable: !disabled && !readonly && !props.readOnly && props.editable !== false,
3448
3493
  nativeID: props.nativeID || (field == null ? void 0 : field.id),
3449
3494
  accessibilityLabel: ariaLabel || props.accessibilityLabel || (field == null ? void 0 : field.label),
3450
- accessibilityHint: (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint) || props.accessibilityHint,
3495
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, field == null ? void 0 : field.error) || props.accessibilityHint,
3451
3496
  accessibilityState: { disabled },
3452
3497
  ...Platform13.OS === "web" ? {
3453
3498
  "aria-disabled": disabled,
3454
3499
  "aria-invalid": invalid || void 0,
3455
3500
  "aria-describedby": field == null ? void 0 : field.describedBy,
3456
- "aria-labelledby": (field == null ? void 0 : field.label) ? field.id + "-label" : void 0,
3501
+ "aria-labelledby": (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0,
3457
3502
  "aria-required": (field == null ? void 0 : field.required) || void 0
3458
3503
  } : {},
3459
3504
  placeholderTextColor: disabled ? colors.textDisabled : colors.inputPlaceholder,
@@ -3723,8 +3768,11 @@ function FloatingPanel({
3723
3768
  flip = true,
3724
3769
  minimumWidth = 0,
3725
3770
  focusOnOpen = false,
3726
- reveal
3771
+ reveal,
3772
+ header,
3773
+ nonModal = false
3727
3774
  }) {
3775
+ const [headerHeight, setHeaderHeight] = useState16(0);
3728
3776
  const panelRef = useRef16(null);
3729
3777
  const scroll = useRef16(null), viewport = useRef16(0);
3730
3778
  const { colors } = useKjunStyles(), window2 = useWindowDimensions4(), [anchored, setAnchored] = useState16(false), [sized, setSized] = useState16(false), [anchor, setAnchor] = useState16({ x: 0, y: 0, width: 0, height: 0 }), [panel, setPanel] = useState16({ width: 0, height: 0 });
@@ -3779,7 +3827,7 @@ function FloatingPanel({
3779
3827
  maximumWidth,
3780
3828
  Math.max(minimumPanelWidth, contentSizedMenu ? panel.width : matchTriggerWidth ? anchor.width : tokens21.extensions.popover.width)
3781
3829
  );
3782
- const available = Math.min(maxHeight != null ? maxHeight : field ? tokens21.extensions.menu.listMaxHeight : tokens21.extensions.floating.maxHeight, window2.height - 2 * tokens21.extensions.floating.viewportInset), height = Math.min(panel.height, available), roomAbove = anchor.y - gap - tokens21.extensions.floating.viewportInset, roomBelow = window2.height - anchor.y - anchor.height - gap - tokens21.extensions.floating.viewportInset, above = placement.startsWith("top") ? !(flip && height > roomAbove && roomBelow > roomAbove) : flip && height > roomBelow && roomAbove > roomBelow;
3830
+ const available = Math.min(maxHeight != null ? maxHeight : field ? tokens21.extensions.menu.listMaxHeight : tokens21.extensions.floating.maxHeight, window2.height - 2 * tokens21.extensions.floating.viewportInset), height = Math.min(panel.height + (header != null ? headerHeight : 0), available), roomAbove = anchor.y - gap - tokens21.extensions.floating.viewportInset, roomBelow = window2.height - anchor.y - anchor.height - gap - tokens21.extensions.floating.viewportInset, above = placement.startsWith("top") ? !(flip && height > roomAbove && roomBelow > roomAbove) : flip && height > roomBelow && roomAbove > roomBelow;
3783
3831
  let left = placement.endsWith("end") ? anchor.x + anchor.width - width : placement === "top" || placement === "bottom" ? anchor.x + (anchor.width - width) / 2 : anchor.x, top = above ? anchor.y - height - gap : anchor.y + anchor.height + gap;
3784
3832
  if (placement === "left" || placement === "right") {
3785
3833
  left = placement === "left" ? anchor.x - width - gap : anchor.x + anchor.width + gap;
@@ -3863,7 +3911,7 @@ function FloatingPanel({
3863
3911
  }
3864
3912
  }
3865
3913
  ),
3866
- /* @__PURE__ */ jsx24(
3914
+ /* @__PURE__ */ jsxs14(
3867
3915
  Animated13.View,
3868
3916
  {
3869
3917
  ref: panelRef,
@@ -3874,7 +3922,7 @@ function FloatingPanel({
3874
3922
  pointerEvents: open && anchored && sized ? "auto" : "none",
3875
3923
  accessibilityElementsHidden: !open || !anchored || !sized,
3876
3924
  ...!open || !anchored || !sized ? { inert: true } : {},
3877
- accessibilityViewIsModal: true,
3925
+ accessibilityViewIsModal: !nonModal,
3878
3926
  accessibilityLabel: ariaLabel,
3879
3927
  style: {
3880
3928
  opacity: anchored && sized ? motion.opacity : 0,
@@ -3896,26 +3944,42 @@ function FloatingPanel({
3896
3944
  boxShadow: (tooltip ? tokens21.extensions.tooltip.elevation : popover ? tokens21.extensions.popover.elevation : tokens21.extensions.menu.elevation).map(({ colorRole, ...layer2 }) => ({ ...layer2, color: colors[colorRole] })),
3897
3945
  overflow: "hidden"
3898
3946
  },
3899
- children: /* @__PURE__ */ jsx24(
3900
- ScrollView4,
3901
- {
3902
- ref: scroll,
3903
- onLayout: (event) => {
3904
- viewport.current = event.nativeEvent.layout.height;
3905
- },
3906
- keyboardShouldPersistTaps: "handled",
3907
- style: { maxHeight: available },
3908
- contentContainerStyle: { padding: menu || field ? tokens21.extensions.menu.padding : 0 },
3909
- onContentSizeChange: (width2, height2) => {
3910
- if (contentSizedMenu) return;
3911
- setSized(true);
3912
- setPanel(
3913
- (previous) => previous.width === width2 && previous.height === height2 ? previous : { width: width2, height: height2 }
3914
- );
3915
- },
3916
- children
3917
- }
3918
- )
3947
+ children: [
3948
+ header != null && /* @__PURE__ */ jsx24(
3949
+ View18,
3950
+ {
3951
+ onLayout: ({ nativeEvent: { layout } }) => setHeaderHeight(layout.height),
3952
+ style: {
3953
+ paddingTop: tokens21.extensions.popover.padding,
3954
+ paddingHorizontal: tokens21.extensions.popover.padding,
3955
+ paddingBottom: tokens21.extensions.popover.headerPaddingBottom,
3956
+ borderBottomWidth: tokens21.border.defaultWidth,
3957
+ borderBottomColor: colors.divider
3958
+ },
3959
+ children: header
3960
+ }
3961
+ ),
3962
+ /* @__PURE__ */ jsx24(
3963
+ ScrollView4,
3964
+ {
3965
+ ref: scroll,
3966
+ onLayout: (event) => {
3967
+ viewport.current = event.nativeEvent.layout.height;
3968
+ },
3969
+ keyboardShouldPersistTaps: "handled",
3970
+ style: { maxHeight: available - (header != null ? headerHeight : 0), flexShrink: 1 },
3971
+ contentContainerStyle: { padding: menu || field ? tokens21.extensions.menu.padding : 0 },
3972
+ onContentSizeChange: (width2, height2) => {
3973
+ if (contentSizedMenu) return;
3974
+ setSized(true);
3975
+ setPanel(
3976
+ (previous) => previous.width === width2 && previous.height === height2 ? previous : { width: width2, height: height2 }
3977
+ );
3978
+ },
3979
+ children
3980
+ }
3981
+ )
3982
+ ]
3919
3983
  }
3920
3984
  )
3921
3985
  ]
@@ -3950,9 +4014,12 @@ function KjunPopover({
3950
4014
  focusOnOpen = false,
3951
4015
  ariaLabel,
3952
4016
  placement = "bottom",
4017
+ header,
4018
+ modal = true,
3953
4019
  trigger,
3954
4020
  children
3955
4021
  }) {
4022
+ const { colors } = useKjunStyles();
3956
4023
  const [internal, setInternal] = useState17(false), open = controlled != null ? controlled : internal, ref = useRef17(null);
3957
4024
  const change = (v) => {
3958
4025
  setInternal(v);
@@ -3981,6 +4048,8 @@ function KjunPopover({
3981
4048
  popover: true,
3982
4049
  focusOnOpen,
3983
4050
  ariaLabel,
4051
+ header: header != null ? content(header, { ...typeStyle("cardTitle"), color: colors.text }) : void 0,
4052
+ nonModal: !modal,
3984
4053
  children: /* @__PURE__ */ jsx25(View19, { style: { padding: noPadding ? 0 : tokens22.extensions.popover.padding, gap: noPadding ? 0 : tokens22.extensions.popover.gap }, children: content(
3985
4054
  typeof children === "function" ? children(() => change(false)) : children,
3986
4055
  Platform16.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : void 0
@@ -3995,9 +4064,18 @@ function KjunTooltip({
3995
4064
  delay = 0,
3996
4065
  children,
3997
4066
  onPress,
3998
- disabled
4067
+ disabled,
4068
+ open: controlled,
4069
+ onOpenChange
3999
4070
  }) {
4000
- const { colors } = useKjunStyles(), [open, setOpen] = useState17(false), ref = useRef17(null), timer = useRef17(void 0);
4071
+ const { colors } = useKjunStyles(), [internal, setInternal] = useState17(false), open = controlled != null ? controlled : internal, latest = useRef17({ open, onOpenChange }), setOpen = (value) => {
4072
+ var _a, _b;
4073
+ if (latest.current.open === value) return;
4074
+ latest.current.open = value;
4075
+ setInternal(value);
4076
+ (_b = (_a = latest.current).onOpenChange) == null ? void 0 : _b.call(_a, value);
4077
+ }, ref = useRef17(null), timer = useRef17(void 0);
4078
+ latest.current = { open, onOpenChange };
4001
4079
  const scopeActive = useActiveLayerScope();
4002
4080
  const hide = () => {
4003
4081
  clearTimeout(timer.current);
@@ -7272,7 +7350,7 @@ function TableToolbar({
7272
7350
  selectable && selected.length > 0 && /* @__PURE__ */ jsxs29(
7273
7351
  View35,
7274
7352
  {
7275
- accessibilityRole: "toolbar",
7353
+ role: "group",
7276
7354
  accessibilityLabel: "\uC120\uD0DD \uC791\uC5C5",
7277
7355
  style: [toolbarStyle, { backgroundColor: colors.selectedBg }],
7278
7356
  children: [
@@ -9624,7 +9702,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9624
9702
  const { colors, fontFamily } = useKjunStyles();
9625
9703
  const field = useKjunField();
9626
9704
  const invalid = error || !!(field == null ? void 0 : field.error);
9627
- const labelledBy = !ariaLabel && (field == null ? void 0 : field.label) ? field.id + "-label" : void 0;
9705
+ const labelledBy = !ariaLabel && (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0;
9628
9706
  const geometry2 = tokens57.extensions.compound;
9629
9707
  const buttonStyle = { width: tokens57.input[size].height, paddingHorizontal: 0 };
9630
9708
  const outer = tokens57.input[size].radius;
@@ -9732,7 +9810,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9732
9810
  nativeID: id || (field == null ? void 0 : field.id),
9733
9811
  accessibilityLabel: ariaLabel || (field == null ? void 0 : field.label) || "\uC218\uB7C9",
9734
9812
  accessibilityLabelledBy: labelledBy,
9735
- accessibilityHint: (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint),
9813
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, field == null ? void 0 : field.error),
9736
9814
  role: "spinbutton",
9737
9815
  accessibilityValue: { min, max, now: value },
9738
9816
  accessibilityState: { disabled: blocked },