@uxf/ui 11.104.0 → 11.106.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 (44) hide show
  1. package/css/avatar.css +19 -3
  2. package/css/badge.css +15 -6
  3. package/css/button-list.css +3 -1
  4. package/css/button.css +3 -1
  5. package/css/checkbox-button.css +79 -25
  6. package/css/checkbox-input.css +2 -3
  7. package/css/checkbox.css +49 -24
  8. package/css/color-radio-group.css +7 -3
  9. package/css/color-radio.css +29 -6
  10. package/css/date-picker.css +5 -2
  11. package/css/dropzone.css +9 -3
  12. package/css/error-message.css +4 -1
  13. package/css/file-input.css +30 -11
  14. package/css/flash-messages.css +2 -2
  15. package/css/form-component.css +5 -2
  16. package/css/image-gallery.css +65 -15
  17. package/css/input-basic.css +10 -4
  18. package/css/input-with-popover.css +15 -6
  19. package/css/input.css +69 -40
  20. package/css/label.css +7 -2
  21. package/css/list-item.css +14 -5
  22. package/css/loader.css +17 -6
  23. package/css/lozenge.css +2 -2
  24. package/css/menu.css +2 -2
  25. package/css/multi-combobox.css +0 -8
  26. package/css/multi-select.css +0 -10
  27. package/css/pagination.css +27 -16
  28. package/css/paper.css +3 -1
  29. package/css/radio-group.css +39 -13
  30. package/css/radio.css +45 -23
  31. package/css/raster-image.css +14 -5
  32. package/css/text-link.css +5 -1
  33. package/css/typography.css +30 -10
  34. package/date-picker/date-picker-decade.js +4 -2
  35. package/date-picker/date-picker.stories.js +1 -1
  36. package/date-range-picker/date-range-picker-decade.js +4 -2
  37. package/label/label.js +2 -1
  38. package/modal/README.md +64 -22
  39. package/modal/index.d.ts +1 -1
  40. package/modal/index.js +2 -2
  41. package/modal/modal-service.d.ts +3 -3
  42. package/modal/modal-service.js +5 -10
  43. package/modal/modal.stories.js +5 -5
  44. package/package.json +5 -5
