@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.cjs CHANGED
@@ -141,9 +141,23 @@ function mergeIcons(parent, own) {
141
141
  function withFallbackIcons(registry, fallbacks) {
142
142
  return mergeIcons(fallbacks, registry);
143
143
  }
144
+ var warnedIcons = /* @__PURE__ */ new Set();
145
+ function developmentMode() {
146
+ try {
147
+ return true;
148
+ } catch (e) {
149
+ return false;
150
+ }
151
+ }
152
+ function warnMissingIcon(name) {
153
+ if (!developmentMode() || warnedIcons.has(name)) return;
154
+ warnedIcons.add(name);
155
+ 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.`);
156
+ }
144
157
  function resolveIcon(registry, name, filled = false) {
145
158
  var _a;
146
159
  const definition = Object.prototype.hasOwnProperty.call(registry, name) ? registry[name] : void 0;
160
+ if (!definition && name) warnMissingIcon(name);
147
161
  const filledNodes = filled && (definition == null ? void 0 : definition.filled);
148
162
  return { nodes: filledNodes || (definition == null ? void 0 : definition.outline) || ((_a = registry["help-circle"]) == null ? void 0 : _a.outline) || import_defaults.defaultIcons["help-circle"].outline, filled: !!filledNodes };
149
163
  }
@@ -783,6 +797,7 @@ function KjunButton(input) {
783
797
  size: declaredSize = "md",
784
798
  loading = false,
785
799
  block = false,
800
+ wrap = false,
786
801
  disabled = false,
787
802
  prefixIcon,
788
803
  suffixIcon,
@@ -803,7 +818,7 @@ function KjunButton(input) {
803
818
  const formActions = (0, import_react12.useContext)(FormActionsContext);
804
819
  const cardActions = (0, import_react12.useContext)(CardActionsContext);
805
820
  const stateContent = (0, import_react12.useContext)(DataStateContentContext);
806
- const multiline = !!formActions || cardActions || !!stateContent || (navigation == null ? void 0 : navigation.slot) === "actions";
821
+ const multiline = wrap || !!formActions || cardActions || !!stateContent || (navigation == null ? void 0 : navigation.slot) === "actions";
807
822
  const height = Math.max(import_tokens5.tokens.button.heights[size], (_a = navigation == null ? void 0 : navigation.controlSize) != null ? _a : 0);
808
823
  const iconSize = (_b = navigation == null ? void 0 : navigation.iconSize) != null ? _b : import_tokens5.tokens.button.iconSizes[size];
809
824
  const hasContent2 = children !== void 0 && children !== null && children !== false && children !== "";
@@ -964,6 +979,16 @@ function FieldClear({ size, disabled, label, onPress }) {
964
979
  );
965
980
  }
966
981
 
982
+ // shared/package-runtime/aria-ids.ts
983
+ function mergeIds(...lists) {
984
+ const ids = [...new Set(lists.flatMap((list) => (list != null ? list : "").split(/\s+/)).filter(Boolean))];
985
+ return ids.length ? ids.join(" ") : void 0;
986
+ }
987
+ function joinHints(...parts) {
988
+ const text = parts.filter(Boolean);
989
+ return text.length ? text.join(". ") : void 0;
990
+ }
991
+
967
992
  // packages/native/src/field-surface.ts
968
993
  var import_react15 = require("react");
969
994
  var import_react_native7 = require("react-native");
@@ -1132,6 +1157,7 @@ function KjunFormGroup({
1132
1157
  required = false,
1133
1158
  error = "",
1134
1159
  hint = "",
1160
+ labelHidden = false,
1135
1161
  children,
1136
1162
  style
1137
1163
  }) {
@@ -1141,9 +1167,17 @@ function KjunFormGroup({
1141
1167
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1142
1168
  FieldContext.Provider,
1143
1169
  {
1144
- value: { id: fieldId, label, error, hint, required, describedBy: error ? fieldId + "-error" : hint ? fieldId + "-hint" : void 0 },
1170
+ value: {
1171
+ id: fieldId,
1172
+ label,
1173
+ error,
1174
+ hint,
1175
+ required,
1176
+ labelHidden,
1177
+ describedBy: mergeIds(hint ? fieldId + "-hint" : void 0, error ? fieldId + "-error" : void 0)
1178
+ },
1145
1179
  children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native8.View, { style: [{ minWidth: 0, marginBottom: import_tokens9.tokens.extensions.form.itemGap }, style], children: [
1146
- label && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1180
+ label && !labelHidden && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1147
1181
  import_react_native8.Text,
1148
1182
  {
1149
1183
  nativeID: fieldId + "-label",
@@ -1160,7 +1194,15 @@ function KjunFormGroup({
1160
1194
  }
1161
1195
  ),
1162
1196
  children,
1163
- (error || hint) && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1197
+ !!hint && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1198
+ import_react_native8.Text,
1199
+ {
1200
+ nativeID: fieldId + "-hint",
1201
+ style: { fontFamily, ...typeStyle("caption"), marginTop: import_tokens9.tokens.extensions.form.messageGap, color: c.textSecondary },
1202
+ children: hint
1203
+ }
1204
+ ),
1205
+ !!error && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1164
1206
  import_react_native8.View,
1165
1207
  {
1166
1208
  style: {
@@ -1170,19 +1212,14 @@ function KjunFormGroup({
1170
1212
  marginTop: import_tokens9.tokens.extensions.form.messageGap
1171
1213
  },
1172
1214
  children: [
1173
- !!error && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(KjunIcon, { name: "alert-circle", size: import_tokens9.tokens.extensions.form.messageIconSize, color: c.danger }),
1215
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(KjunIcon, { name: "alert-circle", size: import_tokens9.tokens.extensions.form.messageIconSize, color: c.danger }),
1174
1216
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1175
1217
  import_react_native8.Text,
1176
1218
  {
1177
- nativeID: fieldId + (error ? "-error" : "-hint"),
1178
- accessibilityRole: error ? "alert" : void 0,
1179
- style: {
1180
- fontFamily,
1181
- ...typeStyle("caption"),
1182
- flexShrink: 1,
1183
- color: error ? c.danger : c.textSecondary
1184
- },
1185
- children: error || hint
1219
+ nativeID: fieldId + "-error",
1220
+ accessibilityRole: "alert",
1221
+ style: { fontFamily, ...typeStyle("caption"), flexShrink: 1, color: c.danger },
1222
+ children: error
1186
1223
  }
1187
1224
  )
1188
1225
  ]
@@ -1256,8 +1293,8 @@ var KjunInput = (0, import_react16.forwardRef)(function KjunInput2({
1256
1293
  editable: canEdit,
1257
1294
  readOnly,
1258
1295
  accessibilityLabel: ariaLabel || props.accessibilityLabel || (field == null ? void 0 : field.label),
1259
- accessibilityLabelledBy: (field == null ? void 0 : field.label) ? field.id + "-label" : void 0,
1260
- accessibilityHint: errorMessage || (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint) || props.accessibilityHint,
1296
+ accessibilityLabelledBy: (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0,
1297
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, errorMessage || (field == null ? void 0 : field.error)) || props.accessibilityHint,
1261
1298
  accessibilityState: { disabled },
1262
1299
  ...import_react_native8.Platform.OS === "web" ? {
1263
1300
  // Native Web TextInput does not translate accessibilityState.
@@ -1843,6 +1880,13 @@ function KjunCard({
1843
1880
  ] });
1844
1881
  }
1845
1882
 
1883
+ // shared/package-runtime/alert-live.ts
1884
+ function alertRole(tone, live) {
1885
+ if (live === "off") return void 0;
1886
+ if (live) return live === "assertive" ? "alert" : "status";
1887
+ return tone === "danger" ? "alert" : "status";
1888
+ }
1889
+
1846
1890
  // packages/native/src/display-primitives.tsx
1847
1891
  var import_tokens12 = require("@kjun-ui/tokens");
1848
1892
  var import_react21 = require("react");
@@ -2035,6 +2079,7 @@ function KjunAlert({
2035
2079
  size = "md",
2036
2080
  title,
2037
2081
  closable = false,
2082
+ live,
2038
2083
  children,
2039
2084
  actions,
2040
2085
  onClose
@@ -2051,7 +2096,7 @@ function KjunAlert({
2051
2096
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2052
2097
  import_react_native15.View,
2053
2098
  {
2054
- role: tone === "danger" ? "alert" : "status",
2099
+ role: alertRole(tone, live),
2055
2100
  style: {
2056
2101
  flexDirection: "row",
2057
2102
  alignItems: "flex-start",
@@ -3472,13 +3517,13 @@ var KjunTextarea = (0, import_react32.forwardRef)(
3472
3517
  editable: !disabled && !readonly && !props.readOnly && props.editable !== false,
3473
3518
  nativeID: props.nativeID || (field == null ? void 0 : field.id),
3474
3519
  accessibilityLabel: ariaLabel || props.accessibilityLabel || (field == null ? void 0 : field.label),
3475
- accessibilityHint: (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint) || props.accessibilityHint,
3520
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, field == null ? void 0 : field.error) || props.accessibilityHint,
3476
3521
  accessibilityState: { disabled },
3477
3522
  ...import_react_native24.Platform.OS === "web" ? {
3478
3523
  "aria-disabled": disabled,
3479
3524
  "aria-invalid": invalid || void 0,
3480
3525
  "aria-describedby": field == null ? void 0 : field.describedBy,
3481
- "aria-labelledby": (field == null ? void 0 : field.label) ? field.id + "-label" : void 0,
3526
+ "aria-labelledby": (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0,
3482
3527
  "aria-required": (field == null ? void 0 : field.required) || void 0
3483
3528
  } : {},
3484
3529
  placeholderTextColor: disabled ? colors.textDisabled : colors.inputPlaceholder,
@@ -3717,8 +3762,11 @@ function FloatingPanel({
3717
3762
  flip = true,
3718
3763
  minimumWidth = 0,
3719
3764
  focusOnOpen = false,
3720
- reveal
3765
+ reveal,
3766
+ header,
3767
+ nonModal = false
3721
3768
  }) {
3769
+ const [headerHeight, setHeaderHeight] = (0, import_react34.useState)(0);
3722
3770
  const panelRef = (0, import_react34.useRef)(null);
3723
3771
  const scroll = (0, import_react34.useRef)(null), viewport = (0, import_react34.useRef)(0);
3724
3772
  const { colors } = useKjunStyles(), window2 = (0, import_react_native28.useWindowDimensions)(), [anchored, setAnchored] = (0, import_react34.useState)(false), [sized, setSized] = (0, import_react34.useState)(false), [anchor, setAnchor] = (0, import_react34.useState)({ x: 0, y: 0, width: 0, height: 0 }), [panel, setPanel] = (0, import_react34.useState)({ width: 0, height: 0 });
@@ -3773,7 +3821,7 @@ function FloatingPanel({
3773
3821
  maximumWidth,
3774
3822
  Math.max(minimumPanelWidth, contentSizedMenu ? panel.width : matchTriggerWidth ? anchor.width : import_tokens23.tokens.extensions.popover.width)
3775
3823
  );
3776
- const available = Math.min(maxHeight != null ? maxHeight : field ? import_tokens23.tokens.extensions.menu.listMaxHeight : import_tokens23.tokens.extensions.floating.maxHeight, window2.height - 2 * import_tokens23.tokens.extensions.floating.viewportInset), height = Math.min(panel.height, available), roomAbove = anchor.y - gap - import_tokens23.tokens.extensions.floating.viewportInset, roomBelow = window2.height - anchor.y - anchor.height - gap - import_tokens23.tokens.extensions.floating.viewportInset, above = placement.startsWith("top") ? !(flip && height > roomAbove && roomBelow > roomAbove) : flip && height > roomBelow && roomAbove > roomBelow;
3824
+ const available = Math.min(maxHeight != null ? maxHeight : field ? import_tokens23.tokens.extensions.menu.listMaxHeight : import_tokens23.tokens.extensions.floating.maxHeight, window2.height - 2 * import_tokens23.tokens.extensions.floating.viewportInset), height = Math.min(panel.height + (header != null ? headerHeight : 0), available), roomAbove = anchor.y - gap - import_tokens23.tokens.extensions.floating.viewportInset, roomBelow = window2.height - anchor.y - anchor.height - gap - import_tokens23.tokens.extensions.floating.viewportInset, above = placement.startsWith("top") ? !(flip && height > roomAbove && roomBelow > roomAbove) : flip && height > roomBelow && roomAbove > roomBelow;
3777
3825
  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;
3778
3826
  if (placement === "left" || placement === "right") {
3779
3827
  left = placement === "left" ? anchor.x - width - gap : anchor.x + anchor.width + gap;
@@ -3857,7 +3905,7 @@ function FloatingPanel({
3857
3905
  }
3858
3906
  }
3859
3907
  ),
3860
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3908
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
3861
3909
  import_react_native27.Animated.View,
3862
3910
  {
3863
3911
  ref: panelRef,
@@ -3868,7 +3916,7 @@ function FloatingPanel({
3868
3916
  pointerEvents: open && anchored && sized ? "auto" : "none",
3869
3917
  accessibilityElementsHidden: !open || !anchored || !sized,
3870
3918
  ...!open || !anchored || !sized ? { inert: true } : {},
3871
- accessibilityViewIsModal: true,
3919
+ accessibilityViewIsModal: !nonModal,
3872
3920
  accessibilityLabel: ariaLabel,
3873
3921
  style: {
3874
3922
  opacity: anchored && sized ? motion.opacity : 0,
@@ -3890,26 +3938,42 @@ function FloatingPanel({
3890
3938
  boxShadow: (tooltip ? import_tokens23.tokens.extensions.tooltip.elevation : popover ? import_tokens23.tokens.extensions.popover.elevation : import_tokens23.tokens.extensions.menu.elevation).map(({ colorRole, ...layer2 }) => ({ ...layer2, color: colors[colorRole] })),
3891
3939
  overflow: "hidden"
3892
3940
  },
3893
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3894
- import_react_native28.ScrollView,
3895
- {
3896
- ref: scroll,
3897
- onLayout: (event) => {
3898
- viewport.current = event.nativeEvent.layout.height;
3899
- },
3900
- keyboardShouldPersistTaps: "handled",
3901
- style: { maxHeight: available },
3902
- contentContainerStyle: { padding: menu || field ? import_tokens23.tokens.extensions.menu.padding : 0 },
3903
- onContentSizeChange: (width2, height2) => {
3904
- if (contentSizedMenu) return;
3905
- setSized(true);
3906
- setPanel(
3907
- (previous) => previous.width === width2 && previous.height === height2 ? previous : { width: width2, height: height2 }
3908
- );
3909
- },
3910
- children
3911
- }
3912
- )
3941
+ children: [
3942
+ header != null && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3943
+ import_react_native28.View,
3944
+ {
3945
+ onLayout: ({ nativeEvent: { layout } }) => setHeaderHeight(layout.height),
3946
+ style: {
3947
+ paddingTop: import_tokens23.tokens.extensions.popover.padding,
3948
+ paddingHorizontal: import_tokens23.tokens.extensions.popover.padding,
3949
+ paddingBottom: import_tokens23.tokens.extensions.popover.headerPaddingBottom,
3950
+ borderBottomWidth: import_tokens23.tokens.border.defaultWidth,
3951
+ borderBottomColor: colors.divider
3952
+ },
3953
+ children: header
3954
+ }
3955
+ ),
3956
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3957
+ import_react_native28.ScrollView,
3958
+ {
3959
+ ref: scroll,
3960
+ onLayout: (event) => {
3961
+ viewport.current = event.nativeEvent.layout.height;
3962
+ },
3963
+ keyboardShouldPersistTaps: "handled",
3964
+ style: { maxHeight: available - (header != null ? headerHeight : 0), flexShrink: 1 },
3965
+ contentContainerStyle: { padding: menu || field ? import_tokens23.tokens.extensions.menu.padding : 0 },
3966
+ onContentSizeChange: (width2, height2) => {
3967
+ if (contentSizedMenu) return;
3968
+ setSized(true);
3969
+ setPanel(
3970
+ (previous) => previous.width === width2 && previous.height === height2 ? previous : { width: width2, height: height2 }
3971
+ );
3972
+ },
3973
+ children
3974
+ }
3975
+ )
3976
+ ]
3913
3977
  }
3914
3978
  )
3915
3979
  ]
@@ -3935,9 +3999,12 @@ function KjunPopover({
3935
3999
  focusOnOpen = false,
3936
4000
  ariaLabel,
3937
4001
  placement = "bottom",
4002
+ header,
4003
+ modal = true,
3938
4004
  trigger,
3939
4005
  children
3940
4006
  }) {
4007
+ const { colors } = useKjunStyles();
3941
4008
  const [internal, setInternal] = (0, import_react35.useState)(false), open = controlled != null ? controlled : internal, ref = (0, import_react35.useRef)(null);
3942
4009
  const change = (v) => {
3943
4010
  setInternal(v);
@@ -3966,6 +4033,8 @@ function KjunPopover({
3966
4033
  popover: true,
3967
4034
  focusOnOpen,
3968
4035
  ariaLabel,
4036
+ header: header != null ? content(header, { ...typeStyle("cardTitle"), color: colors.text }) : void 0,
4037
+ nonModal: !modal,
3969
4038
  children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_native29.View, { style: { padding: noPadding ? 0 : import_tokens24.tokens.extensions.popover.padding, gap: noPadding ? 0 : import_tokens24.tokens.extensions.popover.gap }, children: content(
3970
4039
  typeof children === "function" ? children(() => change(false)) : children,
3971
4040
  import_react_native29.Platform.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : void 0
@@ -3980,9 +4049,18 @@ function KjunTooltip({
3980
4049
  delay = 0,
3981
4050
  children,
3982
4051
  onPress,
3983
- disabled
4052
+ disabled,
4053
+ open: controlled,
4054
+ onOpenChange
3984
4055
  }) {
3985
- const { colors } = useKjunStyles(), [open, setOpen] = (0, import_react35.useState)(false), ref = (0, import_react35.useRef)(null), timer = (0, import_react35.useRef)(void 0);
4056
+ const { colors } = useKjunStyles(), [internal, setInternal] = (0, import_react35.useState)(false), open = controlled != null ? controlled : internal, latest = (0, import_react35.useRef)({ open, onOpenChange }), setOpen = (value) => {
4057
+ var _a, _b;
4058
+ if (latest.current.open === value) return;
4059
+ latest.current.open = value;
4060
+ setInternal(value);
4061
+ (_b = (_a = latest.current).onOpenChange) == null ? void 0 : _b.call(_a, value);
4062
+ }, ref = (0, import_react35.useRef)(null), timer = (0, import_react35.useRef)(void 0);
4063
+ latest.current = { open, onOpenChange };
3986
4064
  const scopeActive = useActiveLayerScope();
3987
4065
  const hide = () => {
3988
4066
  clearTimeout(timer.current);
@@ -7233,7 +7311,7 @@ function TableToolbar({
7233
7311
  selectable && selected.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
7234
7312
  import_react_native45.View,
7235
7313
  {
7236
- accessibilityRole: "toolbar",
7314
+ role: "group",
7237
7315
  accessibilityLabel: "\uC120\uD0DD \uC791\uC5C5",
7238
7316
  style: [toolbarStyle, { backgroundColor: colors.selectedBg }],
7239
7317
  children: [
@@ -9573,7 +9651,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9573
9651
  const { colors, fontFamily } = useKjunStyles();
9574
9652
  const field = useKjunField();
9575
9653
  const invalid = error || !!(field == null ? void 0 : field.error);
9576
- const labelledBy = !ariaLabel && (field == null ? void 0 : field.label) ? field.id + "-label" : void 0;
9654
+ const labelledBy = !ariaLabel && (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0;
9577
9655
  const geometry2 = import_tokens60.tokens.extensions.compound;
9578
9656
  const buttonStyle = { width: import_tokens60.tokens.input[size].height, paddingHorizontal: 0 };
9579
9657
  const outer = import_tokens60.tokens.input[size].radius;
@@ -9681,7 +9759,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9681
9759
  nativeID: id || (field == null ? void 0 : field.id),
9682
9760
  accessibilityLabel: ariaLabel || (field == null ? void 0 : field.label) || "\uC218\uB7C9",
9683
9761
  accessibilityLabelledBy: labelledBy,
9684
- accessibilityHint: (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint),
9762
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, field == null ? void 0 : field.error),
9685
9763
  role: "spinbutton",
9686
9764
  accessibilityValue: { min, max, now: value },
9687
9765
  accessibilityState: { disabled: blocked },