@kjun-ui/native 0.13.0 → 0.15.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 (32) hide show
  1. package/dist/index.cjs +1038 -875
  2. package/dist/index.js +907 -741
  3. package/dist/index.web.cjs +1051 -888
  4. package/dist/index.web.js +913 -747
  5. package/dist/styles.css +73 -1
  6. package/dist/types/packages/native/src/card.d.cts +9 -2
  7. package/dist/types/packages/native/src/card.d.ts +9 -2
  8. package/dist/types/packages/native/src/collapse.d.cts +3 -1
  9. package/dist/types/packages/native/src/collapse.d.ts +3 -1
  10. package/dist/types/packages/native/src/display-primitives.d.cts +2 -1
  11. package/dist/types/packages/native/src/display-primitives.d.ts +2 -1
  12. package/dist/types/packages/native/src/drawer.d.cts +3 -1
  13. package/dist/types/packages/native/src/drawer.d.ts +3 -1
  14. package/dist/types/packages/native/src/feedback.d.cts +5 -1
  15. package/dist/types/packages/native/src/feedback.d.ts +5 -1
  16. package/dist/types/packages/native/src/layout-primitives.d.cts +5 -3
  17. package/dist/types/packages/native/src/layout-primitives.d.ts +5 -3
  18. package/dist/types/packages/native/src/list.d.cts +17 -2
  19. package/dist/types/packages/native/src/list.d.ts +17 -2
  20. package/dist/types/packages/native/src/media.d.cts +4 -2
  21. package/dist/types/packages/native/src/media.d.ts +4 -2
  22. package/dist/types/packages/native/src/modal.d.cts +3 -1
  23. package/dist/types/packages/native/src/modal.d.ts +3 -1
  24. package/dist/types/packages/native/src/navigation.d.cts +7 -1
  25. package/dist/types/packages/native/src/navigation.d.ts +7 -1
  26. package/dist/types/shared/package-runtime/layout-primitives.d.cts +1 -1
  27. package/dist/types/shared/package-runtime/layout-primitives.d.ts +1 -1
  28. package/dist/types/shared/package-runtime/progress-circle.d.cts +11 -0
  29. package/dist/types/shared/package-runtime/progress-circle.d.ts +11 -0
  30. package/dist/types/shared/package-runtime/use-after-close.d.cts +6 -0
  31. package/dist/types/shared/package-runtime/use-after-close.d.ts +6 -0
  32. package/package.json +3 -3
package/dist/styles.css CHANGED
@@ -654,6 +654,12 @@
654
654
  --_kjun-extension-progress-heights-sm: 4px;
655
655
  --_kjun-extension-progress-heights-md: 8px;
656
656
  --_kjun-extension-progress-heights-lg: 12px;
657
+ --_kjun-extension-progress-circle-sm-size: 24px;
658
+ --_kjun-extension-progress-circle-sm-stroke: 4px;
659
+ --_kjun-extension-progress-circle-md-size: 48px;
660
+ --_kjun-extension-progress-circle-md-stroke: 4px;
661
+ --_kjun-extension-progress-circle-lg-size: 64px;
662
+ --_kjun-extension-progress-circle-lg-stroke: 6px;
657
663
  --_kjun-extension-icon-toggle-compact-size: 24px;
658
664
  --_kjun-extension-icon-toggle-sizes-xs: 24px;
659
665
  --_kjun-extension-icon-toggle-sizes-sm: 24px;
@@ -2228,8 +2234,50 @@
2228
2234
  font-weight: var(--_kjun-type-caption-weight);
2229
2235
  letter-spacing: var(--_kjun-type-caption-tracking);
2230
2236
  }