@@ -1,8 +1,20 @@
1
1
  .uxf-pagination {
2
- @apply isolate inline-flex -space-x-px rounded-md;
2
+ @apply -space-x-px;
3
+
4
+ border-radius: theme("borderRadius.md");
5
+ display: inline-flex;
6
+ isolation: isolate;
3
7
 
4
8
  &__button {
5
- @apply relative inline-flex cursor-pointer items-center border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50 focus:z-10;
9
+ @apply border border-gray-300 text-sm hover:bg-gray-50 focus:z-10;
10
+
11
+ align-items: center;
12
+ background-color: theme("colors.white");
13
+ color: theme("colors.gray.500");
14
+ cursor: pointer;
15
+ display: inline-flex;
16
+ font-weight: 500;
17
+ position: relative;
6
18
 
7
19
  :root .dark & {
8
20
  @apply border-gray-600 bg-gray-900 text-gray-200 hover:bg-gray-800;
@@ -17,15 +29,17 @@
17
29
  }
18
30
 
19
31
  &:first-child {
20
- @apply rounded-l-md;
32
+ border-radius: theme("borderRadius.md") 0 0 theme("borderRadius.md");
21
33
  }
22
34
 
23
35
  &:last-child {
24
- @apply rounded-r-md;
36
+ border-radius: 0 theme("borderRadius.md") theme("borderRadius.md") 0;
25
37
  }
26
38
 
27
39
  &.is-disabled {
28
- @apply cursor-not-allowed text-neutral-300 hover:bg-white;
40
+ @apply text-neutral-300 hover:bg-white;
41
+
42
+ cursor: not-allowed;
29
43
 
30
44
  :root .dark & {
31
45
  @apply text-neutral-500 hover:bg-gray-800;
@@ -33,7 +47,9 @@
33
47
  }
34
48
 
35
49
  &.is-ellipsis {
36
- @apply pointer-events-none text-neutral-300 hover:bg-white;
50
+ @apply text-neutral-300 hover:bg-white;
51
+
52
+ pointer-events: none;
37
53
 
38
54
  :root .dark & {
39
55
  @apply text-neutral-500 hover:bg-gray-800;
@@ -46,32 +62,27 @@
46
62
  }
47
63
 
48
64
  &--size-xs .uxf-pagination__button {
49
- @apply px-2;
50
-
51
65
  height: theme("inputSize.xs");
66
+ padding-inline: theme("spacing.2");
52
67
  }
53
68
 
54
69
  &--size-sm .uxf-pagination__button {
55
- @apply px-3;
56
-
57
70
  height: theme("inputSize.sm");
71
+ padding-inline: theme("spacing.3");
58
72
  }
59
73
 
60
74
  &--size-default .uxf-pagination__button {
61
- @apply px-4;
62
-
63
75
  height: theme("inputSize.default");
76
+ padding-inline: theme("spacing.4");
64
77
  }
65
78
 
66
79
  &--size-lg .uxf-pagination__button {
67
- @apply px-5;
68
-
69
80
  height: theme("inputSize.lg");
81
+ padding-inline: theme("spacing.5");
70
82
  }
71
83
 
72
84
  &--size-xl .uxf-pagination__button {
73
- @apply px-6;
74
-
75
85
  height: theme("inputSize.xl");
86
+ padding-inline: theme("spacing.6");
76
87
  }
77
88
  }
package/css/paper.css CHANGED
@@ -1,3 +1,5 @@
1
1
  .uxf-paper {
2
- @apply shadow-paper rounded-lg bg-white;
2
+ background-color: theme("colors.white");
3
+ border-radius: theme("borderRadius.lg");
4
+ box-shadow: theme("boxShadow.paper");
3
5
  }
@@ -13,20 +13,22 @@
13
13
 
14
14
  &.is-selected {
15
15
  .uxf-radio-group__option-label {
16
- @apply text-gray-900;
16
+ color: theme("colors.gray.900");
17
17
 
18
18
  :root .dark & {
19
- @apply text-white;
19
+ color: theme("colors.white");
20
20
  }
21
21
  }
22
22
  }
23
23
  }
24
24
 
25
25
  &__option-label {
26
- @apply line-clamp-2 text-gray-600;
26
+ @apply line-clamp-2;
27
+
28
+ color: theme("colors.gray.600");
27
29
 
28
30
  :root .dark & {
29
- @apply text-white/80;
31
+ color: rgb(255 255 255 / 80%);
30
32
  }
31
33
  }
32
34
 
@@ -45,15 +47,21 @@
45
47
  }
46
48
 
47
49
  .uxf-radio-group__options-wrapper {
48
- @apply divide-y divide-gray-200;
50
+ & > * + * {
51
+ border-color: theme("colors.gray.200");
52
+ border-top-width: 1px;
53
+ }
49
54
 
50
55
  :root .dark & {
51
- @apply divide-gray-800;
56
+ & > * + * {
57
+ border-color: theme("colors.gray.800");
58
+ }
52
59
  }
53
60
  }
54
61
 
55
62
  .uxf-radio-group__option-wrapper {
56
- @apply items-center py-4;
63
+ align-items: center;
64
+ padding-block: theme("spacing.4");
57
65
  }
58
66
 
59
67
  .uxf-radio-group__option-label {
@@ -77,17 +85,36 @@
77
85
  }
78
86
 
79
87
  .uxf-radio-group__option-wrapper {
80
- @apply relative min-w-[112px] flex-col-reverse p-3 before:pointer-events-none before:absolute before:inset-0 before:rounded-lg before:border before:border-gray-400;
88
+ flex-direction: column-reverse;
89
+ min-width: 112px;
90
+ padding: theme("spacing.3");
91
+ position: relative;
92
+
93
+ &::before {
94
+ border: 1px solid theme("colors.gray.400");
95
+ border-radius: theme("borderRadius.lg");
96
+ content: "";
97
+ inset: 0;
98
+ pointer-events: none;
99
+ position: absolute;
100
+ }
81
101
 
82
102
  :root .dark & {
83
- @apply border-gray-400;
103
+ &::before {
104
+ border-color: theme("colors.gray.400");
105
+ }
84
106
  }
85
107
 
86
108
  &.is-selected {
87
- @apply before:border-primary before:border-2;
109
+ &::before {
110
+ border-color: theme("colors.primary.DEFAULT");
111
+ border-width: 2px;
112
+ }
88
113
 
89
114
  :root .dark & {
90
- @apply before:border-primary;
115
+ &::before {
116
+ border-color: theme("colors.primary.DEFAULT");
117
+ }
91
118
  }
92
119
  }
93
120
  }
@@ -110,9 +137,8 @@
110
137
  }
