@kjun-ui/native 0.9.0 → 0.11.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/styles.css CHANGED
@@ -250,6 +250,7 @@
250
250
  --_kjun-extension-popover-radius: 12px;
251
251
  --_kjun-extension-popover-padding: 16px;
252
252
  --_kjun-extension-popover-gap: 12px;
253
+ --_kjun-extension-popover-header-padding-bottom: 12px;
253
254
  --_kjun-extension-state-title-size: 1.25rem;
254
255
  --_kjun-extension-state-description-size: 0.75rem;
255
256
  --_kjun-extension-state-padding-sm: 24px;
@@ -416,6 +417,7 @@
416
417
  --_kjun-extension-drawer-bottom-radius: 16px;
417
418
  --_kjun-extension-drawer-radius: 0px;
418
419
  --_kjun-extension-drawer-close-radius: 8px;
420
+ --_kjun-extension-drawer-sheet-gap: 48px;
419
421
  --_kjun-extension-drawer-width: 400px;
420
422
  --_kjun-extension-drawer-close-size: 32px;
421
423
  --_kjun-extension-drawer-close-icon-size: 16px;
@@ -946,28 +948,30 @@
946
948
  /* Baseline for kjun-owned markup only: an element with a class that starts with kjun- (kjun- is reserved;
947
949
  my-kjun-logo is app markup). Component roots carry a kjun- class (Vue adds kjun-root, the Vue layer host is kjun-layers);
948
950
  portal roots pair kjun-scope with a kjun- class. The bare Provider root (kjun-scope) is excluded, so app markup inside the Provider keeps its own styles.
949
- Slot content placed inside a kjun component follows this baseline; style it with classes. */
951
+ Slot content placed inside a kjun component follows this baseline; style it with classes.
952
+ App markup under [data-kjun-content] opts out of the descendant resets, while kjun elements inside it get them back;
953
+ both branches keep the same specificity as before (the exclusion sits in :where). */
950
954
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]),
951
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) *,
955
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) *:not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) *,
952
956
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"])::before,
953
957
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"])::after,
954
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) *::before,
955
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) *::after {
958
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) *:not(:where([data-kjun-content] *))::before, .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) *::before,
959
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) *:not(:where([data-kjun-content] *))::after, .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) *::after {
956
960
  box-sizing: border-box;
957
961
  border-width: 0;
958
962
  border-style: solid;
959
963
  border-color: var(--_kjun-color-border);
960
964
  }
961
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(h1, h2, h3, h4, h5, h6, p, blockquote, dl, dd, figure, pre),
965
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(h1, h2, h3, h4, h5, h6, p, blockquote, dl, dd, figure, pre):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(h1, h2, h3, h4, h5, h6, p, blockquote, dl, dd, figure, pre),
962
966
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(h1, h2, h3, h4, h5, h6, p, blockquote, dl, dd, figure, pre) {
963
967
  margin: 0;
964
968
  }
965
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(h1, h2, h3, h4, h5, h6),
969
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(h1, h2, h3, h4, h5, h6):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(h1, h2, h3, h4, h5, h6),
966
970
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(h1, h2, h3, h4, h5, h6) {
967
971
  font-size: inherit;
968
972
  font-weight: inherit;
969
973
  }
970
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(button, input, optgroup, select, textarea),
974
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(button, input, optgroup, select, textarea):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(button, input, optgroup, select, textarea),
971
975
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(button, input, optgroup, select, textarea) {
972
976
  font: inherit;
973
977
  letter-spacing: inherit;
@@ -975,33 +979,33 @@
975
979
  margin: 0;
976
980
  padding: 0;
977
981
  }
978
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(button, select),
982
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(button, select):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(button, select),
979
983
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(button, select) { text-transform: none; }
980
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(button, [type="button"], [type="reset"], [type="submit"]),
984
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(button, [type="button"], [type="reset"], [type="submit"]):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(button, [type="button"], [type="reset"], [type="submit"]),
981
985
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(button, [type="button"], [type="reset"], [type="submit"]) {
982
986
  -webkit-appearance: button;
983
987
  background-color: transparent;
984
988
  background-image: none;
985
989
  }