2237
+ /* Circle progress: circle, then label and subLabel centred below it. */
2238
+ .kjun-progress[data-shape="circle"] {
2239
+ display: inline-flex;
2240
+ flex-direction: column;
2241
+ align-items: center;
2242
+ text-align: center;
2243
+ }
2244
+ .kjun-progress-circle {
2245
+ position: relative;
2246
+ display: flex;
2247
+ align-items: center;
2248
+ justify-content: center;
2249
+ flex-shrink: 0;
2250
+ }
2251
+ .kjun-progress-circle > svg { position: absolute; inset: 0; display: block; }
2252
+ .kjun-progress-circle-track { stroke: var(--_kjun-color-secondary); }
2253
+ .kjun-progress-circle-value { transition: stroke-dashoffset var(--_kjun-motion-progress) var(--_kjun-ease-out); }
2254
+ .kjun-progress-circle-percent {
2255
+ position: relative;
2256
+ font-family: var(--_kjun-font-numeric);
2257
+ font-variant-numeric: tabular-nums;
2258
+ color: var(--_kjun-color-text);
2259
+ font-size: var(--_kjun-type-meta-size);
2260
+ line-height: var(--_kjun-type-meta-line);
2261
+ font-weight: var(--_kjun-type-meta-weight);
2262
+ letter-spacing: var(--_kjun-type-meta-tracking);
2263
+ }
2264
+ .kjun-progress[data-size="lg"] .kjun-progress-circle-percent {
2265
+ font-size: var(--_kjun-type-number-md-size);
2266
+ line-height: var(--_kjun-type-number-md-line);
2267
+ font-weight: var(--_kjun-type-number-md-weight);
2268
+ letter-spacing: var(--_kjun-type-number-md-tracking);
2269
+ }
2270
+ .kjun-progress-circle-label {
2271
+ margin-top: var(--_kjun-geometry-dimension-value8);
2272
+ font-size: var(--_kjun-type-caption-size);
2273
+ color: var(--_kjun-color-text-secondary);
2274
+ line-height: var(--_kjun-type-caption-line);
2275
+ font-weight: var(--_kjun-type-caption-weight);
2276
+ letter-spacing: var(--_kjun-type-caption-tracking);
2277
+ }
2231
2278
  @media (prefers-reduced-motion: reduce) {
2232
2279
  .kjun-progress-track > div,
2280
+ .kjun-progress-circle-value,
2233
2281
  .kjun-card {
2234
2282
  transition: none;
2235
2283
  }
@@ -4595,6 +4643,13 @@
4595
4643
  .kjun-card[data-dividers="false"] > .kjun-card-header + .kjun-card-footer { padding-top: 0; }
4596
4644
  .kjun-card[data-dividers="true"] > .kjun-card-header:has(+ .kjun-card-body) { border-bottom: var(--_kjun-border-default-width) solid var(--_kjun-card-border); }
4597
4645
  .kjun-card[data-dividers="true"] > .kjun-card-footer:not(:first-child) { border-top: var(--_kjun-border-default-width) solid var(--_kjun-card-border); }
4646
+ /* A pressable card is one button: it lifts on hover, presses in on activation and shows the focus ring. */
4647
+ .kjun-card[data-pressable] { box-shadow: var(--_kjun-card-shadow); cursor: pointer; transition: box-shadow var(--_kjun-motion-control) var(--_kjun-ease-out), transform var(--_kjun-motion-control) var(--_kjun-ease-out); }
4648
+ .kjun-card[data-pressable]:hover:not([aria-disabled="true"]) { box-shadow: var(--_kjun-elevation-floating); }
4649
+ .kjun-card[data-pressable]:active:not([aria-disabled="true"]) { transform: scale(0.99); }
4650
+ .kjun-card[data-pressable]:focus-visible { outline: var(--_kjun-state-focus-width) solid var(--_kjun-color-focus-ring); outline-offset: var(--_kjun-state-focus-offset); }
4651
+ .kjun-card[data-pressable][aria-disabled="true"] { cursor: not-allowed; opacity: var(--_kjun-state-opacity-disabled); }
4652
+ @media (prefers-reduced-motion: reduce) { .kjun-card[data-pressable] { transition: none; } .kjun-card[data-pressable]:active { transform: none; } }
4598
4653
 
4599
4654
  .kjun-toast-stack {
4600
4655
  position: fixed;
@@ -4745,7 +4800,7 @@
4745
4800
 
4746
4801
  .kjun-list-section,.kjun-top-navigation,.kjun-bottom-navigation,.kjun-bottom-action-bar{min-width:0;max-width:100%;color:var(--_kjun-color-text);background:var(--_kjun-color-surface)}
4747
4802
  .kjun-list-heading{display:flex;align-items:center;justify-content:space-between;gap:var(--_kjun-extension-list-gap);padding:var(--_kjun-extension-list-padding)}.kjun-list-heading h3{font-size:var(--_kjun-type-card-title-size);margin:0;line-height:var(--_kjun-type-card-title-line);font-weight:var(--_kjun-type-card-title-weight);letter-spacing:var(--_kjun-type-card-title-tracking)}.kjun-list-heading p{margin:var(--_kjun-geometry-dimension-value4) 0 0;font-size:var(--_kjun-type-caption-size);color:var(--_kjun-color-text-secondary);overflow-wrap:anywhere;line-height:var(--_kjun-type-caption-line);font-weight:var(--_kjun-type-caption-weight);letter-spacing:var(--_kjun-type-caption-tracking)}
4748
- .kjun-list{list-style:none;padding:0;margin:0}.kjun-list-row{display:flex;align-items:center;gap:var(--_kjun-extension-list-gap);border-bottom:var(--_kjun-border-default-width) solid var(--_kjun-color-divider);min-width:0}.kjun-list-row:last-child{border-bottom:0}.kjun-list[data-divider=false]>.kjun-list-row{border-bottom:0}.kjun-list[data-density=compact] .kjun-list-main{min-height:var(--_kjun-extension-list-compact-minimum-height);padding-block:var(--_kjun-extension-list-compact-padding-y)}.kjun-list-main{display:flex;align-items:center;gap:var(--_kjun-extension-list-gap);flex:1;min-width:0;min-height:var(--_kjun-extension-list-minimum-height);padding:var(--_kjun-extension-list-padding);text-align:start;color:inherit;text-decoration:none;background:transparent;border:0;font:inherit}.kjun-list-main:is(button,a):hover{background:var(--_kjun-color-hover)}.kjun-list-body{flex:1;min-width:0}.kjun-list-title,.kjun-list-description{display:block;overflow-wrap:anywhere;line-height:1.5}.kjun-list-title{font-size:var(--_kjun-type-label-size);line-height:var(--_kjun-type-label-line);font-weight:var(--_kjun-type-label-weight);letter-spacing:var(--_kjun-type-label-tracking)}.kjun-list-description{font-size:var(--_kjun-type-caption-size);color:var(--_kjun-color-text-secondary);line-height:var(--_kjun-type-caption-line);font-weight:var(--_kjun-type-caption-weight);letter-spacing:var(--_kjun-type-caption-tracking)}.kjun-list-leading{flex-shrink:0}.kjun-list-trailing{overflow-wrap:anywhere;max-width:35%}.kjun-list-actions{display:flex;flex-wrap:wrap;gap:var(--_kjun-extension-list-actions-gap);padding-inline-end:var(--_kjun-extension-list-actions-padding);max-width:45%}
4803
+ .kjun-list{list-style:none;padding:0;margin:0}.kjun-list-row{display:flex;align-items:center;gap:var(--_kjun-extension-list-gap);border-bottom:var(--_kjun-border-default-width) solid var(--_kjun-color-divider);min-width:0}.kjun-list-row:last-child{border-bottom:0}.kjun-list[data-divider=false]>.kjun-list-row{border-bottom:0}.kjun-list[data-density=compact] .kjun-list-main{min-height:var(--_kjun-extension-list-compact-minimum-height);padding-block:var(--_kjun-extension-list-compact-padding-y)}.kjun-list-main{display:flex;align-items:center;gap:var(--_kjun-extension-list-gap);flex:1;min-width:0;min-height:var(--_kjun-extension-list-minimum-height);padding:var(--_kjun-extension-list-padding);text-align:start;color:inherit;text-decoration:none;background:transparent;border:0;font:inherit}.kjun-list-main:is(button,a):hover{background:var(--_kjun-color-hover)}.kjun-list-body{flex:1;min-width:0}.kjun-list-content{display:block;margin-top:var(--_kjun-extension-list-description-gap)}.kjun-list-title,.kjun-list-description{display:block;overflow-wrap:anywhere;line-height:1.5}.kjun-list-title{font-size:var(--_kjun-type-label-size);line-height:var(--_kjun-type-label-line);font-weight:var(--_kjun-type-label-weight);letter-spacing:var(--_kjun-type-label-tracking)}.kjun-list-description{font-size:var(--_kjun-type-caption-size);color:var(--_kjun-color-text-secondary);line-height:var(--_kjun-type-caption-line);font-weight:var(--_kjun-type-caption-weight);letter-spacing:var(--_kjun-type-caption-tracking)}.kjun-list-leading{flex-shrink:0}.kjun-list-trailing{overflow-wrap:anywhere;max-width:35%}.kjun-list-actions{display:flex;flex-wrap:wrap;gap:var(--_kjun-extension-list-actions-gap);padding-inline-end:var(--_kjun-extension-list-actions-padding);max-width:45%}
4749
4804
  /* A disabled row disables its actions too, so the row never reads as partly available. */
4750
4805
  .kjun-list-actions[data-disabled]{opacity:var(--_kjun-state-opacity-disabled)}
4751
4806
  .kjun-navigation-actions{display:flex;align-items:center;justify-content:flex-end;flex-wrap:wrap;gap:var(--_kjun-extension-navigation-actions-gap);min-width:0}
@@ -4754,6 +4809,8 @@
4754
4809
  .kjun-avatar .kjun-image-fallback{text-transform:uppercase;font-size:var(--_kjun-type-input-size);line-height:var(--_kjun-type-input-line)}
4755
4810
  .kjun-avatar[data-size=sm] .kjun-image-fallback{font-size:var(--_kjun-type-control-size);line-height:var(--_kjun-type-control-line)}
4756
4811
  .kjun-avatar[data-size=lg] .kjun-image-fallback{font-size:var(--_kjun-type-section-title-size);line-height:var(--_kjun-type-section-title-line)}.kjun-avatar[data-shape=square]{border-radius:var(--_kjun-geometry-radius-radius8)}
4812
+ /* App colors (color/textColor) arrive as private custom properties; without them the image roles stay. */
4813
+ .kjun-avatar .kjun-image{background:var(--_kjun-avatar-color,var(--_kjun-color-secondary))}.kjun-avatar .kjun-image-fallback{color:var(--_kjun-avatar-text-color,var(--_kjun-color-text-secondary))}
4757
4814
  .kjun-chip{--_kjun-chip-remove-icon:var(--_kjun-extension-chip-remove-icon-sizes-md);display:inline-flex;max-width:100%;align-items:center;gap:var(--_kjun-extension-chip-gap);min-height:var(--_kjun-extension-chip-md);padding:var(--_kjun-extension-chip-padding-y) var(--_kjun-extension-chip-padding-x);border-radius:var(--_kjun-extension-chip-radius);background:var(--_kjun-color-secondary);font-size:var(--_kjun-extension-chip-typography-md-font-size-px);line-height:var(--_kjun-extension-chip-typography-md-line-height-px);font-weight:var(--_kjun-extension-chip-typography-md-font-weight);letter-spacing:var(--_kjun-extension-chip-typography-md-letter-spacing-em)}
4758
4815
  .kjun-chip>span{overflow-wrap:anywhere;min-width:0}
4759
4816
  .kjun-chip[data-size=sm]{--_kjun-chip-remove-icon:var(--_kjun-extension-chip-remove-icon-sizes-sm);min-height:var(--_kjun-extension-chip-sm);font-size:var(--_kjun-extension-chip-typography-sm-font-size-px);line-height:var(--_kjun-extension-chip-typography-sm-line-height-px);font-weight:var(--_kjun-extension-chip-typography-sm-font-weight);letter-spacing:var(--_kjun-extension-chip-typography-sm-letter-spacing-em)}
@@ -4964,6 +5021,15 @@
4964
5021
  .kjun-fab:focus-visible{outline:var(--_kjun-state-focus-width) solid var(--_kjun-color-focus-ring);outline-offset:var(--_kjun-state-focus-offset)}
4965
5022
  .kjun-fab:disabled{opacity:var(--_kjun-state-opacity-disabled);cursor:not-allowed}
4966
5023
 
5024
+ /* 0.14.0: plain sections, destructive rows, decorative chevrons and non-row list items share the row inset. */
5025
+ .kjun-list-section[data-surface="false"]{background:transparent}
5026
+ .kjun-list-row[data-destructive] .kjun-list-title{color:var(--_kjun-color-danger)}
5027
+ .kjun-list-chevron{flex-shrink:0;color:var(--_kjun-color-text-tertiary)}
5028
+ .kjun-list-item{padding:var(--_kjun-extension-list-padding);border-bottom:var(--_kjun-border-default-width) solid var(--_kjun-color-divider);min-width:0}
5029
+ .kjun-list-item:last-child{border-bottom:0}
5030
+ .kjun-list[data-divider=false]>.kjun-list-item{border-bottom:0}
5031
+ .kjun-list[data-density=compact]>.kjun-list-item{padding-block:var(--_kjun-extension-list-compact-padding-y)}
5032
+
4967
5033
  /* Compound fields share input geometry; buttons retain their own size contract. */
4968
5034
  .kjun-quantity,.kjun-time-picker {
4969
5035
  --_kjun-compound-height: var(--_kjun-geometry-input-md-height);
@@ -5160,6 +5226,12 @@
5160
5226
  .kjun-text.kjun-text[data-tone="secondary"] { color: var(--_kjun-color-text-secondary); }
5161
5227
  .kjun-text.kjun-text[data-tone="tertiary"] { color: var(--_kjun-color-text-tertiary); }
5162
5228
  .kjun-text.kjun-text[data-tone="disabled"] { color: var(--_kjun-color-text-disabled); }
5229
+ .kjun-text.kjun-text[data-tone="brand"] { color: var(--_kjun-color-brand); }
5230
+ .kjun-text.kjun-text[data-tone="danger"] { color: var(--_kjun-color-danger); }
5231
+ .kjun-text.kjun-text[data-tone="success"] { color: var(--_kjun-color-success); }
5232
+ .kjun-text.kjun-text[data-tone="warning"] { color: var(--_kjun-color-warning); }
5233
+ /* inverse is text on the brand surface. */
5234
+ .kjun-text.kjun-text[data-tone="inverse"] { color: var(--_kjun-color-on-brand); }
5163
5235
  .kjun-text[data-align="start"] { text-align: start; }
5164
5236
  .kjun-text[data-align="center"] { text-align: center; }
5165
5237
  .kjun-text[data-align="end"] { text-align: end; }
@@ -1,6 +1,6 @@
1
1
  import { type CardElevation, type CardPadding, type CardRadius, type CardSurface } from "@kjun-ui/tokens";
2
2
  import { type ReactNode } from "react";
3
- import { type StyleProp, type ViewStyle } from "react-native";
3
+ import { type GestureResponderEvent, type StyleProp, type ViewStyle } from "react-native";
4
4
  export interface KjunCardProps {
5
5
  title?: string;
6
6
  subtitle?: string;
@@ -17,5 +17,12 @@ export interface KjunCardProps {
17
17
  dividers?: boolean;
18
18
  children?: ReactNode;
19
19
  style?: StyleProp<ViewStyle>;
20
+ /** Makes the whole card one button (onPress). Do not put buttons or links inside a pressable card. */
21
+ pressable?: boolean;
22
+ /** Blocks a pressable card and dims it. */
23
+ disabled?: boolean;
24
+ /** Button name of a pressable card; defaults to the title. */
25
+ ariaLabel?: string;
26
+ onPress?: (event: GestureResponderEvent) => void;
20
27
  }
21
- export declare function KjunCard({ title, subtitle, header, headerActions, media, footer, padding, bodyPadding, radius, surface, elevation, border, dividers, children, style, }: KjunCardProps): import("react/jsx-runtime").JSX.Element;
28
+ export declare function KjunCard({ title, subtitle, header, headerActions, media, footer, padding, bodyPadding, radius, surface, elevation, border, dividers, children, style, pressable, disabled, ariaLabel, onPress, }: KjunCardProps): import("react/jsx-runtime").JSX.Element;
@@ -1,6 +1,6 @@
1
1
  import { type CardElevation, type CardPadding, type CardRadius, type CardSurface } from "@kjun-ui/tokens";
2
2
  import { type ReactNode } from "react";
3
- import { type StyleProp, type ViewStyle } from "react-native";
3
+ import { type GestureResponderEvent, type StyleProp, type ViewStyle } from "react-native";
4
4
  export interface KjunCardProps {
5
5
  title?: string;
6
6
  subtitle?: string;
@@ -17,5 +17,12 @@ export interface KjunCardProps {
17
17
  dividers?: boolean;
18
18
  children?: ReactNode;
19
19
  style?: StyleProp<ViewStyle>;
20
+ /** Makes the whole card one button (onPress). Do not put buttons or links inside a pressable card. */
21
+ pressable?: boolean;
22
+ /** Blocks a pressable card and dims it. */
23
+ disabled?: boolean;
24
+ /** Button name of a pressable card; defaults to the title. */
25
+ ariaLabel?: string;
26
+ onPress?: (event: GestureResponderEvent) => void;
20
27
  }
21
- export declare function KjunCard({ title, subtitle, header, headerActions, media, footer, padding, bodyPadding, radius, surface, elevation, border, dividers, children, style, }: KjunCardProps): import("react/jsx-runtime").JSX.Element;
28
+ export declare function KjunCard({ title, subtitle, header, headerActions, media, footer, padding, bodyPadding, radius, surface, elevation, border, dividers, children, style, pressable, disabled, ariaLabel, onPress, }: KjunCardProps): import("react/jsx-runtime").JSX.Element;
@@ -1,5 +1,7 @@
1
1
  import { type ReactNode } from "react";
2
- export declare function Collapse({ open, children }: {
2
+ /** keepMounted: once opened, the body stays mounted (height 0, hidden from assistive technology) while closed. */
3
+ export declare function Collapse({ open, keepMounted, children }: {
3
4
  open: boolean;
5
+ keepMounted?: boolean;
4
6
  children: ReactNode;
5
7
  }): import("react/jsx-runtime").JSX.Element | null;
@@ -1,5 +1,7 @@
1
1
  import { type ReactNode } from "react";
2
- export declare function Collapse({ open, children }: {
2
+ /** keepMounted: once opened, the body stays mounted (height 0, hidden from assistive technology) while closed. */
3
+ export declare function Collapse({ open, keepMounted, children }: {
3
4
  open: boolean;
5
+ keepMounted?: boolean;
4
6
  children: ReactNode;
5
7
  }): import("react/jsx-runtime").JSX.Element | null;
@@ -49,5 +49,6 @@ export interface KjunProgressProps {
49
49
  showLabel?: boolean;
50
50
  label?: ReactNode;
51
51
  subLabel?: ReactNode;
52
+ shape?: "bar" | "circle";
52
53
  }
53
- export declare function KjunProgress({ value, max, size, variant, showLabel, label, subLabel, }: KjunProgressProps): import("react/jsx-runtime").JSX.Element;
54
+ export declare function KjunProgress({ value, max, size, variant, showLabel, label, subLabel, shape, }: KjunProgressProps): import("react/jsx-runtime").JSX.Element;
@@ -49,5 +49,6 @@ export interface KjunProgressProps {
49
49
  showLabel?: boolean;
50
50
  label?: ReactNode;
51
51
  subLabel?: ReactNode;
52
+ shape?: "bar" | "circle";
52
53
  }
53
- export declare function KjunProgress({ value, max, size, variant, showLabel, label, subLabel, }: KjunProgressProps): import("react/jsx-runtime").JSX.Element;
54
+ export declare function KjunProgress({ value, max, size, variant, showLabel, label, subLabel, shape, }: KjunProgressProps): import("react/jsx-runtime").JSX.Element;
@@ -13,6 +13,8 @@ export interface KjunDrawerProps {
13
13
  footer?: ReactNode;
14
14
  children: ReactNode;
15
15
  onClose?: () => void;
16
+ /** Called once after the close finishes and the layer has left the screen (not when it mounts closed or reopens during the exit). */
17
+ onAfterClose?: () => void;
16
18
  ariaLabel?: string;
17
19
  /** Top safe area, padded inside top and side drawers and kept clear above a bottom sheet. */
18
20
  safeAreaTop?: number;
@@ -23,4 +25,4 @@ export interface KjunDrawerProps {
23
25
  /** Keeps the panel above the on-screen keyboard (devices only). */
24
26
  avoidKeyboard?: boolean;
25
27
  }
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;
28
+ export declare function KjunDrawer({ open, onOpenChange, title, position, width, closable, closeOnOverlay, closeOnEsc, noPadding, header, footer, children, onClose, onAfterClose, ariaLabel, safeAreaTop, safeAreaBottom, maxHeight, avoidKeyboard, }: KjunDrawerProps): import("react/jsx-runtime").JSX.Element;
@@ -13,6 +13,8 @@ export interface KjunDrawerProps {
13
13
  footer?: ReactNode;
14
14
  children: ReactNode;
15
15
  onClose?: () => void;
16
+ /** Called once after the close finishes and the layer has left the screen (not when it mounts closed or reopens during the exit). */
17
+ onAfterClose?: () => void;
16
18
  ariaLabel?: string;
17
19
  /** Top safe area, padded inside top and side drawers and kept clear above a bottom sheet. */
18
20
  safeAreaTop?: number;
@@ -23,4 +25,4 @@ export interface KjunDrawerProps {
23
25
  /** Keeps the panel above the on-screen keyboard (devices only). */
24
26
  avoidKeyboard?: boolean;
25
27
  }
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;
28
+ export declare function KjunDrawer({ open, onOpenChange, title, position, width, closable, closeOnOverlay, closeOnEsc, noPadding, header, footer, children, onClose, onAfterClose, ariaLabel, safeAreaTop, safeAreaBottom, maxHeight, avoidKeyboard, }: KjunDrawerProps): import("react/jsx-runtime").JSX.Element;
@@ -5,6 +5,10 @@ export declare function useKjunFeedback(): KjunFeedback;
5
5
  export interface KjunFeedbackProviderProps {
6
6
  children: ReactNode;
7
7
  style?: StyleProp<ViewStyle>;
8
+ /** A feedback API from createKjunFeedback(), for calls outside components. */
9
+ controller?: KjunFeedback;
8
10
  }
9
- export declare function KjunFeedbackProvider({ children, style }: KjunFeedbackProviderProps): import("react/jsx-runtime").JSX.Element;
11
+ export declare function KjunFeedbackProvider({ children, style, controller: controllerProp }: KjunFeedbackProviderProps): import("react/jsx-runtime").JSX.Element;
10
12
  export type { ConfirmOptions, KjunFeedback, PromptOptions, ToastOptions } from "@kjun-ui/tokens";
13
+ /** An app-owned feedback API that plain functions can call; pass it to KjunFeedbackProvider controller. */
14
+ export { createKjunFeedback } from "@kjun-ui/tokens";
@@ -5,6 +5,10 @@ export declare function useKjunFeedback(): KjunFeedback;
5
5
  export interface KjunFeedbackProviderProps {
6
6
  children: ReactNode;
7
7
  style?: StyleProp<ViewStyle>;
8
+ /** A feedback API from createKjunFeedback(), for calls outside components. */
9
+ controller?: KjunFeedback;
8
10
  }
9
- export declare function KjunFeedbackProvider({ children, style }: KjunFeedbackProviderProps): import("react/jsx-runtime").JSX.Element;
11
+ export declare function KjunFeedbackProvider({ children, style, controller: controllerProp }: KjunFeedbackProviderProps): import("react/jsx-runtime").JSX.Element;
10
12
  export type { ConfirmOptions, KjunFeedback, PromptOptions, ToastOptions } from "@kjun-ui/tokens";
13
+ /** An app-owned feedback API that plain functions can call; pass it to KjunFeedbackProvider controller. */
14
+ export { createKjunFeedback } from "@kjun-ui/tokens";
@@ -1,8 +1,10 @@
1
1
  import { type ReactNode } from "react";
2
- import { type StyleProp, type TextStyle, type ViewStyle } from "react-native";
2
+ import { type StyleProp, type TextProps, type TextStyle, type ViewStyle } from "react-native";
3
3
  import { type InlineAlign, type InlineGap, type InlineJustify, type StackAlign, type StackGap, type TextAlign, type TextTone, type TextVariant } from "../../../shared/package-runtime/layout-primitives.cjs";
4
4
  export type { InlineAlign, InlineGap, InlineJustify, StackAlign, StackGap, TextAlign, TextTone, TextVariant };
5
- export interface KjunTextProps {
5
+ /** Other React Native Text props (accessibilityLabel, testID, selectable, allowFontScaling, adjustsFontSizeToFit…) pass through.
6
+ * adjustsFontSizeToFit only shrinks: the variant size stays the largest. */
7
+ export interface KjunTextProps extends Omit<TextProps, "style" | "children" | "numberOfLines"> {
6
8
  /** Typography role; every size, line height and weight comes from tokens.typography. */
7
9
  variant?: TextVariant;
8
10
  /** Text color role. */
@@ -13,7 +15,7 @@ export interface KjunTextProps {
13
15
  style?: StyleProp<TextStyle>;
14
16
  children?: ReactNode;
15
17
  }
16
- export declare function KjunText({ variant, tone, align, maxLines, style, children }: KjunTextProps): import("react/jsx-runtime").JSX.Element;
18
+ export declare function KjunText({ variant, tone, align, maxLines, style, children, ...rest }: KjunTextProps): import("react/jsx-runtime").JSX.Element;
17
19
  export interface KjunStackProps {
18
20
  /** Space between children: space.stack (sm·md·lg) or space.section (section-sm·md·lg). */
19
21
  gap?: StackGap;
@@ -1,8 +1,10 @@
1
1
  import { type ReactNode } from "react";
2
- import { type StyleProp, type TextStyle, type ViewStyle } from "react-native";
2
+ import { type StyleProp, type TextProps, type TextStyle, type ViewStyle } from "react-native";
3
3
  import { type InlineAlign, type InlineGap, type InlineJustify, type StackAlign, type StackGap, type TextAlign, type TextTone, type TextVariant } from "../../../shared/package-runtime/layout-primitives.js";
4
4
  export type { InlineAlign, InlineGap, InlineJustify, StackAlign, StackGap, TextAlign, TextTone, TextVariant };
5
- export interface KjunTextProps {
5
+ /** Other React Native Text props (accessibilityLabel, testID, selectable, allowFontScaling, adjustsFontSizeToFit…) pass through.
6
+ * adjustsFontSizeToFit only shrinks: the variant size stays the largest. */
7
+ export interface KjunTextProps extends Omit<TextProps, "style" | "children" | "numberOfLines"> {
6
8
  /** Typography role; every size, line height and weight comes from tokens.typography. */
7
9
  variant?: TextVariant;
8
10
  /** Text color role. */
@@ -13,7 +15,7 @@ export interface KjunTextProps {
13
15
  style?: StyleProp<TextStyle>;
14
16
  children?: ReactNode;
15
17
  }
16
- export declare function KjunText({ variant, tone, align, maxLines, style, children }: KjunTextProps): import("react/jsx-runtime").JSX.Element;
18
+ export declare function KjunText({ variant, tone, align, maxLines, style, children, ...rest }: KjunTextProps): import("react/jsx-runtime").JSX.Element;
17
19
  export interface KjunStackProps {
18
20
  /** Space between children: space.stack (sm·md·lg) or space.section (section-sm·md·lg). */
19
21
  gap?: StackGap;
@@ -8,18 +8,33 @@ export interface KjunListRowProps {
8
8
  actions?: ReactNode;
9
9
  href?: string;
10
10
  disabled?: boolean;
11
+ /** Draws the title in the danger role, for rows such as "Delete account". */
12
+ destructive?: boolean;
13
+ /** Ends the row with a decorative chevron that marks it as navigation. */
14
+ chevron?: boolean;
11
15
  onPress?: (event: GestureResponderEvent) => void;
16
+ /** Name of a pressable or link row, e.g. with the amount a screen reader should hear. */
17
+ ariaLabel?: string;
18
+ /** Display-only content under the description (a budget bar); no buttons or links inside a pressable row. */
19
+ children?: ReactNode;
12
20
  }
13
- export declare function KjunListRow({ title, description, leading, trailing, actions, href, disabled, onPress }: KjunListRowProps): import("react/jsx-runtime").JSX.Element;
21
+ export declare function KjunListRow({ title, description, leading, trailing, actions, href, disabled, destructive, chevron, onPress, ariaLabel, children }: KjunListRowProps): import("react/jsx-runtime").JSX.Element;
14
22
  export interface KjunListSectionProps {
15
23
  title?: string;
16
24
  description?: string;
17
25
  actions?: ReactNode;
18
26
  children: ReactNode;
19
27
  ariaLabel?: string;
28
+ /** Paints the surface background (default true); false lets the page background show through. */
29
+ surface?: boolean;
20
30
  /** Draws dividers between rows (default true). */
21
31
  divider?: boolean;
22
32
  /** "compact" rows keep the 16px inset but are 44 tall (the touch minimum) instead of 56. */
23
33
  density?: "default" | "compact";
24
34
  }
25
- export declare function KjunListSection({ title, description, actions, children, ariaLabel, divider, density }: KjunListSectionProps): import("react/jsx-runtime").JSX.Element;
35
+ export declare function KjunListSection({ title, description, actions, children, ariaLabel, surface, divider, density }: KjunListSectionProps): import("react/jsx-runtime").JSX.Element;
36
+ export interface KjunListItemProps {
37
+ children: ReactNode;
38
+ }
39
+ /** Non-row content inside a KjunListSection (a theme switcher, a note) on the rows' inset and dividers. */
40
+ export declare function KjunListItem({ children }: KjunListItemProps): import("react/jsx-runtime").JSX.Element;
@@ -8,18 +8,33 @@ export interface KjunListRowProps {
8
8
  actions?: ReactNode;
9
9
  href?: string;
10
10
  disabled?: boolean;
11
+ /** Draws the title in the danger role, for rows such as "Delete account". */
12
+ destructive?: boolean;
13
+ /** Ends the row with a decorative chevron that marks it as navigation. */
14
+ chevron?: boolean;
11
15
  onPress?: (event: GestureResponderEvent) => void;
16
+ /** Name of a pressable or link row, e.g. with the amount a screen reader should hear. */
17
+ ariaLabel?: string;
18
+ /** Display-only content under the description (a budget bar); no buttons or links inside a pressable row. */
19
+ children?: ReactNode;
12
20
  }
13
- export declare function KjunListRow({ title, description, leading, trailing, actions, href, disabled, onPress }: KjunListRowProps): import("react/jsx-runtime").JSX.Element;
21
+ export declare function KjunListRow({ title, description, leading, trailing, actions, href, disabled, destructive, chevron, onPress, ariaLabel, children }: KjunListRowProps): import("react/jsx-runtime").JSX.Element;
14
22
  export interface KjunListSectionProps {
15
23
  title?: string;
16
24
  description?: string;
17
25
  actions?: ReactNode;
18
26
  children: ReactNode;
19
27
  ariaLabel?: string;
28
+ /** Paints the surface background (default true); false lets the page background show through. */
29
+ surface?: boolean;
20
30
  /** Draws dividers between rows (default true). */
21
31
  divider?: boolean;
22
32
  /** "compact" rows keep the 16px inset but are 44 tall (the touch minimum) instead of 56. */
23
33
  density?: "default" | "compact";
24
34
  }
25
- export declare function KjunListSection({ title, description, actions, children, ariaLabel, divider, density }: KjunListSectionProps): import("react/jsx-runtime").JSX.Element;
35
+ export declare function KjunListSection({ title, description, actions, children, ariaLabel, surface, divider, density }: KjunListSectionProps): import("react/jsx-runtime").JSX.Element;
36
+ export interface KjunListItemProps {
37
+ children: ReactNode;
38
+ }
39
+ /** Non-row content inside a KjunListSection (a theme switcher, a note) on the rows' inset and dividers. */
40
+ export declare function KjunListItem({ children }: KjunListItemProps): import("react/jsx-runtime").JSX.Element;
@@ -1,5 +1,5 @@
1
1
  import { type ReactNode } from "react";
2
- import { type ImageSourcePropType } from "react-native";
2
+ import { type ColorValue, type ImageSourcePropType } from "react-native";
3
3
  export interface KjunImageProps {
4
4
  src?: string;
5
5
  source?: ImageSourcePropType;
@@ -20,8 +20,10 @@ export interface KjunAvatarProps {
20
20
  decorative?: boolean;
21
21
  size?: "sm" | "md" | "lg";
22
22
  shape?: "circle" | "square";
23
+ color?: ColorValue;
24
+ textColor?: ColorValue;
23
25
  fallback?: ReactNode;
24
26
  onLoad?: () => void;
25
27
  onError?: () => void;
26
28
  }
27
- export declare function KjunAvatar({ src, source, name, alt, decorative, size, shape, fallback, onLoad, onError }: KjunAvatarProps): import("react/jsx-runtime").JSX.Element;
29
+ export declare function KjunAvatar({ src, source, name, alt, decorative, size, shape, color, textColor, fallback, onLoad, onError }: KjunAvatarProps): import("react/jsx-runtime").JSX.Element;
@@ -1,5 +1,5 @@
1
1
  import { type ReactNode } from "react";
2
- import { type ImageSourcePropType } from "react-native";
2
+ import { type ColorValue, type ImageSourcePropType } from "react-native";
3
3
  export interface KjunImageProps {
4
4
  src?: string;
5
5
  source?: ImageSourcePropType;
@@ -20,8 +20,10 @@ export interface KjunAvatarProps {
20
20
  decorative?: boolean;
21
21
  size?: "sm" | "md" | "lg";
22
22
  shape?: "circle" | "square";
23
+ color?: ColorValue;
24
+ textColor?: ColorValue;
23
25
  fallback?: ReactNode;
24
26
  onLoad?: () => void;
25
27
  onError?: () => void;
26
28
  }
27
- export declare function KjunAvatar({ src, source, name, alt, decorative, size, shape, fallback, onLoad, onError }: KjunAvatarProps): import("react/jsx-runtime").JSX.Element;
29
+ export declare function KjunAvatar({ src, source, name, alt, decorative, size, shape, color, textColor, fallback, onLoad, onError }: KjunAvatarProps): import("react/jsx-runtime").JSX.Element;
@@ -19,6 +19,8 @@ export interface KjunModalProps {
19
19
  open: boolean;
20
20
  onOpenChange: (value: boolean) => void;
21
21
  onClose?: () => void;
22
+ /** Called once after the close finishes and the layer has left the screen (not when it mounts closed or reopens during the exit). */
23
+ onAfterClose?: () => void;
22
24
  onCancel?: () => void;
23
25
  onConfirm?: () => void;
24
26
  title?: string;
@@ -43,4 +45,4 @@ export interface KjunModalProps {
43
45
  children: ReactNode;
44
46
  ariaLabel?: string;
45
47
  }
46
- export declare function KjunModal({ open, onOpenChange, onClose, onCancel, onConfirm, title, size, closable, closeOnOverlay, closeOnEsc, showHeader, showFooter, showConfirmButton, showCancelButton, confirmText, cancelText, confirmDisabled, loading, noPadding, height, confirmVariant, footerSize, header, footer, children, ariaLabel, }: KjunModalProps): import("react/jsx-runtime").JSX.Element;
48
+ export declare function KjunModal({ open, onOpenChange, onClose, onAfterClose, onCancel, onConfirm, title, size, closable, closeOnOverlay, closeOnEsc, showHeader, showFooter, showConfirmButton, showCancelButton, confirmText, cancelText, confirmDisabled, loading, noPadding, height, confirmVariant, footerSize, header, footer, children, ariaLabel, }: KjunModalProps): import("react/jsx-runtime").JSX.Element;
@@ -19,6 +19,8 @@ export interface KjunModalProps {
19
19
  open: boolean;
20
20
  onOpenChange: (value: boolean) => void;
21
21
  onClose?: () => void;
22
+ /** Called once after the close finishes and the layer has left the screen (not when it mounts closed or reopens during the exit). */
23
+ onAfterClose?: () => void;
22
24
  onCancel?: () => void;
23
25
  onConfirm?: () => void;
24
26
  title?: string;
@@ -43,4 +45,4 @@ export interface KjunModalProps {
43
45
  children: ReactNode;
44
46
  ariaLabel?: string;
45
47
  }
46
- export declare function KjunModal({ open, onOpenChange, onClose, onCancel, onConfirm, title, size, closable, closeOnOverlay, closeOnEsc, showHeader, showFooter, showConfirmButton, showCancelButton, confirmText, cancelText, confirmDisabled, loading, noPadding, height, confirmVariant, footerSize, header, footer, children, ariaLabel, }: KjunModalProps): import("react/jsx-runtime").JSX.Element;
48
+ export declare function KjunModal({ open, onOpenChange, onClose, onAfterClose, onCancel, onConfirm, title, size, closable, closeOnOverlay, closeOnEsc, showHeader, showFooter, showConfirmButton, showCancelButton, confirmText, cancelText, confirmDisabled, loading, noPadding, height, confirmVariant, footerSize, header, footer, children, ariaLabel, }: KjunModalProps): import("react/jsx-runtime").JSX.Element;
@@ -9,11 +9,17 @@ export declare function KjunAccordion({ multiple, tone, children, }: KjunAccordi
9
9
  export interface KjunAccordionItemProps {
10
10
  title?: string;
11
11
  defaultOpen?: boolean;
12
+ /** Controlled open state. A controlled item follows this value only and stays out of the Accordion's single-open rule. */
13
+ open?: boolean;
14
+ /** Requests a change on a controlled item; reports every change on an uncontrolled one (also when a sibling closes it). */
15
+ onOpenChange?: (open: boolean) => void;
16
+ /** Keeps the body mounted after it first opens, so inputs keep their state while closed. */
17
+ keepMounted?: boolean;
12
18
  disabled?: boolean;
13
19
  header?: ReactNode;
14
20
  children: ReactNode;
15
21
  }
16
- export declare function KjunAccordionItem({ title, defaultOpen, disabled, header, children, }: KjunAccordionItemProps): import("react/jsx-runtime").JSX.Element;
22
+ export declare function KjunAccordionItem({ title, defaultOpen, disabled, header, open: openProp, onOpenChange, keepMounted, children, }: KjunAccordionItemProps): import("react/jsx-runtime").JSX.Element;
17
23
  export interface BreadcrumbItem {
18
24
  label: string;
19
25
  to?: string;
@@ -9,11 +9,17 @@ export declare function KjunAccordion({ multiple, tone, children, }: KjunAccordi
9
9
  export interface KjunAccordionItemProps {
10
10
  title?: string;
11
11
  defaultOpen?: boolean;
12
+ /** Controlled open state. A controlled item follows this value only and stays out of the Accordion's single-open rule. */
13
+ open?: boolean;
14
+ /** Requests a change on a controlled item; reports every change on an uncontrolled one (also when a sibling closes it). */
15
+ onOpenChange?: (open: boolean) => void;
16
+ /** Keeps the body mounted after it first opens, so inputs keep their state while closed. */
17
+ keepMounted?: boolean;
12
18
  disabled?: boolean;
13
19
  header?: ReactNode;
14
20
  children: ReactNode;
15
21
  }
16
- export declare function KjunAccordionItem({ title, defaultOpen, disabled, header, children, }: KjunAccordionItemProps): import("react/jsx-runtime").JSX.Element;
22
+ export declare function KjunAccordionItem({ title, defaultOpen, disabled, header, open: openProp, onOpenChange, keepMounted, children, }: KjunAccordionItemProps): import("react/jsx-runtime").JSX.Element;
17
23
  export interface BreadcrumbItem {
18
24
  label: string;
19
25
  to?: string;
@@ -1,5 +1,5 @@
1
1
  export type TextVariant = "pageTitle" | "sectionTitle" | "cardTitle" | "body" | "label" | "meta" | "caption" | "numberSm" | "numberMd" | "numberLg" | "displaySm" | "displayMd" | "displayLg";
2
- export type TextTone = "default" | "secondary" | "tertiary" | "disabled";
2
+ export type TextTone = "default" | "secondary" | "tertiary" | "disabled" | "brand" | "danger" | "success" | "warning" | "inverse";
3
3
  export type TextAlign = "start" | "center" | "end";
4
4
  export type TextTag = "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div";
5
5
  export type StackGap = "none" | "sm" | "md" | "lg" | "section-sm" | "section-md" | "section-lg";
@@ -1,5 +1,5 @@
1
1
  export type TextVariant = "pageTitle" | "sectionTitle" | "cardTitle" | "body" | "label" | "meta" | "caption" | "numberSm" | "numberMd" | "numberLg" | "displaySm" | "displayMd" | "displayLg";
2
- export type TextTone = "default" | "secondary" | "tertiary" | "disabled";
2
+ export type TextTone = "default" | "secondary" | "tertiary" | "disabled" | "brand" | "danger" | "success" | "warning" | "inverse";
3
3
  export type TextAlign = "start" | "center" | "end";
4
4
  export type TextTag = "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div";
5
5
  export type StackGap = "none" | "sm" | "md" | "lg" | "section-sm" | "section-md" | "section-lg";
@@ -0,0 +1,11 @@
1
+ export type ProgressCircleSize = "sm" | "md" | "lg";
2
+ /** Circle progress geometry: the arc is a dashed stroke whose visible length is the percentage of the circumference. */
3
+ export declare function progressCircle(size: ProgressCircleSize, percentage: number): {
4
+ diameter: 24 | 48 | 64;
5
+ stroke: 4 | 6;
6
+ radius: number;
7
+ centre: number;
8
+ circumference: number;
9
+ offset: number;
10
+ showsPercentage: boolean;
11
+ };
@@ -0,0 +1,11 @@
1
+ export type ProgressCircleSize = "sm" | "md" | "lg";
2
+ /** Circle progress geometry: the arc is a dashed stroke whose visible length is the percentage of the circumference. */
3
+ export declare function progressCircle(size: ProgressCircleSize, percentage: number): {
4
+ diameter: 24 | 48 | 64;
5
+ stroke: 4 | 6;
6
+ radius: number;
7
+ centre: number;
8
+ circumference: number;
9
+ offset: number;
10
+ showsPercentage: boolean;
11
+ };
@@ -0,0 +1,6 @@
1
+ /** Calls onAfterClose once when a layer leaves the screen (present true → false). A reopen during the exit keeps
2
+ * present true, so it reports nothing; a layer mounted closed reports nothing either. */
3
+ export declare function useAfterClose(present: boolean, onAfterClose?: () => void, enabled?: boolean): void;
4
+ /** Web overlays can outlive present by a render (react-aria finishes its exit first), so this reports when the
5
+ * overlay element itself leaves the DOM while closed. Attach the returned ref to the overlay. */
6
+ export declare function useAfterCloseRef(open: boolean, onAfterClose?: () => void): (node: Element | null) => void;
@@ -0,0 +1,6 @@
1
+ /** Calls onAfterClose once when a layer leaves the screen (present true → false). A reopen during the exit keeps
2
+ * present true, so it reports nothing; a layer mounted closed reports nothing either. */
3
+ export declare function useAfterClose(present: boolean, onAfterClose?: () => void, enabled?: boolean): void;
4
+ /** Web overlays can outlive present by a render (react-aria finishes its exit first), so this reports when the
5
+ * overlay element itself leaves the DOM while closed. Attach the returned ref to the overlay. */
6
+ export declare function useAfterCloseRef(open: boolean, onAfterClose?: () => void): (node: Element | null) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kjun-ui/native",
3
- "version": "0.13.0",
3
+ "version": "0.15.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.13.0",
51
- "@kjun-ui/icons": "0.13.0"
50
+ "@kjun-ui/tokens": "0.15.0",
51
+ "@kjun-ui/icons": "0.15.0"
52
52
  },
53
53
  "peerDependencies": {
54
54
  "react": "^19.2.3",