111
138
 
112
139
  .uxf-radio-group__option-wrapper {
113
- @apply flex-row-reverse;
114
-
115
140
  align-items: center;
141
+ flex-direction: row-reverse;
116
142
  }
117
143
 
118
144
  .uxf-radio-group__option-label {
package/css/radio.css CHANGED
@@ -1,91 +1,113 @@
1
1
  .uxf-radio {
2
- @apply focus-visible:ring-primary flex shrink-0 items-center justify-center rounded-full border border-gray-400 transition focus-visible:ring-2;
2
+ @apply focus-visible:ring-primary transition focus-visible:ring-2;
3
+
4
+ align-items: center;
5
+ border: 1px solid theme("colors.gray.400");
6
+ border-radius: 9999px;
7
+ display: flex;
8
+ flex-shrink: 0;
9
+ justify-content: center;
3
10
 
4
11
  :root .dark & {
5
- @apply focus-visible:ring-primary border-gray-400 focus-visible:ring-offset-gray-900;
12
+ @apply focus-visible:ring-primary focus-visible:ring-offset-gray-900;
13
+
14
+ border-color: theme("colors.gray.400");
6
15
  }
7
16
 
8
17
  &__inner {
9
- @apply block rounded-full opacity-0 transition;
18
+ @apply transition;
19
+
20
+ border-radius: 9999px;
21
+ display: block;
22
+ opacity: 0;
10
23
  }
11
24
 
12
25
  &.is-selected {
13
- @apply bg-primary border-none;
26
+ background-color: theme("colors.primary.DEFAULT");
27
+ border: none;
14
28
 
15
29
  :root .dark & {
16
- @apply bg-primary border-none;
30
+ background-color: theme("colors.primary.DEFAULT");
31
+ border: none;
17
32
  }
18
33
 
19
34
  .uxf-radio__inner {
20
- @apply bg-white opacity-100;
35
+ background-color: theme("colors.white");
36
+ opacity: 1;
21
37
 
22
38
  :root .dark & {
23
- @apply bg-white;
39
+ background-color: theme("colors.white");
24
40
  }
25
41
  }
26
42
  }
27
43
 
28
44
  &.is-disabled {
29
- @apply pointer-events-none border-gray-200;
45
+ border-color: theme("colors.gray.200");
46
+ pointer-events: none;
30
47
 
31
48
  :root .dark & {
32
- @apply pointer-events-none border-gray-600;
49
+ border-color: theme("colors.gray.600");
50
+ pointer-events: none;
33
51
  }
34
52
 
35
53
  &.is-selected {
36
- @apply bg-gray-200;
54
+ background-color: theme("colors.gray.200");
37
55
 
38
56
  :root .dark & {
39
- @apply bg-gray-600;
57
+ background-color: theme("colors.gray.600");
40
58
  }
41
59
  }
42
60
 
43
61
  .uxf-radio__inner {
44
- @apply bg-gray-400;
62
+ background-color: theme("colors.gray.400");
45
63
 
46
64
  :root .dark & {
47
- @apply bg-gray-800;
65
+ background-color: theme("colors.gray.800");
48
66
  }
49
67
  }
50
68
  }
51
69
 
52
70
  &.is-invalid {
53
- @apply border-error;
71
+ border-color: theme("colors.error.DEFAULT");
54
72
 
55
73
  :root .dark & {
56
- @apply border-error;
74
+ border-color: theme("colors.error.DEFAULT");
57
75
  }
58
76
 
59
77
  &.is-selected {
60
- @apply bg-error;
78
+ background-color: theme("colors.error.DEFAULT");
61
79
 
62
80
  :root .dark & {
63
- @apply bg-error;
81
+ background-color: theme("colors.error.DEFAULT");
64
82
  }
65
83
  }
66
84
 
67
85
  .uxf-radio__inner {
68
- @apply bg-white;
86
+ background-color: theme("colors.white");
69
87
 
70
88
  :root .dark & {
71
- @apply bg-white;
89
+ background-color: theme("colors.white");
72
90
  }
73
91
  }
74
92
  }
75
93
 
76
94
  &--size-default {
77
- @apply size-4;
95
+ height: theme("spacing.4");
96
+ width: theme("spacing.4");
78
97
 
79
98
  .uxf-radio__inner {
80
- @apply size-1.5;
99
+ height: theme("spacing[1.5]");
100
+ width: theme("spacing[1.5]");
81
101
  }
82
102
  }
83
103
 
84
104
  &--size-lg {
85
- @apply size-6;
105
+ height: theme("spacing.6");
106
+ width: theme("spacing.6");
86
107
 
87
108
  .uxf-radio__inner {
88
- @apply size-2.5;
109
+ height: theme("spacing[2.5]");
110
+ width: theme("spacing[2.5]");
89
111
  }
90
112
  }
91
113
  }
@@ -1,20 +1,29 @@
1
1
  .uxf-raster-image {
2
- @apply block w-full;
2
+ display: block;
3
+ width: 100%;
3
4
 
4
5
  &--contain,
5
6
  &--cover {
6
- @apply relative;
7
+ position: relative;
7
8
  }
8
9
 
9
10
  &__img {
10
- @apply block h-auto w-full;
11
+ display: block;
12
+ height: auto;
13
+ width: 100%;
11
14
 
12
15
  &--contain {
13
- @apply absolute inset-0 h-full object-contain;
16
+ height: 100%;
17
+ inset: 0;
18
+ object-fit: contain;
19
+ position: absolute;
14
20
  }
15
21
 
16
22
  &--cover {
17
- @apply absolute inset-0 h-full object-cover;
23
+ height: 100%;
24
+ inset: 0;
25
+ object-fit: cover;
26
+ position: absolute;
18
27
  }
19
28
  }
20
29
  }
package/css/text-link.css CHANGED
@@ -1,5 +1,9 @@
1
1
  .uxf-text-link {
2
- @apply relative underline outline-none transition before:absolute before:-inset-1 focus-visible:before:rounded-lg focus-visible:before:border-2;
2
+ @apply transition before:absolute before:-inset-1 focus-visible:before:rounded-lg focus-visible:before:border-2;
3
+
4
+ outline: none;
5
+ position: relative;
6
+ text-decoration-line: underline;
3
7
 
4
8
  &--variant-default {
5
9
  color: theme("colors.brand_surface_default");
@@ -1,25 +1,37 @@
1
1
  .uxf-typo-h1 {
2
- @apply text-desktopH1 font-bold;
2
+ @apply text-desktopH1;
3
+
4
+ font-weight: 700;
3
5
  }
4
6
 
5
7
  .uxf-typo-h2 {
6
- @apply text-desktopH2 font-bold;
8
+ @apply text-desktopH2;
9
+
10
+ font-weight: 700;
7
11
  }
8
12
 
9
13
  .uxf-typo-h3 {
10
- @apply text-desktopH3 font-bold;
14
+ @apply text-desktopH3;
15
+
16
+ font-weight: 700;
11
17
  }
12
18
 
13
19
  .uxf-typo-h4 {
14
- @apply text-desktopH4 font-bold;
20
+ @apply text-desktopH4;
21
+
22
+ font-weight: 700;
15
23
  }
16
24
 
17
25
  .uxf-typo-h5 {
18
- @apply text-desktopH5 font-bold;
26
+ @apply text-desktopH5;
27
+
28
+ font-weight: 700;
19
29
  }
20
30
 
21
31
  .uxf-typo-h6 {
22
- @apply text-desktopH6 font-bold;
32
+ @apply text-desktopH6;
33
+
34
+ font-weight: 700;
23
35
  }
24
36
 
25
37
  .uxf-typo-body {
@@ -31,7 +43,9 @@
31
43
  }
32
44
 
33
45
  .uxf-typo-button {
34
- @apply text-body font-bold;
46
+ @apply text-body;
47
+
48
+ font-weight: 700;
35
49
  }
36
50
 
37
51
  .uxf-typo-caption {
@@ -39,13 +53,19 @@
39
53
  }
40
54
 
41
55
  .uxf-typo-medium {
42
- @apply text-medium font-medium;
56
+ @apply text-medium;
57
+
58
+ font-weight: 500;
43
59
  }
44
60
 
45
61
  .uxf-typo-medium2 {
46
- @apply text-medium2 font-medium;
62
+ @apply text-medium2;
63
+
64
+ font-weight: 500;
47
65
  }
48
66
 
49
67
  .uxf-typo-overline {
50
- @apply text-overline font-bold;
68
+ @apply text-overline;
69
+
70
+ font-weight: 700;
51
71
  }
@@ -84,10 +84,12 @@ exports.DatePickerDecade = (0, react_1.memo)((props) => {
84
84
  onClick: handleSetMonthViewMode,
85
85
  title: (_f = (_e = props.customButtonTitles) === null || _e === void 0 ? void 0 : _e.selectMonth) !== null && _f !== void 0 ? _f : t("uxf-ui-date-picker:select-month"),
86
86
  }, nextButtonProps: {
87
- onClick: handleGoToNextDecadeClick,
87
+ isDisabled: !canGoToNextDecade,
88
+ onClick: canGoToNextDecade ? handleGoToNextDecadeClick : undefined,
88
89
  title: (_h = (_g = props.customButtonTitles) === null || _g === void 0 ? void 0 : _g.nextDecade) !== null && _h !== void 0 ? _h : t("uxf-ui-date-picker:next-decade"),
89
90
  }, prevButtonProps: {
90
- onClick: handleGoToPrevDecadeClick,
91
+ isDisabled: !canGoToPrevDecade,
92
+ onClick: canGoToPrevDecade ? handleGoToPrevDecadeClick : undefined,
91
93
  title: (_k = (_j = props.customButtonTitles) === null || _j === void 0 ? void 0 : _j.prevDecade) !== null && _k !== void 0 ? _k : t("uxf-ui-date-picker:prev-decade"),
92
94
  }, yearButtonProps: {
93
95
  children: currentYear,
@@ -38,7 +38,7 @@ const date_1 = require("@uxf/localize/src/date");
38
38
  const react_1 = __importStar(require("react"));
39
39
  const action_1 = require("../utils/action");
40
40
  const date_picker_1 = require("./date-picker");
41
- const today = (0, date_1.dayjsTz)();
41
+ const today = (0, date_1.dayjsTz)().startOf("day");
42
42
  const minDate = today.subtract(10, "day").toDate();
43
43
  const maxDate = today.add(20, "days").toDate();
44
44
  const unavailableDates = [
@@ -91,10 +91,12 @@ exports.DateRangePickerDecade = (0, react_1.memo)((props) => {
91
91
  onClick: handleSetMonthViewMode,
92
92
  title: (_f = (_e = props.customButtonTitles) === null || _e === void 0 ? void 0 : _e.selectMonth) !== null && _f !== void 0 ? _f : t("uxf-ui-date-range-picker:select-month"),
93
93
  }, nextButtonProps: {
94
- onClick: handleGoToNextDecadeClick,
94
+ isDisabled: !canGoToNextDecade,
95
+ onClick: canGoToNextDecade ? handleGoToNextDecadeClick : undefined,
95
96
  title: (_h = (_g = props.customButtonTitles) === null || _g === void 0 ? void 0 : _g.nextDecade) !== null && _h !== void 0 ? _h : t("uxf-ui-date-range-picker:next-decade"),
96
97
  }, prevButtonProps: {
97
- onClick: handleGoToPrevDecadeClick,
98
+ isDisabled: !canGoToPrevDecade,
99
+ onClick: canGoToPrevDecade ? handleGoToPrevDecadeClick : undefined,
98
100
  title: (_k = (_j = props.customButtonTitles) === null || _j === void 0 ? void 0 : _j.prevDecade) !== null && _k !== void 0 ? _k : t("uxf-ui-date-range-picker:prev-decade"),
99
101
  }, yearButtonProps: {
100
102
  children: yearLabel,
package/label/label.js CHANGED
@@ -37,11 +37,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
37
37
  exports.Label = void 0;
38
38
  const classes_1 = require("@uxf/core/constants/classes");
39
39
  const cx_1 = require("@uxf/core/utils/cx");
40
+ const is_not_nil_1 = require("@uxf/core/utils/is-not-nil");
40
41
  const react_1 = __importStar(require("react"));
41
42
  exports.Label = (0, react_1.forwardRef)((props, ref) => {
42
43
  const className = (0, cx_1.cx)("uxf-label", props.isHidden && "uxf-label--hidden", props.isRequired && classes_1.CLASSES.IS_REQUIRED, props.className);
43
44
  return (
44
45
  // eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-noninteractive-element-interactions
45
- react_1.default.createElement("label", { className: className, form: props.form, htmlFor: props.htmlFor, id: props.id, onClick: props.onClick, ref: ref }, props.children));
46
+ react_1.default.createElement("label", { className: className, form: props.form, htmlFor: props.htmlFor, id: props.id, onClick: props.onClick, ref: ref, tabIndex: (0, is_not_nil_1.isNotNil)(ref) ? 0 : undefined }, props.children));
46
47
  });
47
48
  exports.Label.displayName = "UxfUiLabel";
package/modal/README.md CHANGED
@@ -50,7 +50,7 @@ function Component(props: AppProps) {
50
50
  return (
51
51
  <div>
52
52
  <Button onClick={() => setIsOpen(true)}>Click to open modal</Button>
53
- <Modal close={() => setIsOpen(false)} isOpen={isOpen}>
53
+ <Modal onClose={() => setIsOpen(false)} isOpen={isOpen}>
54
54
  <DialogPanel width="xs">Whatever content inside Modal</DialogPanel>
55
55
  </Modal>
56
56
  </div>
@@ -64,15 +64,13 @@ The modal system supports opening multiple modals simultaneously in different la
64
64
 
65
65
  ### Default Layer Configuration
66
66
 
67
- By default, four layers are available:
67
+ By default, two layers are available:
68
68
 
69
69
  ```typescript
70
70
  {
71
71
  layers: {
72
- main: { name: "main", zIndex: 1000 },
73
- confirmation: { name: "confirmation", zIndex: 1010 },
74
- alert: { name: "alert", zIndex: 1020 },
75
- critical: { name: "critical", zIndex: 1030 },
72
+ main: { name: "main", zIndex: 100 },
73
+ confirm: { name: "confirm", zIndex: 1000 },
76
74
  },
77
75
  defaultLayer: "main"
78
76
  }
@@ -81,7 +79,7 @@ By default, four layers are available:
81
79
  ### Basic Usage with Layers
82
80
 
83
81
  ```tsx
84
- import { openModal, closeModal, closeAllModals, closeModalsByLayer } from "@uxf/ui/modal";
82
+ import { openModal, closeModal, closeModalLayer, closeAllModals } from "@uxf/ui/modal";
85
83
 
86
84
  // Open modal in default "main" layer
87
85
  openModal({
@@ -92,17 +90,14 @@ openModal({
92
90
  openModal({
93
91
  children: <DialogPanel>Confirmation dialog</DialogPanel>
94
92
  }, {
95
- layer: "confirmation" // TypeScript autocompletes: "main" | "confirmation" | "alert" | "critical"
93
+ layer: "confirm" // TypeScript autocompletes: "main" | "confirm"
96
94
  });
97
95
 
98
- // Close topmost modal
96
+ // Close topmost modal (highest z-index)
99
97
  closeModal();
100
98
 
101
- // Close modal in specific layer
102
- closeModal("confirmation");
103
-
104
- // Close all modals in a layer
105
- closeModalsByLayer("alert");
99
+ // Close all modals in a specific layer
100
+ closeModalLayer("confirm");
106
101
 
107
102
  // Close all modals across all layers
108
103
  closeAllModals();
@@ -113,7 +108,58 @@ closeAllModals();
113
108
  - Modals with higher z-index appear on top of modals with lower z-index
114
109
  - Only the topmost modal (highest z-index) responds to backdrop clicks and ESC key
115
110
  - Clicking inside a higher-layer modal will NOT close lower-layer modals
116
- - Each layer can have one modal by default (use `replace: false` option to allow multiple)
111
+ - Each layer can have one modal by default (use `shouldReplace: false` option to allow multiple)
112
+
113
+ ## Closing Modals
114
+
115
+ ### Service functions (for modals opened via `openModal`)
116
+
117
+ | Function | Description |
118
+ |---|---|
119
+ | `closeModal()` | Closes the topmost modal (highest z-index across all layers) |
120
+ | `closeModalLayer(layer)` | Closes all modals in the specified layer |
121
+ | `closeAllModals()` | Closes all modals across all layers |
122
+
123
+ ```tsx
124
+ import { closeModal, closeModalLayer, closeAllModals } from "@uxf/ui/modal";
125
+
126
+ closeModal(); // closes topmost
127
+ closeModalLayer("confirm"); // closes all in "confirm" layer
128
+ closeAllModals(); // closes everything
129
+ ```
130
+
131
+ ### `onClose` callback
132
+
133
+ When opening a modal via `openModal`, you can pass an `onClose` callback that fires whenever the modal is closed (by backdrop click, ESC key, or programmatic close):
134
+
135
+ ```tsx
136
+ openModal({
137
+ children: <DialogPanel>Content</DialogPanel>,
138
+ onClose: () => console.log("modal closed"),
139
+ });
140
+ ```
141
+
142
+ ### Disabling close triggers
143
+
144
+ You can prevent the modal from being closed by backdrop click or ESC key:
145
+
146
+ ```tsx
147
+ openModal({
148
+ children: <DialogPanel>Content</DialogPanel>,
149
+ isBackdropCloseDisabled: true, // backdrop click won't close
150
+ isEscapeKeyCloseDisabled: true, // ESC key won't close
151
+ });
152
+ ```
153
+
154
+ ### `Modal` component (controlled)
155
+
156
+ When using the `Modal` component directly, pass `onClose` to handle close events:
157
+
158
+ ```tsx
159
+ <Modal onClose={() => setIsOpen(false)} isOpen={isOpen} isBackdropCloseDisabled={false}>
160
+ <DialogPanel>Content</DialogPanel>
161
+ </Modal>
162
+ ```
117
163
 
118
164
  ## Custom Layer Configuration
119
165
 
@@ -180,10 +226,9 @@ You can extend the available layer names using TypeScript declaration merging:
180
226
  // types/modal.d.ts (in your project)
181
227
  import "@uxf/ui/modal";
182
228
 
183
- declare module "@uxf/ui/modal" {
229
+ declare module "@uxf/ui/modal/theme" {
184
230
  interface ModalLayers {
185
231
  // Add your custom layer names
186
- base: true;
187
232
  form: true;
188
233
  notification: true;
189
234
  tooltip: true;
@@ -201,11 +246,8 @@ const customModalLayers = {
201
246
  layers: {
202
247
  // Default layers (keep or override)
203
248
  main: { name: "main", zIndex: 1000 },
204
- confirmation: { name: "confirmation", zIndex: 1010 },
205
- alert: { name: "alert", zIndex: 1020 },
206
- critical: { name: "critical", zIndex: 1030 },
249
+ confirm: { name: "confirm", zIndex: 1010 },
207
250
  // Your custom layers
208
- base: { name: "base", zIndex: 900 },
209
251
  form: { name: "form", zIndex: 1100 },
210
252
  notification: { name: "notification", zIndex: 9000 },
211
253
  tooltip: { name: "tooltip", zIndex: 9999 },
@@ -228,7 +270,7 @@ openModal({
228
270
  children: <DialogPanel>Form content</DialogPanel>
229
271
  }, {
230
272
  layer: "form" // ✅ TypeScript autocompletes and validates!
231
- // Available: "main" | "confirmation" | "alert" | "critical" | "base" | "form" | "notification" | "tooltip"
273
+ // Available: "main" | "confirm" | "form" | "notification" | "tooltip"
232
274
  });
233
275
  ```
234
276