986
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(button, [role="button"]),
990
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(button, [role="button"]):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(button, [role="button"]),
987
991
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(button, [role="button"]) { cursor: pointer; }
988
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(:disabled),
992
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(:disabled):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(:disabled),
989
993
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(:disabled) { cursor: default; }
990
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(a),
994
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(a):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(a),
991
995
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(a) { color: inherit; text-decoration: inherit; }
992
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(ol, ul, menu),
996
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(ol, ul, menu):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(ol, ul, menu),
993
997
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(ol, ul, menu) { list-style: none; margin: 0; padding: 0; }
994
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(table),
998
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(table):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(table),
995
999
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(table) { text-indent: 0; border-color: inherit; border-collapse: collapse; }
996
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(fieldset, legend),
1000
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(fieldset, legend):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(fieldset, legend),
997
1001
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(fieldset, legend) { margin: 0; padding: 0; }
998
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(textarea),
1002
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(textarea):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(textarea),
999
1003
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(textarea) { resize: vertical; }
1000
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(input, textarea)::placeholder,
1004
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(input, textarea):not(:where([data-kjun-content] *))::placeholder, .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(input, textarea)::placeholder,
1001
1005
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(input, textarea)::placeholder { opacity: 1; }
1002
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(img, video),
1006
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(img, video):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(img, video),
1003
1007
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(img, video) { max-width: 100%; height: auto; }
1004
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where([hidden]:not([hidden="until-found"])),
1008
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where([hidden]:not([hidden="until-found"])):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where([hidden]:not([hidden="until-found"])),
1005
1009
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where([hidden]:not([hidden="until-found"])) { display: none; }
1006
1010
  /* Last so it wins over the element resets above (e.g. font: inherit on a root <button>).
1007
1011
  The outermost kjun element takes the scope's text style; nested kjun elements inherit it.
@@ -1770,6 +1774,9 @@
1770
1774
  justify-content: flex-end;
1771
1775
  gap: var(--_kjun-geometry-modal-actions-gap);
1772
1776
  }
1777
+ /* wrap: the inline height becomes a minimum (set by the component) and the label may break lines. */
1778
+ .kjun-button[data-wrap] { flex-shrink: 1; max-width: 100%; white-space: normal; overflow-wrap: anywhere; text-align: center; }
1779
+ .kjun-button[data-wrap] > .kjun-button-label { min-width: 0; }
1773
1780
  .kjun-form-actions > .kjun-button {
1774
1781
  --_kjun-action-height: var(--_kjun-geometry-button-heights-lg);
1775
1782
  height: auto !important;
@@ -2424,6 +2431,11 @@
2424
2431
  font-weight:var(--_kjun-type-body-weight); letter-spacing:var(--_kjun-type-body-tracking);
2425
2432
  }
2426
2433
  .kjun-popover-dialog { outline:none; }
2434
+ /* With a header only the body scrolls: the panel and dialog become clipping flex columns. */
2435
+ .kjun-popover-has-header { display:flex; flex-direction:column; overflow:hidden; }
2436
+ .kjun-popover-has-header .kjun-popover-dialog { display:flex; flex-direction:column; flex:1 1 auto; min-height:0; }
2437
+ .kjun-popover-header { flex:none; padding:var(--_kjun-extension-popover-padding) var(--_kjun-extension-popover-padding) var(--_kjun-extension-popover-header-padding-bottom); border-bottom:var(--_kjun-border-default-width) solid var(--_kjun-color-divider); font-size:var(--_kjun-type-card-title-size); line-height:var(--_kjun-type-card-title-line); font-weight:var(--_kjun-type-card-title-weight); letter-spacing:var(--_kjun-type-card-title-tracking); word-break:keep-all; overflow-wrap:anywhere; }
2438
+ .kjun-popover-body { flex:1 1 auto; min-height:0; overflow-y:auto; }
2427
2439
  .kjun-popover-content { display:flex; flex-direction:column; gap:var(--_kjun-extension-popover-gap); padding:var(--_kjun-extension-popover-padding); min-width:0; word-break:keep-all; overflow-wrap:anywhere; }
2428
2440
  .kjun-popover-content[data-no-padding="true"] { padding:0; gap:0; }
2429
2441
  .kjun-tooltip {
@@ -2548,6 +2560,19 @@
2548
2560
  flex-direction: column;
2549
2561
  outline: none;
2550
2562
  }
2563
+ /* Safe areas arrive as --_kjun-drawer-safe-top/bottom on the overlay; the panel surface extends under them. */
2564
+ .kjun-drawer-overlay:not([data-position="bottom"]) .kjun-drawer-dialog { padding-top: var(--_kjun-drawer-safe-top); }
2565
+ .kjun-drawer-overlay:not([data-position="top"]) .kjun-drawer-dialog { padding-bottom: var(--_kjun-drawer-safe-bottom); }
2566
+ /* Top and bottom sheets stop short of the opposite edge (its safe area plus the sheet gap); only the body scrolls. */
2567
+ .kjun-drawer-overlay[data-position="bottom"] {
2568
+ --_kjun-drawer-sheet-max: calc(100dvh - var(--_kjun-drawer-safe-top) - var(--_kjun-extension-drawer-sheet-gap));
2569
+ }
2570
+ .kjun-drawer-overlay[data-position="top"] {
2571
+ --_kjun-drawer-sheet-max: calc(100dvh - var(--_kjun-drawer-safe-bottom) - var(--_kjun-extension-drawer-sheet-gap));
2572
+ }
2573
+ .kjun-drawer-overlay:is([data-position="top"], [data-position="bottom"]) :is(.kjun-drawer, .kjun-drawer-dialog) {
2574
+ max-height: var(--_kjun-drawer-max-height, var(--_kjun-drawer-sheet-max));
2575
+ }
2551
2576
  .kjun-drawer-header {
2552
2577
  display: flex;
2553
2578
  align-items: center;
@@ -4382,6 +4407,8 @@
4382
4407
  .kjun-kpi-row__value {
4383
4408
  white-space: normal;
4384
4409
  overflow-wrap: anywhere;
4410
+ /* Korean labels and values wrap at spaces, not between syllables; anywhere stays the last resort. */
4411
+ word-break: keep-all;
4385
4412
  }
4386
4413
  .kjun-kpi-row--mobile-summary
4387
4414
  .kjun-kpi-row__item--secondary
@@ -14,6 +14,8 @@ export interface KjunButtonProps extends Omit<PressableProps, "children" | "styl
14
14
  size?: ButtonSize;
15
15
  loading?: boolean;
16
16
  block?: boolean;
17
+ /** Long labels wrap: the size's height becomes a minimum and the button grows instead of overflowing. */
18
+ wrap?: boolean;
17
19
  prefixIcon?: string;
18
20
  suffixIcon?: string;
19
21
  prefixIconFilled?: boolean;
@@ -14,6 +14,8 @@ export interface KjunButtonProps extends Omit<PressableProps, "children" | "styl
14
14
  size?: ButtonSize;
15
15
  loading?: boolean;
16
16
  block?: boolean;
17
+ /** Long labels wrap: the size's height becomes a minimum and the button grows instead of overflowing. */
18
+ wrap?: boolean;
17
19
  prefixIcon?: string;
18
20
  suffixIcon?: string;
19
21
  prefixIconFilled?: boolean;
@@ -1,3 +1,4 @@
1
+ import { type AlertLive } from "../../../shared/package-runtime/alert-live.cjs";
1
2
  import { type ReactNode } from "react";
2
3
  import { type StyleProp, type ViewStyle } from "react-native";
3
4
  export type DisplaySize = "xs" | "sm" | "md" | "lg" | "xl";
@@ -32,12 +33,14 @@ export interface KjunAlertProps {
32
33
  size?: "sm" | "md" | "lg";
33
34
  title?: ReactNode;
34
35
  closable?: boolean;
36
+ /** How screen readers announce it: assertive (role alert), polite (status) or off; defaults to alert for danger, status otherwise. */
37
+ live?: AlertLive;
35
38
  children?: ReactNode;
36
39
  /** Buttons placed under the message; unsized KjunButtons follow the alert size. */
37
40
  actions?: ReactNode;
38
41
  onClose?: () => void;
39
42
  }
40
- export declare function KjunAlert({ type, variant, size, title, closable, children, actions, onClose, }: KjunAlertProps): import("react/jsx-runtime").JSX.Element | null;
43
+ export declare function KjunAlert({ type, variant, size, title, closable, live, children, actions, onClose, }: KjunAlertProps): import("react/jsx-runtime").JSX.Element | null;
41
44
  export interface KjunProgressProps {
42
45
  value?: number;
43
46
  max?: number;
@@ -1,3 +1,4 @@
1
+ import { type AlertLive } from "../../../shared/package-runtime/alert-live.js";
1
2
  import { type ReactNode } from "react";
2
3
  import { type StyleProp, type ViewStyle } from "react-native";
3
4
  export type DisplaySize = "xs" | "sm" | "md" | "lg" | "xl";
@@ -32,12 +33,14 @@ export interface KjunAlertProps {
32
33
  size?: "sm" | "md" | "lg";
33
34
  title?: ReactNode;
34
35
  closable?: boolean;
36
+ /** How screen readers announce it: assertive (role alert), polite (status) or off; defaults to alert for danger, status otherwise. */
37
+ live?: AlertLive;
35
38
  children?: ReactNode;
36
39
  /** Buttons placed under the message; unsized KjunButtons follow the alert size. */
37
40
  actions?: ReactNode;
38
41
  onClose?: () => void;
39
42
  }
40
- export declare function KjunAlert({ type, variant, size, title, closable, children, actions, onClose, }: KjunAlertProps): import("react/jsx-runtime").JSX.Element | null;
43
+ export declare function KjunAlert({ type, variant, size, title, closable, live, children, actions, onClose, }: KjunAlertProps): import("react/jsx-runtime").JSX.Element | null;
41
44
  export interface KjunProgressProps {
42
45
  value?: number;
43
46
  max?: number;
@@ -14,5 +14,13 @@ export interface KjunDrawerProps {
14
14
  children: ReactNode;
15
15
  onClose?: () => void;
16
16
  ariaLabel?: string;
17
+ /** Top safe area, padded inside top and side drawers and kept clear above a bottom sheet. */
18
+ safeAreaTop?: number;
19
+ /** Bottom safe area, padded inside bottom and side drawers and kept clear below a top sheet. */
20
+ safeAreaBottom?: number;
21
+ /** Height limit for top and bottom drawers; by default they stop a sheet gap short of the opposite edge. */
22
+ maxHeight?: number;
23
+ /** Keeps the panel above the on-screen keyboard (devices only). */
24
+ avoidKeyboard?: boolean;
17
25
  }
18
- export declare function KjunDrawer({ open, onOpenChange, title, position, width, closable, closeOnOverlay, closeOnEsc, noPadding, header, footer, children, onClose, ariaLabel, }: KjunDrawerProps): import("react/jsx-runtime").JSX.Element;
26
+ export declare function KjunDrawer({ open, onOpenChange, title, position, width, closable, closeOnOverlay, closeOnEsc, noPadding, header, footer, children, onClose, ariaLabel, safeAreaTop, safeAreaBottom, maxHeight, avoidKeyboard, }: KjunDrawerProps): import("react/jsx-runtime").JSX.Element;
@@ -14,5 +14,13 @@ export interface KjunDrawerProps {
14
14
  children: ReactNode;
15
15
  onClose?: () => void;
16
16
  ariaLabel?: string;
17
+ /** Top safe area, padded inside top and side drawers and kept clear above a bottom sheet. */
18
+ safeAreaTop?: number;
19
+ /** Bottom safe area, padded inside bottom and side drawers and kept clear below a top sheet. */
20
+ safeAreaBottom?: number;
21
+ /** Height limit for top and bottom drawers; by default they stop a sheet gap short of the opposite edge. */
22
+ maxHeight?: number;
23
+ /** Keeps the panel above the on-screen keyboard (devices only). */
24
+ avoidKeyboard?: boolean;
17
25
  }
18
- export declare function KjunDrawer({ open, onOpenChange, title, position, width, closable, closeOnOverlay, closeOnEsc, noPadding, header, footer, children, onClose, ariaLabel, }: KjunDrawerProps): import("react/jsx-runtime").JSX.Element;
26
+ export declare function KjunDrawer({ open, onOpenChange, title, position, width, closable, closeOnOverlay, closeOnEsc, noPadding, header, footer, children, onClose, ariaLabel, safeAreaTop, safeAreaBottom, maxHeight, avoidKeyboard, }: KjunDrawerProps): import("react/jsx-runtime").JSX.Element;
@@ -1,7 +1,10 @@
1
1
  import { type KjunFeedback } from "@kjun-ui/tokens";
2
2
  import { type ReactNode } from "react";
3
+ import { type StyleProp, type ViewStyle } from "react-native";
3
4
  export declare function useKjunFeedback(): KjunFeedback;
4
- export declare function KjunFeedbackProvider({ children }: {
5
+ export interface KjunFeedbackProviderProps {
5
6
  children: ReactNode;
6
- }): import("react/jsx-runtime").JSX.Element;
7
+ style?: StyleProp<ViewStyle>;
8
+ }
9
+ export declare function KjunFeedbackProvider({ children, style }: KjunFeedbackProviderProps): import("react/jsx-runtime").JSX.Element;
7
10
  export type { ConfirmOptions, KjunFeedback, PromptOptions, ToastOptions } from "@kjun-ui/tokens";
@@ -1,7 +1,10 @@
1
1
  import { type KjunFeedback } from "@kjun-ui/tokens";
2
2
  import { type ReactNode } from "react";
3
+ import { type StyleProp, type ViewStyle } from "react-native";
3
4
  export declare function useKjunFeedback(): KjunFeedback;
4
- export declare function KjunFeedbackProvider({ children }: {
5
+ export interface KjunFeedbackProviderProps {
5
6
  children: ReactNode;
6
- }): import("react/jsx-runtime").JSX.Element;
7
+ style?: StyleProp<ViewStyle>;
8
+ }
9
+ export declare function KjunFeedbackProvider({ children, style }: KjunFeedbackProviderProps): import("react/jsx-runtime").JSX.Element;
7
10
  export type { ConfirmOptions, KjunFeedback, PromptOptions, ToastOptions } from "@kjun-ui/tokens";
@@ -6,7 +6,7 @@ export declare function LayerTrigger({ trigger, onPress, disabled, }: {
6
6
  disabled?: boolean;
7
7
  }): import("react/jsx-runtime").JSX.Element;
8
8
  export type Placement = "top" | "bottom" | "left" | "right" | "bottom-start" | "bottom-end" | "top-start" | "top-end";
9
- export declare function FloatingPanel({ open, onOpenChange, triggerRef, children, placement, matchTriggerWidth, maxHeight, ariaLabel, menu, field, tooltip, popover, flip, minimumWidth, focusOnOpen, reveal, }: {
9
+ export declare function FloatingPanel({ open, onOpenChange, triggerRef, children, placement, matchTriggerWidth, maxHeight, ariaLabel, menu, field, tooltip, popover, flip, minimumWidth, focusOnOpen, reveal, header, nonModal, }: {
10
10
  open: boolean;
11
11
  onOpenChange: (v: boolean) => void;
12
12
  triggerRef: RefObject<View | null>;
@@ -28,4 +28,8 @@ export declare function FloatingPanel({ open, onOpenChange, triggerRef, children
28
28
  y: number;
29
29
  height: number;
30
30
  } | null>;
31
+ /** Popover header drawn above the scroll view, so only the body scrolls. */
32
+ header?: ReactNode;
33
+ /** Popover modal={false}: screen readers are not confined to the panel. */
34
+ nonModal?: boolean;
31
35
  }): import("react/jsx-runtime").JSX.Element | null;
@@ -6,7 +6,7 @@ export declare function LayerTrigger({ trigger, onPress, disabled, }: {
6
6
  disabled?: boolean;
7
7
  }): import("react/jsx-runtime").JSX.Element;
8
8
  export type Placement = "top" | "bottom" | "left" | "right" | "bottom-start" | "bottom-end" | "top-start" | "top-end";
9
- export declare function FloatingPanel({ open, onOpenChange, triggerRef, children, placement, matchTriggerWidth, maxHeight, ariaLabel, menu, field, tooltip, popover, flip, minimumWidth, focusOnOpen, reveal, }: {
9
+ export declare function FloatingPanel({ open, onOpenChange, triggerRef, children, placement, matchTriggerWidth, maxHeight, ariaLabel, menu, field, tooltip, popover, flip, minimumWidth, focusOnOpen, reveal, header, nonModal, }: {
10
10
  open: boolean;
11
11
  onOpenChange: (v: boolean) => void;
12
12
  triggerRef: RefObject<View | null>;
@@ -28,4 +28,8 @@ export declare function FloatingPanel({ open, onOpenChange, triggerRef, children
28
28
  y: number;
29
29
  height: number;
30
30
  } | null>;
31
+ /** Popover header drawn above the scroll view, so only the body scrolls. */
32
+ header?: ReactNode;
33
+ /** Popover modal={false}: screen readers are not confined to the panel. */
34
+ nonModal?: boolean;
31
35
  }): import("react/jsx-runtime").JSX.Element | null;
@@ -8,6 +8,7 @@ interface FieldValue {
8
8
  hint?: string;
9
9
  required?: boolean;
10
10
  describedBy?: string;
11
+ labelHidden?: boolean;
11
12
  }
12
13
  export declare const FieldContext: import("react").Context<FieldValue | null>;
13
14
  export declare const useKjunField: () => FieldValue | null;
@@ -17,10 +18,12 @@ export interface KjunFormGroupProps {
17
18
  required?: boolean;
18
19
  error?: string;
19
20
  hint?: string;
21
+ /** Hides the label visually while it still names the field. */
22
+ labelHidden?: boolean;
20
23
  children: ReactNode;
21
24
  style?: StyleProp<ViewStyle>;
22
25
  }
23
- export declare function KjunFormGroup({ label, id, required, error, hint, children, style, }: KjunFormGroupProps): import("react/jsx-runtime").JSX.Element;
26
+ export declare function KjunFormGroup({ label, id, required, error, hint, labelHidden, children, style, }: KjunFormGroupProps): import("react/jsx-runtime").JSX.Element;
24
27
  export interface KjunInputProps extends Omit<TextInputProps, "value" | "onChange"> {
25
28
  value: string | number;
26
29
  size?: InputSize;
@@ -8,6 +8,7 @@ interface FieldValue {
8
8
  hint?: string;
9
9
  required?: boolean;
10
10
  describedBy?: string;
11
+ labelHidden?: boolean;
11
12
  }
12
13
  export declare const FieldContext: import("react").Context<FieldValue | null>;
13
14
  export declare const useKjunField: () => FieldValue | null;
@@ -17,10 +18,12 @@ export interface KjunFormGroupProps {
17
18
  required?: boolean;
18
19
  error?: string;
19
20
  hint?: string;
21
+ /** Hides the label visually while it still names the field. */
22
+ labelHidden?: boolean;
20
23
  children: ReactNode;
21
24
  style?: StyleProp<ViewStyle>;
22
25
  }
23
- export declare function KjunFormGroup({ label, id, required, error, hint, children, style, }: KjunFormGroupProps): import("react/jsx-runtime").JSX.Element;
26
+ export declare function KjunFormGroup({ label, id, required, error, hint, labelHidden, children, style, }: KjunFormGroupProps): import("react/jsx-runtime").JSX.Element;
24
27
  export interface KjunInputProps extends Omit<TextInputProps, "value" | "onChange"> {
25
28
  value: string | number;
26
29
  size?: InputSize;
@@ -11,14 +11,21 @@ export interface KjunPopoverProps {
11
11
  focusOnOpen?: boolean;
12
12
  ariaLabel?: string;
13
13
  placement?: "top" | "bottom" | "left" | "right";
14
+ /** Fixed header above the body; only the body scrolls and maxHeight covers both. */
15
+ header?: ReactNode;
16
+ /** false stops confining screen readers to the panel; Native has no Tab order to release. */
17
+ modal?: boolean;
14
18
  trigger: ReactNode | ((open: boolean) => ReactNode);
15
19
  children: ReactNode | ((close: () => void) => ReactNode);
16
20
  }
17
- export declare function KjunPopover({ noPadding, open: controlled, onOpenChange, manualTrigger, matchTriggerWidth, maxHeight, flip, focusOnOpen, ariaLabel, placement, trigger, children, }: KjunPopoverProps): import("react/jsx-runtime").JSX.Element;
21
+ export declare function KjunPopover({ noPadding, open: controlled, onOpenChange, manualTrigger, matchTriggerWidth, maxHeight, flip, focusOnOpen, ariaLabel, placement, header, modal, trigger, children, }: KjunPopoverProps): import("react/jsx-runtime").JSX.Element;
18
22
  export interface KjunTooltipProps extends Pick<PressableProps, "onPress" | "disabled"> {
19
23
  content?: string;
20
24
  placement?: "top" | "bottom" | "left" | "right";
21
25
  delay?: number;
26
+ /** Controlled visibility; omit to let hover and focus drive it. */
27
+ open?: boolean;
28
+ onOpenChange?: (open: boolean) => void;
22
29
  children: ReactNode;
23
30
  }
24
- export declare function KjunTooltip({ content: message, placement, delay, children, onPress, disabled, }: KjunTooltipProps): import("react/jsx-runtime").JSX.Element;
31
+ export declare function KjunTooltip({ content: message, placement, delay, children, onPress, disabled, open: controlled, onOpenChange, }: KjunTooltipProps): import("react/jsx-runtime").JSX.Element;
@@ -11,14 +11,21 @@ export interface KjunPopoverProps {
11
11
  focusOnOpen?: boolean;
12
12
  ariaLabel?: string;
13
13
  placement?: "top" | "bottom" | "left" | "right";
14
+ /** Fixed header above the body; only the body scrolls and maxHeight covers both. */
15
+ header?: ReactNode;
16
+ /** false stops confining screen readers to the panel; Native has no Tab order to release. */
17
+ modal?: boolean;
14
18
  trigger: ReactNode | ((open: boolean) => ReactNode);
15
19
  children: ReactNode | ((close: () => void) => ReactNode);
16
20
  }
17
- export declare function KjunPopover({ noPadding, open: controlled, onOpenChange, manualTrigger, matchTriggerWidth, maxHeight, flip, focusOnOpen, ariaLabel, placement, trigger, children, }: KjunPopoverProps): import("react/jsx-runtime").JSX.Element;
21
+ export declare function KjunPopover({ noPadding, open: controlled, onOpenChange, manualTrigger, matchTriggerWidth, maxHeight, flip, focusOnOpen, ariaLabel, placement, header, modal, trigger, children, }: KjunPopoverProps): import("react/jsx-runtime").JSX.Element;
18
22
  export interface KjunTooltipProps extends Pick<PressableProps, "onPress" | "disabled"> {
19
23
  content?: string;
20
24
  placement?: "top" | "bottom" | "left" | "right";
21
25
  delay?: number;
26
+ /** Controlled visibility; omit to let hover and focus drive it. */
27
+ open?: boolean;
28
+ onOpenChange?: (open: boolean) => void;
22
29
  children: ReactNode;
23
30
  }
24
- export declare function KjunTooltip({ content: message, placement, delay, children, onPress, disabled, }: KjunTooltipProps): import("react/jsx-runtime").JSX.Element;
31
+ export declare function KjunTooltip({ content: message, placement, delay, children, onPress, disabled, open: controlled, onOpenChange, }: KjunTooltipProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,3 @@
1
+ export type AlertLive = "assertive" | "polite" | "off";
2
+ /** Alert announcement role: `live` overrides the tone default (danger interrupts, others are polite); off announces nothing. */
3
+ export declare function alertRole(tone: string, live?: AlertLive): "alert" | "status" | undefined;
@@ -0,0 +1,3 @@
1
+ export type AlertLive = "assertive" | "polite" | "off";
2
+ /** Alert announcement role: `live` overrides the tone default (danger interrupts, others are polite); off announces nothing. */
3
+ export declare function alertRole(tone: string, live?: AlertLive): "alert" | "status" | undefined;
@@ -0,0 +1,4 @@
1
+ /** Joins space-separated id lists, keeping each id once; empty input gives undefined so the attribute is omitted. */
2
+ export declare function mergeIds(...lists: (string | null | undefined)[]): string | undefined;
3
+ /** Joins hint and error text for platforms that announce one accessibility hint string. */
4
+ export declare function joinHints(...parts: (string | null | undefined)[]): string | undefined;
@@ -0,0 +1,4 @@
1
+ /** Joins space-separated id lists, keeping each id once; empty input gives undefined so the attribute is omitted. */
2
+ export declare function mergeIds(...lists: (string | null | undefined)[]): string | undefined;
3
+ /** Joins hint and error text for platforms that announce one accessibility hint string. */
4
+ export declare function joinHints(...parts: (string | null | undefined)[]): string | undefined;
@@ -0,0 +1,36 @@
1
+ export type DrawerPosition = "left" | "right" | "top" | "bottom";
2
+ export interface DrawerFrameInput {
3
+ position: DrawerPosition;
4
+ windowHeight: number;
5
+ safeAreaTop: number;
6
+ safeAreaBottom: number;
7
+ /** Space a top or bottom sheet leaves at the opposite edge. */
8
+ gap: number;
9
+ /** Height of the window the keyboard covers (0 when hidden). */
10
+ keyboard: number;
11
+ maxHeight?: number;
12
+ }
13
+ export interface DrawerFrame {
14
+ maxHeight: number;
15
+ height: number | undefined;
16
+ paddingTop: number;
17
+ paddingBottom: number;
18
+ lift: number;
19
+ }
20
+ /**
21
+ * Keyboard events a drawer listens to. Only iOS: React Native's Android Modal window is adjustResize,
22
+ * so the system already shrinks the modal and a measured root height covers it.
23
+ */
24
+ export declare function keyboardEvents(os: string): {
25
+ readonly show: "keyboardWillShow";
26
+ readonly hide: "keyboardWillHide";
27
+ } | null;
28
+ /** The part of the window the keyboard covers; 0 when the platform already resized the window. */
29
+ export declare function keyboardOverlap(windowHeight: number, keyboardScreenY: number): number;
30
+ export declare function drawerFrame({ position, windowHeight, safeAreaTop, safeAreaBottom, gap, keyboard, maxHeight }: DrawerFrameInput): DrawerFrame;
31
+ /** Web overlay variables read by the drawer CSS (tokens controls.css). */
32
+ export declare function drawerOverlayVars({ safeAreaTop, safeAreaBottom, maxHeight }: {
33
+ safeAreaTop?: number;
34
+ safeAreaBottom?: number;
35
+ maxHeight?: number;
36
+ }): Record<string, string>;
@@ -0,0 +1,36 @@
1
+ export type DrawerPosition = "left" | "right" | "top" | "bottom";
2
+ export interface DrawerFrameInput {
3
+ position: DrawerPosition;
4
+ windowHeight: number;
5
+ safeAreaTop: number;
6
+ safeAreaBottom: number;
7
+ /** Space a top or bottom sheet leaves at the opposite edge. */
8
+ gap: number;
9
+ /** Height of the window the keyboard covers (0 when hidden). */
10
+ keyboard: number;
11
+ maxHeight?: number;
12
+ }
13
+ export interface DrawerFrame {
14
+ maxHeight: number;
15
+ height: number | undefined;
16
+ paddingTop: number;
17
+ paddingBottom: number;
18
+ lift: number;
19
+ }
20
+ /**
21
+ * Keyboard events a drawer listens to. Only iOS: React Native's Android Modal window is adjustResize,
22
+ * so the system already shrinks the modal and a measured root height covers it.
23
+ */
24
+ export declare function keyboardEvents(os: string): {
25
+ readonly show: "keyboardWillShow";
26
+ readonly hide: "keyboardWillHide";
27
+ } | null;
28
+ /** The part of the window the keyboard covers; 0 when the platform already resized the window. */
29
+ export declare function keyboardOverlap(windowHeight: number, keyboardScreenY: number): number;
30
+ export declare function drawerFrame({ position, windowHeight, safeAreaTop, safeAreaBottom, gap, keyboard, maxHeight }: DrawerFrameInput): DrawerFrame;
31
+ /** Web overlay variables read by the drawer CSS (tokens controls.css). */
32
+ export declare function drawerOverlayVars({ safeAreaTop, safeAreaBottom, maxHeight }: {
33
+ safeAreaTop?: number;
34
+ safeAreaBottom?: number;
35
+ maxHeight?: number;
36
+ }): Record<string, string>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kjun-ui/native",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "license": "MIT",
5
5
  "homepage": "https://ui.kjun.dev",
6
6
  "repository": {
@@ -47,8 +47,8 @@
47
47
  "./package.json": "./package.json"
48
48
  },
49
49
  "dependencies": {
50
- "@kjun-ui/tokens": "0.9.0",
51
- "@kjun-ui/icons": "0.9.0"
50
+ "@kjun-ui/tokens": "0.11.0",
51
+ "@kjun-ui/icons": "0.11.0"
52
52
  },
53
53
  "peerDependencies": {
54
54
  "react": "^19.2.3",