@reactzero/combo 0.1.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 (124) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/LICENSE +21 -0
  3. package/README.md +188 -0
  4. package/dist/Combo-Cx3kSkop.cjs +2 -0
  5. package/dist/Combo-Cx3kSkop.cjs.map +1 -0
  6. package/dist/Combo-qs6_L512.js +439 -0
  7. package/dist/Combo-qs6_L512.js.map +1 -0
  8. package/dist/components/Combo.d.ts +71 -0
  9. package/dist/components/Combo.d.ts.map +1 -0
  10. package/dist/components/LiveRegion.d.ts +7 -0
  11. package/dist/components/LiveRegion.d.ts.map +1 -0
  12. package/dist/components/Portal.d.ts +8 -0
  13. package/dist/components/Portal.d.ts.map +1 -0
  14. package/dist/components/slots/CheckboxItem.d.ts +38 -0
  15. package/dist/components/slots/CheckboxItem.d.ts.map +1 -0
  16. package/dist/components/slots/CustomItem.d.ts +35 -0
  17. package/dist/components/slots/CustomItem.d.ts.map +1 -0
  18. package/dist/components/slots/FooterActions.d.ts +42 -0
  19. package/dist/components/slots/FooterActions.d.ts.map +1 -0
  20. package/dist/components/slots/GroupSeparator.d.ts +30 -0
  21. package/dist/components/slots/GroupSeparator.d.ts.map +1 -0
  22. package/dist/components/slots/index.d.ts +9 -0
  23. package/dist/components/slots/index.d.ts.map +1 -0
  24. package/dist/components/tabs/TabbedCombo.d.ts +45 -0
  25. package/dist/components/tabs/TabbedCombo.d.ts.map +1 -0
  26. package/dist/components/tabs/index.d.ts +3 -0
  27. package/dist/components/tabs/index.d.ts.map +1 -0
  28. package/dist/core/announce.d.ts +10 -0
  29. package/dist/core/announce.d.ts.map +1 -0
  30. package/dist/core/ids.d.ts +13 -0
  31. package/dist/core/ids.d.ts.map +1 -0
  32. package/dist/core/keyboard.d.ts +13 -0
  33. package/dist/core/keyboard.d.ts.map +1 -0
  34. package/dist/core/scroll.d.ts +5 -0
  35. package/dist/core/scroll.d.ts.map +1 -0
  36. package/dist/core/stateMachine.d.ts +32 -0
  37. package/dist/core/stateMachine.d.ts.map +1 -0
  38. package/dist/core/utils.d.ts +26 -0
  39. package/dist/core/utils.d.ts.map +1 -0
  40. package/dist/defaults-iFGq2Q-7.cjs +2 -0
  41. package/dist/defaults-iFGq2Q-7.cjs.map +1 -0
  42. package/dist/defaults-rhC5DFTg.js +53 -0
  43. package/dist/defaults-rhC5DFTg.js.map +1 -0
  44. package/dist/entries/hook-bare.d.ts +4 -0
  45. package/dist/entries/hook-bare.d.ts.map +1 -0
  46. package/dist/entries/hook.d.ts +4 -0
  47. package/dist/entries/hook.d.ts.map +1 -0
  48. package/dist/entries/icons.d.ts +4 -0
  49. package/dist/entries/icons.d.ts.map +1 -0
  50. package/dist/entries/index.d.ts +9 -0
  51. package/dist/entries/index.d.ts.map +1 -0
  52. package/dist/entries/position.d.ts +3 -0
  53. package/dist/entries/position.d.ts.map +1 -0
  54. package/dist/entries/slots.d.ts +9 -0
  55. package/dist/entries/slots.d.ts.map +1 -0
  56. package/dist/entries/tabs.d.ts +4 -0
  57. package/dist/entries/tabs.d.ts.map +1 -0
  58. package/dist/hook-bare.cjs +2 -0
  59. package/dist/hook-bare.cjs.map +1 -0
  60. package/dist/hook-bare.js +9 -0
  61. package/dist/hook-bare.js.map +1 -0
  62. package/dist/hook.cjs +2 -0
  63. package/dist/hook.cjs.map +1 -0
  64. package/dist/hook.js +11 -0
  65. package/dist/hook.js.map +1 -0
  66. package/dist/hooks/useCombo.d.ts +3 -0
  67. package/dist/hooks/useCombo.d.ts.map +1 -0
  68. package/dist/hooks/usePosition.d.ts +16 -0
  69. package/dist/hooks/usePosition.d.ts.map +1 -0
  70. package/dist/icons/defaults.d.ts +16 -0
  71. package/dist/icons/defaults.d.ts.map +1 -0
  72. package/dist/icons/icons.d.ts +30 -0
  73. package/dist/icons/icons.d.ts.map +1 -0
  74. package/dist/icons-Ch1Q5AhF.js +40 -0
  75. package/dist/icons-Ch1Q5AhF.js.map +1 -0
  76. package/dist/icons-vzkEacAb.cjs +2 -0
  77. package/dist/icons-vzkEacAb.cjs.map +1 -0
  78. package/dist/icons.cjs +2 -0
  79. package/dist/icons.cjs.map +1 -0
  80. package/dist/icons.js +20 -0
  81. package/dist/icons.js.map +1 -0
  82. package/dist/index.cjs +2 -0
  83. package/dist/index.cjs.map +1 -0
  84. package/dist/index.d.ts +2 -0
  85. package/dist/index.d.ts.map +1 -0
  86. package/dist/index.js +12 -0
  87. package/dist/index.js.map +1 -0
  88. package/dist/position.cjs +2 -0
  89. package/dist/position.cjs.map +1 -0
  90. package/dist/position.js +5 -0
  91. package/dist/position.js.map +1 -0
  92. package/dist/slots.cjs +2 -0
  93. package/dist/slots.cjs.map +1 -0
  94. package/dist/slots.js +92 -0
  95. package/dist/slots.js.map +1 -0
  96. package/dist/style.css +1 -0
  97. package/dist/styles/base.css +205 -0
  98. package/dist/styles/checkbox.css +36 -0
  99. package/dist/styles/chips.css +71 -0
  100. package/dist/styles/custom-item.css +64 -0
  101. package/dist/styles/footer.css +73 -0
  102. package/dist/styles/groups.css +23 -0
  103. package/dist/styles/meta.css +30 -0
  104. package/dist/styles/radio.css +36 -0
  105. package/dist/styles/select.css +35 -0
  106. package/dist/styles/states.css +22 -0
  107. package/dist/tabs.cjs +2 -0
  108. package/dist/tabs.cjs.map +1 -0
  109. package/dist/tabs.js +132 -0
  110. package/dist/tabs.js.map +1 -0
  111. package/dist/themes/dark.css +96 -0
  112. package/dist/themes/default.css +126 -0
  113. package/dist/themes/high-contrast.css +98 -0
  114. package/dist/types.d.ts +168 -0
  115. package/dist/types.d.ts.map +1 -0
  116. package/dist/useCombo-D_vriwVz.cjs +2 -0
  117. package/dist/useCombo-D_vriwVz.cjs.map +1 -0
  118. package/dist/useCombo-gPeBdkRf.js +887 -0
  119. package/dist/useCombo-gPeBdkRf.js.map +1 -0
  120. package/dist/usePosition-6GfutqGX.cjs +2 -0
  121. package/dist/usePosition-6GfutqGX.cjs.map +1 -0
  122. package/dist/usePosition-DVw8IlwA.js +36 -0
  123. package/dist/usePosition-DVw8IlwA.js.map +1 -0
  124. package/package.json +219 -0
@@ -0,0 +1,71 @@
1
+ /* ------------------------------------------------------------------ */
2
+ /* Multi-select chip trigger */
3
+ /* ------------------------------------------------------------------ */
4
+
5
+ .rzero-combo-trigger--multi {
6
+ flex-wrap: nowrap;
7
+ gap: 4px;
8
+ padding: var(--rzero-combo-input-padding-multi, 4px 8px);
9
+ align-items: flex-start;
10
+ }
11
+
12
+ .rzero-combo-trigger-content {
13
+ display: flex;
14
+ flex-wrap: wrap;
15
+ gap: 4px;
16
+ align-items: center;
17
+ flex: 1 1 0%;
18
+ min-width: 0;
19
+ }
20
+
21
+ .rzero-combo-trigger-content input {
22
+ flex: 1 1 60px;
23
+ min-width: 60px;
24
+ padding: 2px 4px;
25
+ }
26
+
27
+ .rzero-combo-trigger-actions {
28
+ display: flex;
29
+ align-items: center;
30
+ gap: 2px;
31
+ flex-shrink: 0;
32
+ padding-top: 2px;
33
+ }
34
+
35
+ .rzero-combo-chip {
36
+ display: inline-flex;
37
+ align-items: center;
38
+ gap: 2px;
39
+ padding: var(--rzero-combo-chip-padding, 2px 6px);
40
+ background: var(--rzero-combo-chip-bg, #e5e7eb);
41
+ color: var(--rzero-combo-chip-color, #374151);
42
+ border-radius: var(--rzero-combo-chip-radius, 4px);
43
+ font-size: var(--rzero-combo-chip-font-size, 13px);
44
+ line-height: 1.4;
45
+ max-width: 150px;
46
+ }
47
+
48
+ .rzero-combo-chip-label {
49
+ overflow: hidden;
50
+ text-overflow: ellipsis;
51
+ white-space: nowrap;
52
+ }
53
+
54
+ .rzero-combo-chip-remove {
55
+ display: inline-flex;
56
+ align-items: center;
57
+ justify-content: center;
58
+ background: none;
59
+ border: none;
60
+ cursor: pointer;
61
+ padding: 0;
62
+ margin-left: 2px;
63
+ color: var(--rzero-combo-chip-remove-color, #6b7280);
64
+ border-radius: 2px;
65
+ flex-shrink: 0;
66
+ }
67
+
68
+ .rzero-combo-chip-remove:hover {
69
+ color: var(--rzero-combo-chip-remove-hover-color, #111827);
70
+ background: var(--rzero-combo-chip-remove-hover-bg, rgba(0,0,0,0.08));
71
+ }
@@ -0,0 +1,64 @@
1
+ /* ------------------------------------------------------------------ */
2
+ /* Custom item layout — multi-line with icon, title, description, meta */
3
+ /* ------------------------------------------------------------------ */
4
+
5
+ .rzero-combo-custom-item {
6
+ display: flex;
7
+ align-items: flex-start;
8
+ gap: 10px;
9
+ padding: 4px 0;
10
+ width: 100%;
11
+ }
12
+
13
+ .rzero-combo-custom-item-icon {
14
+ flex-shrink: 0;
15
+ width: var(--rzero-combo-custom-item-icon-size, 32px);
16
+ height: var(--rzero-combo-custom-item-icon-size, 32px);
17
+ display: flex;
18
+ align-items: center;
19
+ justify-content: center;
20
+ border-radius: var(--rzero-combo-custom-item-icon-radius, 6px);
21
+ overflow: hidden;
22
+ }
23
+
24
+ .rzero-combo-custom-item-content {
25
+ flex: 1;
26
+ min-width: 0;
27
+ }
28
+
29
+ .rzero-combo-custom-item-title {
30
+ font-weight: 500;
31
+ color: inherit;
32
+ line-height: 1.3;
33
+ overflow: hidden;
34
+ text-overflow: ellipsis;
35
+ white-space: nowrap;
36
+ }
37
+
38
+ .rzero-combo-custom-item-description {
39
+ font-size: var(--rzero-combo-item-description-font-size, 12px);
40
+ color: var(--rzero-combo-item-description-color, #6b7280);
41
+ line-height: 1.3;
42
+ margin-top: 1px;
43
+ overflow: hidden;
44
+ text-overflow: ellipsis;
45
+ white-space: nowrap;
46
+ }
47
+
48
+ .rzero-combo-custom-item-meta {
49
+ flex-shrink: 0;
50
+ font-size: var(--rzero-combo-item-meta-font-size, 12px);
51
+ color: var(--rzero-combo-item-meta-color, #9ca3af);
52
+ align-self: center;
53
+ }
54
+
55
+ .rzero-combo-custom-item-badge {
56
+ display: inline-flex;
57
+ align-items: center;
58
+ padding: 1px 6px;
59
+ border-radius: var(--rzero-combo-badge-radius, 10px);
60
+ background: var(--rzero-combo-badge-bg, #e5e7eb);
61
+ color: var(--rzero-combo-badge-color, #374151);
62
+ font-size: var(--rzero-combo-badge-font-size, 11px);
63
+ font-weight: 500;
64
+ }
@@ -0,0 +1,73 @@
1
+ /* Footer */
2
+ .rzero-combo-footer {
3
+ border-top: var(--rzero-combo-footer-border-top, 1px solid #e5e7eb);
4
+ padding: var(--rzero-combo-footer-padding, 8px 12px);
5
+ font-size: var(--rzero-combo-input-font-size, 14px);
6
+ color: var(--rzero-combo-footer-color, #6b7280);
7
+ }
8
+
9
+ /* Footer actions layout */
10
+ .rzero-combo-footer-actions {
11
+ display: flex;
12
+ align-items: center;
13
+ justify-content: space-between;
14
+ gap: 8px;
15
+ }
16
+
17
+ .rzero-combo-footer-actions-buttons {
18
+ display: flex;
19
+ gap: 6px;
20
+ }
21
+
22
+ .rzero-combo-footer-actions button {
23
+ padding: 4px 12px;
24
+ border: var(--rzero-combo-footer-action-border, 1px solid #d1d5db);
25
+ border-radius: var(--rzero-combo-footer-action-border-radius, 4px);
26
+ background: var(--rzero-combo-footer-action-bg, transparent);
27
+ color: var(--rzero-combo-footer-action-color, #374151);
28
+ font-size: var(--rzero-combo-footer-action-font-size, 13px);
29
+ cursor: pointer;
30
+ transition: background 150ms ease;
31
+ }
32
+
33
+ .rzero-combo-footer-actions button:hover {
34
+ background: var(--rzero-combo-footer-action-hover-bg, #f3f4f6);
35
+ }
36
+
37
+ .rzero-combo-footer-actions button[data-variant="primary"] {
38
+ background: var(--rzero-combo-footer-action-primary-bg, #3b82f6);
39
+ color: var(--rzero-combo-footer-action-primary-color, #ffffff);
40
+ border-color: var(--rzero-combo-footer-action-primary-bg, #3b82f6);
41
+ }
42
+
43
+ .rzero-combo-footer-actions button[data-variant="primary"]:hover {
44
+ background: var(--rzero-combo-footer-action-primary-hover-bg, #2563eb);
45
+ }
46
+
47
+ .rzero-combo-footer-actions button[data-variant="ghost"] {
48
+ border-color: transparent;
49
+ background: transparent;
50
+ }
51
+
52
+ .rzero-combo-footer-actions button[data-variant="ghost"]:hover {
53
+ background: var(--rzero-combo-footer-action-hover-bg, #f3f4f6);
54
+ }
55
+
56
+ .rzero-combo-footer-actions button:disabled {
57
+ opacity: 0.5;
58
+ cursor: not-allowed;
59
+ }
60
+
61
+ /* Footer note */
62
+ .rzero-combo-footer-note {
63
+ font-size: var(--rzero-combo-footer-note-font-size, 12px);
64
+ color: var(--rzero-combo-footer-note-color, #9ca3af);
65
+ }
66
+
67
+ /* Separator between groups */
68
+ .rzero-combo-separator {
69
+ height: 1px;
70
+ background: var(--rzero-combo-separator-color, #e5e7eb);
71
+ margin: 4px 0;
72
+ border: none;
73
+ }
@@ -0,0 +1,23 @@
1
+ /* Sticky group headers */
2
+ .rzero-combo-group-header[data-sticky] {
3
+ position: sticky;
4
+ top: 0;
5
+ background: var(--rzero-combo-popover-bg, #ffffff);
6
+ z-index: 1;
7
+ }
8
+
9
+ /* Group header with count badge */
10
+ .rzero-combo-group-header-count {
11
+ display: inline-flex;
12
+ align-items: center;
13
+ justify-content: center;
14
+ min-width: 18px;
15
+ height: 18px;
16
+ padding: 0 5px;
17
+ border-radius: 9px;
18
+ background: var(--rzero-combo-group-header-count-bg, #e5e7eb);
19
+ color: var(--rzero-combo-group-header-count-color, #6b7280);
20
+ font-size: 10px;
21
+ font-weight: 600;
22
+ margin-left: 6px;
23
+ }
@@ -0,0 +1,30 @@
1
+ /* ------------------------------------------------------------------ */
2
+ /* Meta row: hint text, error text, selection count */
3
+ /* ------------------------------------------------------------------ */
4
+
5
+ .rzero-combo-meta {
6
+ display: flex;
7
+ align-items: baseline;
8
+ justify-content: space-between;
9
+ gap: 8px;
10
+ margin-top: var(--rzero-combo-meta-margin-top, 4px);
11
+ font-size: var(--rzero-combo-meta-font-size, 12px);
12
+ line-height: 1.4;
13
+ }
14
+
15
+ .rzero-combo-hint {
16
+ color: var(--rzero-combo-hint-color, #6b7280);
17
+ }
18
+
19
+ .rzero-combo-error-text {
20
+ color: var(--rzero-combo-error-text-color, #dc2626);
21
+ }
22
+
23
+ .rzero-combo-selection-count {
24
+ color: var(--rzero-combo-selection-count-color, #9ca3af);
25
+ white-space: nowrap;
26
+ margin-left: auto;
27
+ }
28
+ .rzero-combo-selection-count[data-warning] {
29
+ color: var(--rzero-combo-selection-count-warning-color, #f59e0b);
30
+ }
@@ -0,0 +1,36 @@
1
+ /* ------------------------------------------------------------------ */
2
+ /* Radio variant — pure CSS via ::before pseudo-element */
3
+ /* ------------------------------------------------------------------ */
4
+
5
+ .rzero-combo-item[data-variant="radio"] {
6
+ gap: 8px;
7
+ justify-content: flex-start;
8
+ }
9
+
10
+ .rzero-combo-item[data-variant="radio"]::before {
11
+ content: '';
12
+ flex-shrink: 0;
13
+ width: var(--rzero-combo-radio-size, 16px);
14
+ height: var(--rzero-combo-radio-size, 16px);
15
+ border: 2px solid var(--rzero-combo-radio-border, #d1d5db);
16
+ border-radius: 50%;
17
+ background: var(--rzero-combo-radio-bg, #ffffff);
18
+ transition: background 150ms ease, border-color 150ms ease;
19
+ }
20
+
21
+ .rzero-combo-item[data-variant="radio"][data-selected]::before {
22
+ border-color: var(--rzero-combo-radio-checked-border, #3b82f6);
23
+ background: radial-gradient(
24
+ circle,
25
+ var(--rzero-combo-radio-checked-dot, #3b82f6) 35%,
26
+ transparent 36%
27
+ );
28
+ }
29
+
30
+ .rzero-combo-item[data-variant="radio"][data-highlighted]::before {
31
+ border-color: var(--rzero-combo-radio-hover-border, #9ca3af);
32
+ }
33
+
34
+ .rzero-combo-item[data-variant="radio"][data-selected][data-highlighted]::before {
35
+ border-color: var(--rzero-combo-radio-checked-border, #3b82f6);
36
+ }
@@ -0,0 +1,35 @@
1
+ /* Select variant trigger */
2
+ .rzero-combo-select-trigger {
3
+ display: flex;
4
+ align-items: center;
5
+ justify-content: space-between;
6
+ gap: 8px;
7
+ width: 100%;
8
+ border: var(--rzero-combo-input-border, 1px solid #d1d5db);
9
+ border-radius: var(--rzero-combo-input-border-radius, 6px);
10
+ background: var(--rzero-combo-input-bg, #ffffff);
11
+ padding: var(--rzero-combo-input-padding, 8px 12px);
12
+ min-height: var(--rzero-combo-input-height, 40px);
13
+ font-size: var(--rzero-combo-input-font-size, 14px);
14
+ color: var(--rzero-combo-input-color, #111827);
15
+ cursor: pointer;
16
+ transition: box-shadow 150ms ease, border-color 150ms ease;
17
+ }
18
+
19
+ .rzero-combo-select-trigger:focus-visible {
20
+ box-shadow: var(--rzero-combo-input-focus-ring, 0 0 0 2px #3b82f6);
21
+ outline: none;
22
+ }
23
+
24
+ .rzero-combo-select-trigger:disabled {
25
+ opacity: 0.5;
26
+ cursor: not-allowed;
27
+ }
28
+
29
+ .rzero-combo-select-value {
30
+ flex: 1;
31
+ text-align: left;
32
+ overflow: hidden;
33
+ text-overflow: ellipsis;
34
+ white-space: nowrap;
35
+ }
@@ -0,0 +1,22 @@
1
+ /* Empty / loading states */
2
+ .rzero-combo-empty,
3
+ .rzero-combo-loading {
4
+ display: flex;
5
+ align-items: center;
6
+ justify-content: center;
7
+ min-height: var(--rzero-combo-item-height, 36px);
8
+ padding: var(--rzero-combo-item-padding, 0 12px);
9
+ color: var(--rzero-combo-icon-muted-color, #9ca3af);
10
+ font-size: var(--rzero-combo-input-font-size, 14px);
11
+ }
12
+
13
+ /* Error state */
14
+ .rzero-combo-error {
15
+ display: flex;
16
+ align-items: center;
17
+ justify-content: center;
18
+ min-height: var(--rzero-combo-item-height, 36px);
19
+ padding: var(--rzero-combo-item-padding, 0 12px);
20
+ color: var(--rzero-combo-error-color, #dc2626);
21
+ font-size: var(--rzero-combo-input-font-size, 14px);
22
+ }
package/dist/tabs.cjs ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("react/jsx-runtime"),d=require("react"),k=require("./Combo-Cx3kSkop.cjs");function w(v){const{tabs:a,defaultActiveTab:f,activeTab:l,onTabChange:m,renderTabLabel:g,renderFooter:h,...e}=v,c=d.useId(),T=d.useRef(null),[y,p]=d.useState(()=>f??a[0]?.id??""),o=l!==void 0?l:y,u=d.useCallback(t=>{l===void 0&&p(t),m?.(t)},[l,m]),I=d.useMemo(()=>a.find(n=>n.id===o)?.items??[],[a,o]),C=d.useCallback(t=>{const n=a.filter(x=>!x.disabled),s=n.findIndex(x=>x.id===o);if(s===-1)return;let i=-1;if(t.key==="ArrowRight")i=(s+1)%n.length;else if(t.key==="ArrowLeft")i=(s-1+n.length)%n.length;else if(t.key==="Home")i=0;else if(t.key==="End")i=n.length-1;else return;t.preventDefault();const b=n[i];u(b.id),T.current?.querySelector(`[data-tab-id="${b.id}"]`)?.focus()},[a,o,u]),j=d.useCallback(t=>{if(!t.ctrlKey&&!t.metaKey)return;const n=a.filter(b=>!b.disabled),s=n.findIndex(b=>b.id===o);if(s===-1)return;let i=-1;if(t.key==="ArrowRight")i=(s+1)%n.length;else if(t.key==="ArrowLeft")i=(s-1+n.length)%n.length;else return;t.preventDefault(),u(n[i].id)},[a,o,u]);return r.jsx(k.Combo,{...e,items:I,renderFooter:h,onInputKeyDown:j,renderListHeader:()=>r.jsx(A,{tabs:a,activeTabId:o,baseId:c,tabListRef:T,onTabChange:u,onKeyDown:C,renderTabLabel:g})})}function A({tabs:v,activeTabId:a,baseId:f,tabListRef:l,onTabChange:m,onKeyDown:g,renderTabLabel:h}){return r.jsx("div",{ref:l,className:"rzero-combo-tabs",role:"tablist","aria-label":"Item categories",onMouseDown:e=>e.preventDefault(),children:v.map(e=>{const c=e.id===a;return r.jsx("button",{type:"button",role:"tab",id:`${f}-tab-${e.id}`,"aria-selected":c,"aria-controls":`${f}-panel-${e.id}`,"aria-disabled":e.disabled||void 0,"data-tab-id":e.id,"data-active":c||void 0,"data-disabled":e.disabled||void 0,className:"rzero-combo-tab",tabIndex:c?0:-1,onClick:()=>{e.disabled||m(e.id)},onKeyDown:g,children:h?h(e,c):r.jsxs(r.Fragment,{children:[e.icon&&r.jsx("span",{className:"rzero-combo-tab-icon","aria-hidden":"true",children:e.icon}),r.jsx("span",{children:e.label}),e.badge!==void 0&&r.jsx("span",{className:"rzero-combo-tab-badge",children:e.badge})]})},e.id)})})}exports.TabbedCombo=w;
2
+ //# sourceMappingURL=tabs.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs.cjs","sources":["../src/components/tabs/TabbedCombo.tsx"],"sourcesContent":["import { useState, useCallback, useRef, useMemo, useId } from 'react';\nimport { Combo } from '../Combo';\nimport type { ComboProps } from '../Combo';\nimport type { ReactNode, KeyboardEvent, RefObject } from 'react';\n\nexport interface TabConfig<T> {\n /** Unique tab identifier */\n id: string;\n /** Tab display label */\n label: string;\n /** Items belonging to this tab */\n items: T[];\n /** Optional icon for the tab */\n icon?: ReactNode;\n /** Optional badge (e.g. count) */\n badge?: ReactNode;\n /** Disable this tab */\n disabled?: boolean;\n}\n\nexport interface TabbedComboProps<T>\n extends Omit<ComboProps<T>, 'items' | 'groups'> {\n /** Tab configuration with per-tab items */\n tabs: TabConfig<T>[];\n /** Default active tab ID (uncontrolled) */\n defaultActiveTab?: string;\n /** Active tab ID (controlled) */\n activeTab?: string;\n /** Callback when active tab changes */\n onTabChange?: (tabId: string) => void;\n /** Custom tab label renderer */\n renderTabLabel?: (tab: TabConfig<T>, isActive: boolean) => ReactNode;\n}\n\n/**\n * Combo with a tab strip inside the popover for categorized content.\n * Each tab filters to its own set of items.\n *\n * @example\n * ```tsx\n * <TabbedCombo\n * tabs={[\n * { id: 'fruits', label: 'Fruits', items: fruits },\n * { id: 'vegetables', label: 'Vegetables', items: vegetables },\n * ]}\n * placeholder=\"Search food...\"\n * />\n * ```\n */\nexport function TabbedCombo<T>(props: TabbedComboProps<T>) {\n const {\n tabs,\n defaultActiveTab,\n activeTab: controlledActiveTab,\n onTabChange,\n renderTabLabel,\n renderFooter,\n ...comboProps\n } = props;\n\n const baseId = useId();\n const tabListRef = useRef<HTMLDivElement>(null);\n\n // Active tab state (controlled or uncontrolled)\n const [internalActiveTab, setInternalActiveTab] = useState(\n () => defaultActiveTab ?? tabs[0]?.id ?? '',\n );\n\n const activeTabId =\n controlledActiveTab !== undefined ? controlledActiveTab : internalActiveTab;\n\n const handleTabChange = useCallback(\n (tabId: string) => {\n if (controlledActiveTab === undefined) {\n setInternalActiveTab(tabId);\n }\n onTabChange?.(tabId);\n },\n [controlledActiveTab, onTabChange],\n );\n\n // Get items for the active tab\n const activeItems = useMemo(() => {\n const tab = tabs.find((t) => t.id === activeTabId);\n return tab?.items ?? [];\n }, [tabs, activeTabId]);\n\n // Tab keyboard navigation (Left/Right arrow)\n const handleTabKeyDown = useCallback(\n (e: KeyboardEvent) => {\n const enabledTabs = tabs.filter((t) => !t.disabled);\n const currentIdx = enabledTabs.findIndex((t) => t.id === activeTabId);\n if (currentIdx === -1) return;\n\n let nextIdx = -1;\n if (e.key === 'ArrowRight') {\n nextIdx = (currentIdx + 1) % enabledTabs.length;\n } else if (e.key === 'ArrowLeft') {\n nextIdx = (currentIdx - 1 + enabledTabs.length) % enabledTabs.length;\n } else if (e.key === 'Home') {\n nextIdx = 0;\n } else if (e.key === 'End') {\n nextIdx = enabledTabs.length - 1;\n } else {\n return;\n }\n\n e.preventDefault();\n const nextTab = enabledTabs[nextIdx];\n handleTabChange(nextTab.id);\n\n // Focus the activated tab button\n const tabEl = tabListRef.current?.querySelector(\n `[data-tab-id=\"${nextTab.id}\"]`,\n ) as HTMLButtonElement | null;\n tabEl?.focus();\n },\n [tabs, activeTabId, handleTabChange],\n );\n\n // Ctrl+Arrow from the input switches tabs without losing focus\n const handleInputKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (!e.ctrlKey && !e.metaKey) return;\n\n const enabledTabs = tabs.filter((t) => !t.disabled);\n const currentIdx = enabledTabs.findIndex((t) => t.id === activeTabId);\n if (currentIdx === -1) return;\n\n let nextIdx = -1;\n if (e.key === 'ArrowRight') {\n nextIdx = (currentIdx + 1) % enabledTabs.length;\n } else if (e.key === 'ArrowLeft') {\n nextIdx = (currentIdx - 1 + enabledTabs.length) % enabledTabs.length;\n } else {\n return;\n }\n\n e.preventDefault();\n handleTabChange(enabledTabs[nextIdx].id);\n },\n [tabs, activeTabId, handleTabChange],\n );\n\n return (\n <Combo<T>\n {...comboProps}\n items={activeItems}\n renderFooter={renderFooter}\n onInputKeyDown={handleInputKeyDown}\n renderListHeader={() => (\n <TabStrip\n tabs={tabs}\n activeTabId={activeTabId}\n baseId={baseId}\n tabListRef={tabListRef}\n onTabChange={handleTabChange}\n onKeyDown={handleTabKeyDown}\n renderTabLabel={renderTabLabel}\n />\n )}\n />\n );\n}\n\n// Internal TabStrip component\nfunction TabStrip<T>({\n tabs,\n activeTabId,\n baseId,\n tabListRef,\n onTabChange,\n onKeyDown,\n renderTabLabel,\n}: {\n tabs: TabConfig<T>[];\n activeTabId: string;\n baseId: string;\n tabListRef: RefObject<HTMLDivElement | null>;\n onTabChange: (tabId: string) => void;\n onKeyDown: (e: KeyboardEvent) => void;\n renderTabLabel?: (tab: TabConfig<T>, isActive: boolean) => ReactNode;\n}) {\n return (\n <div\n ref={tabListRef as RefObject<HTMLDivElement>}\n className=\"rzero-combo-tabs\"\n role=\"tablist\"\n aria-label=\"Item categories\"\n onMouseDown={(e) => e.preventDefault()}\n >\n {tabs.map((tab) => {\n const isActive = tab.id === activeTabId;\n return (\n <button\n key={tab.id}\n type=\"button\"\n role=\"tab\"\n id={`${baseId}-tab-${tab.id}`}\n aria-selected={isActive}\n aria-controls={`${baseId}-panel-${tab.id}`}\n aria-disabled={tab.disabled || undefined}\n data-tab-id={tab.id}\n data-active={isActive || undefined}\n data-disabled={tab.disabled || undefined}\n className=\"rzero-combo-tab\"\n tabIndex={isActive ? 0 : -1}\n onClick={() => {\n if (tab.disabled) return;\n onTabChange(tab.id);\n }}\n onKeyDown={onKeyDown}\n >\n {renderTabLabel ? (\n renderTabLabel(tab, isActive)\n ) : (\n <>\n {tab.icon && (\n <span className=\"rzero-combo-tab-icon\" aria-hidden=\"true\">\n {tab.icon}\n </span>\n )}\n <span>{tab.label}</span>\n {tab.badge !== undefined && (\n <span className=\"rzero-combo-tab-badge\">{tab.badge}</span>\n )}\n </>\n )}\n </button>\n );\n })}\n </div>\n );\n}\n"],"names":["TabbedCombo","props","tabs","defaultActiveTab","controlledActiveTab","onTabChange","renderTabLabel","renderFooter","comboProps","baseId","useId","tabListRef","useRef","internalActiveTab","setInternalActiveTab","useState","activeTabId","handleTabChange","useCallback","tabId","activeItems","useMemo","t","handleTabKeyDown","e","enabledTabs","currentIdx","nextIdx","nextTab","handleInputKeyDown","jsx","Combo","TabStrip","onKeyDown","tab","isActive","jsxs","Fragment"],"mappings":"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"}
package/dist/tabs.js ADDED
@@ -0,0 +1,132 @@
1
+ import { jsx as d, jsxs as k, Fragment as C } from "react/jsx-runtime";
2
+ import { useId as D, useRef as K, useState as $, useCallback as p, useMemo as z } from "react";
3
+ import { C as L } from "./Combo-qs6_L512.js";
4
+ function F(h) {
5
+ const {
6
+ tabs: i,
7
+ defaultActiveTab: u,
8
+ activeTab: s,
9
+ onTabChange: f,
10
+ renderTabLabel: v,
11
+ renderFooter: m,
12
+ ...e
13
+ } = h, l = D(), T = K(null), [y, x] = $(
14
+ () => u ?? i[0]?.id ?? ""
15
+ ), a = s !== void 0 ? s : y, b = p(
16
+ (t) => {
17
+ s === void 0 && x(t), f?.(t);
18
+ },
19
+ [s, f]
20
+ ), I = z(() => i.find((n) => n.id === a)?.items ?? [], [i, a]), w = p(
21
+ (t) => {
22
+ const n = i.filter((g) => !g.disabled), o = n.findIndex((g) => g.id === a);
23
+ if (o === -1) return;
24
+ let r = -1;
25
+ if (t.key === "ArrowRight")
26
+ r = (o + 1) % n.length;
27
+ else if (t.key === "ArrowLeft")
28
+ r = (o - 1 + n.length) % n.length;
29
+ else if (t.key === "Home")
30
+ r = 0;
31
+ else if (t.key === "End")
32
+ r = n.length - 1;
33
+ else
34
+ return;
35
+ t.preventDefault();
36
+ const c = n[r];
37
+ b(c.id), T.current?.querySelector(
38
+ `[data-tab-id="${c.id}"]`
39
+ )?.focus();
40
+ },
41
+ [i, a, b]
42
+ ), A = p(
43
+ (t) => {
44
+ if (!t.ctrlKey && !t.metaKey) return;
45
+ const n = i.filter((c) => !c.disabled), o = n.findIndex((c) => c.id === a);
46
+ if (o === -1) return;
47
+ let r = -1;
48
+ if (t.key === "ArrowRight")
49
+ r = (o + 1) % n.length;
50
+ else if (t.key === "ArrowLeft")
51
+ r = (o - 1 + n.length) % n.length;
52
+ else
53
+ return;
54
+ t.preventDefault(), b(n[r].id);
55
+ },
56
+ [i, a, b]
57
+ );
58
+ return /* @__PURE__ */ d(
59
+ L,
60
+ {
61
+ ...e,
62
+ items: I,
63
+ renderFooter: m,
64
+ onInputKeyDown: A,
65
+ renderListHeader: () => /* @__PURE__ */ d(
66
+ N,
67
+ {
68
+ tabs: i,
69
+ activeTabId: a,
70
+ baseId: l,
71
+ tabListRef: T,
72
+ onTabChange: b,
73
+ onKeyDown: w,
74
+ renderTabLabel: v
75
+ }
76
+ )
77
+ }
78
+ );
79
+ }
80
+ function N({
81
+ tabs: h,
82
+ activeTabId: i,
83
+ baseId: u,
84
+ tabListRef: s,
85
+ onTabChange: f,
86
+ onKeyDown: v,
87
+ renderTabLabel: m
88
+ }) {
89
+ return /* @__PURE__ */ d(
90
+ "div",
91
+ {
92
+ ref: s,
93
+ className: "rzero-combo-tabs",
94
+ role: "tablist",
95
+ "aria-label": "Item categories",
96
+ onMouseDown: (e) => e.preventDefault(),
97
+ children: h.map((e) => {
98
+ const l = e.id === i;
99
+ return /* @__PURE__ */ d(
100
+ "button",
101
+ {
102
+ type: "button",
103
+ role: "tab",
104
+ id: `${u}-tab-${e.id}`,
105
+ "aria-selected": l,
106
+ "aria-controls": `${u}-panel-${e.id}`,
107
+ "aria-disabled": e.disabled || void 0,
108
+ "data-tab-id": e.id,
109
+ "data-active": l || void 0,
110
+ "data-disabled": e.disabled || void 0,
111
+ className: "rzero-combo-tab",
112
+ tabIndex: l ? 0 : -1,
113
+ onClick: () => {
114
+ e.disabled || f(e.id);
115
+ },
116
+ onKeyDown: v,
117
+ children: m ? m(e, l) : /* @__PURE__ */ k(C, { children: [
118
+ e.icon && /* @__PURE__ */ d("span", { className: "rzero-combo-tab-icon", "aria-hidden": "true", children: e.icon }),
119
+ /* @__PURE__ */ d("span", { children: e.label }),
120
+ e.badge !== void 0 && /* @__PURE__ */ d("span", { className: "rzero-combo-tab-badge", children: e.badge })
121
+ ] })
122
+ },
123
+ e.id
124
+ );
125
+ })
126
+ }
127
+ );
128
+ }
129
+ export {
130
+ F as TabbedCombo
131
+ };
132
+ //# sourceMappingURL=tabs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs.js","sources":["../src/components/tabs/TabbedCombo.tsx"],"sourcesContent":["import { useState, useCallback, useRef, useMemo, useId } from 'react';\nimport { Combo } from '../Combo';\nimport type { ComboProps } from '../Combo';\nimport type { ReactNode, KeyboardEvent, RefObject } from 'react';\n\nexport interface TabConfig<T> {\n /** Unique tab identifier */\n id: string;\n /** Tab display label */\n label: string;\n /** Items belonging to this tab */\n items: T[];\n /** Optional icon for the tab */\n icon?: ReactNode;\n /** Optional badge (e.g. count) */\n badge?: ReactNode;\n /** Disable this tab */\n disabled?: boolean;\n}\n\nexport interface TabbedComboProps<T>\n extends Omit<ComboProps<T>, 'items' | 'groups'> {\n /** Tab configuration with per-tab items */\n tabs: TabConfig<T>[];\n /** Default active tab ID (uncontrolled) */\n defaultActiveTab?: string;\n /** Active tab ID (controlled) */\n activeTab?: string;\n /** Callback when active tab changes */\n onTabChange?: (tabId: string) => void;\n /** Custom tab label renderer */\n renderTabLabel?: (tab: TabConfig<T>, isActive: boolean) => ReactNode;\n}\n\n/**\n * Combo with a tab strip inside the popover for categorized content.\n * Each tab filters to its own set of items.\n *\n * @example\n * ```tsx\n * <TabbedCombo\n * tabs={[\n * { id: 'fruits', label: 'Fruits', items: fruits },\n * { id: 'vegetables', label: 'Vegetables', items: vegetables },\n * ]}\n * placeholder=\"Search food...\"\n * />\n * ```\n */\nexport function TabbedCombo<T>(props: TabbedComboProps<T>) {\n const {\n tabs,\n defaultActiveTab,\n activeTab: controlledActiveTab,\n onTabChange,\n renderTabLabel,\n renderFooter,\n ...comboProps\n } = props;\n\n const baseId = useId();\n const tabListRef = useRef<HTMLDivElement>(null);\n\n // Active tab state (controlled or uncontrolled)\n const [internalActiveTab, setInternalActiveTab] = useState(\n () => defaultActiveTab ?? tabs[0]?.id ?? '',\n );\n\n const activeTabId =\n controlledActiveTab !== undefined ? controlledActiveTab : internalActiveTab;\n\n const handleTabChange = useCallback(\n (tabId: string) => {\n if (controlledActiveTab === undefined) {\n setInternalActiveTab(tabId);\n }\n onTabChange?.(tabId);\n },\n [controlledActiveTab, onTabChange],\n );\n\n // Get items for the active tab\n const activeItems = useMemo(() => {\n const tab = tabs.find((t) => t.id === activeTabId);\n return tab?.items ?? [];\n }, [tabs, activeTabId]);\n\n // Tab keyboard navigation (Left/Right arrow)\n const handleTabKeyDown = useCallback(\n (e: KeyboardEvent) => {\n const enabledTabs = tabs.filter((t) => !t.disabled);\n const currentIdx = enabledTabs.findIndex((t) => t.id === activeTabId);\n if (currentIdx === -1) return;\n\n let nextIdx = -1;\n if (e.key === 'ArrowRight') {\n nextIdx = (currentIdx + 1) % enabledTabs.length;\n } else if (e.key === 'ArrowLeft') {\n nextIdx = (currentIdx - 1 + enabledTabs.length) % enabledTabs.length;\n } else if (e.key === 'Home') {\n nextIdx = 0;\n } else if (e.key === 'End') {\n nextIdx = enabledTabs.length - 1;\n } else {\n return;\n }\n\n e.preventDefault();\n const nextTab = enabledTabs[nextIdx];\n handleTabChange(nextTab.id);\n\n // Focus the activated tab button\n const tabEl = tabListRef.current?.querySelector(\n `[data-tab-id=\"${nextTab.id}\"]`,\n ) as HTMLButtonElement | null;\n tabEl?.focus();\n },\n [tabs, activeTabId, handleTabChange],\n );\n\n // Ctrl+Arrow from the input switches tabs without losing focus\n const handleInputKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (!e.ctrlKey && !e.metaKey) return;\n\n const enabledTabs = tabs.filter((t) => !t.disabled);\n const currentIdx = enabledTabs.findIndex((t) => t.id === activeTabId);\n if (currentIdx === -1) return;\n\n let nextIdx = -1;\n if (e.key === 'ArrowRight') {\n nextIdx = (currentIdx + 1) % enabledTabs.length;\n } else if (e.key === 'ArrowLeft') {\n nextIdx = (currentIdx - 1 + enabledTabs.length) % enabledTabs.length;\n } else {\n return;\n }\n\n e.preventDefault();\n handleTabChange(enabledTabs[nextIdx].id);\n },\n [tabs, activeTabId, handleTabChange],\n );\n\n return (\n <Combo<T>\n {...comboProps}\n items={activeItems}\n renderFooter={renderFooter}\n onInputKeyDown={handleInputKeyDown}\n renderListHeader={() => (\n <TabStrip\n tabs={tabs}\n activeTabId={activeTabId}\n baseId={baseId}\n tabListRef={tabListRef}\n onTabChange={handleTabChange}\n onKeyDown={handleTabKeyDown}\n renderTabLabel={renderTabLabel}\n />\n )}\n />\n );\n}\n\n// Internal TabStrip component\nfunction TabStrip<T>({\n tabs,\n activeTabId,\n baseId,\n tabListRef,\n onTabChange,\n onKeyDown,\n renderTabLabel,\n}: {\n tabs: TabConfig<T>[];\n activeTabId: string;\n baseId: string;\n tabListRef: RefObject<HTMLDivElement | null>;\n onTabChange: (tabId: string) => void;\n onKeyDown: (e: KeyboardEvent) => void;\n renderTabLabel?: (tab: TabConfig<T>, isActive: boolean) => ReactNode;\n}) {\n return (\n <div\n ref={tabListRef as RefObject<HTMLDivElement>}\n className=\"rzero-combo-tabs\"\n role=\"tablist\"\n aria-label=\"Item categories\"\n onMouseDown={(e) => e.preventDefault()}\n >\n {tabs.map((tab) => {\n const isActive = tab.id === activeTabId;\n return (\n <button\n key={tab.id}\n type=\"button\"\n role=\"tab\"\n id={`${baseId}-tab-${tab.id}`}\n aria-selected={isActive}\n aria-controls={`${baseId}-panel-${tab.id}`}\n aria-disabled={tab.disabled || undefined}\n data-tab-id={tab.id}\n data-active={isActive || undefined}\n data-disabled={tab.disabled || undefined}\n className=\"rzero-combo-tab\"\n tabIndex={isActive ? 0 : -1}\n onClick={() => {\n if (tab.disabled) return;\n onTabChange(tab.id);\n }}\n onKeyDown={onKeyDown}\n >\n {renderTabLabel ? (\n renderTabLabel(tab, isActive)\n ) : (\n <>\n {tab.icon && (\n <span className=\"rzero-combo-tab-icon\" aria-hidden=\"true\">\n {tab.icon}\n </span>\n )}\n <span>{tab.label}</span>\n {tab.badge !== undefined && (\n <span className=\"rzero-combo-tab-badge\">{tab.badge}</span>\n )}\n </>\n )}\n </button>\n );\n })}\n </div>\n );\n}\n"],"names":["TabbedCombo","props","tabs","defaultActiveTab","controlledActiveTab","onTabChange","renderTabLabel","renderFooter","comboProps","baseId","useId","tabListRef","useRef","internalActiveTab","setInternalActiveTab","useState","activeTabId","handleTabChange","useCallback","tabId","activeItems","useMemo","t","handleTabKeyDown","e","enabledTabs","currentIdx","nextIdx","nextTab","handleInputKeyDown","jsx","Combo","TabStrip","onKeyDown","tab","isActive","jsxs","Fragment"],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ [data-rzero-theme="dark"] {
2
+ /* Input */
3
+ --rzero-combo-input-bg: #1f2937;
4
+ --rzero-combo-input-border: 1px solid #374151;
5
+ --rzero-combo-input-color: #f9fafb;
6
+ --rzero-combo-input-hover-border: #60a5fa;
7
+ --rzero-combo-input-focus-ring: 0 0 0 2px #60a5fa;
8
+
9
+ /* Label & hint */
10
+ --rzero-combo-label-color: #e2e8f0;
11
+ --rzero-combo-hint-color: #94a3b8;
12
+
13
+ /* Popover */
14
+ --rzero-combo-popover-bg: #1f2937;
15
+ --rzero-combo-popover-border: 1px solid #374151;
16
+ --rzero-combo-popover-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
17
+
18
+ /* Items */
19
+ --rzero-combo-item-color: #e5e7eb;
20
+ --rzero-combo-item-hover-bg: #374151;
21
+ --rzero-combo-item-selected-bg: #1e3a5f;
22
+ --rzero-combo-item-selected-color: #93c5fd;
23
+ --rzero-combo-item-highlighted-bg: #1e3a5f;
24
+ --rzero-combo-item-description-color: #94a3b8;
25
+ --rzero-combo-item-meta-color: #64748b;
26
+
27
+ /* Group headers */
28
+ --rzero-combo-group-header-color: #94a3b8;
29
+ --rzero-combo-group-header-count-bg: #374151;
30
+ --rzero-combo-group-header-count-color: #94a3b8;
31
+
32
+ /* Icons */
33
+ --rzero-combo-icon-muted-color: #64748b;
34
+
35
+ /* Chips */
36
+ --rzero-combo-chip-bg: #312e81;
37
+ --rzero-combo-chip-color: #c7d2fe;
38
+ --rzero-combo-chip-remove-color: #94a3b8;
39
+ --rzero-combo-chip-remove-hover-color: #f1f5f9;
40
+ --rzero-combo-chip-remove-hover-bg: rgba(255, 255, 255, 0.1);
41
+
42
+ /* Error states */
43
+ --rzero-combo-error-border: #f87171;
44
+ --rzero-combo-error-color: #f87171;
45
+ --rzero-combo-error-text-color: #fca5a5;
46
+ --rzero-combo-error-focus-ring: 0 0 0 2px #f87171;
47
+
48
+ /* Selection count */
49
+ --rzero-combo-selection-count-color: #94a3b8;
50
+ --rzero-combo-selection-count-warning-color: #fbbf24;
51
+
52
+ /* Checkbox variant */
53
+ --rzero-combo-checkbox-border: #4b5563;
54
+ --rzero-combo-checkbox-bg: #374151;
55
+ --rzero-combo-checkbox-checked-bg: #60a5fa;
56
+ --rzero-combo-checkbox-checked-border: #60a5fa;
57
+ --rzero-combo-checkbox-hover-border: #94a3b8;
58
+
59
+ /* Radio variant */
60
+ --rzero-combo-radio-border: #4b5563;
61
+ --rzero-combo-radio-bg: #374151;
62
+ --rzero-combo-radio-checked-border: #60a5fa;
63
+ --rzero-combo-radio-checked-dot: #60a5fa;
64
+ --rzero-combo-radio-hover-border: #94a3b8;
65
+
66
+ /* Badges */
67
+ --rzero-combo-badge-bg: #374151;
68
+ --rzero-combo-badge-color: #d1d5db;
69
+
70
+ /* Footer */
71
+ --rzero-combo-footer-border-top: 1px solid #374151;
72
+ --rzero-combo-footer-color: #94a3b8;
73
+ --rzero-combo-footer-note-color: #64748b;
74
+ --rzero-combo-footer-action-border: 1px solid #4b5563;
75
+ --rzero-combo-footer-action-color: #e5e7eb;
76
+ --rzero-combo-footer-action-hover-bg: #374151;
77
+ --rzero-combo-footer-action-primary-bg: #60a5fa;
78
+ --rzero-combo-footer-action-primary-color: #111827;
79
+ --rzero-combo-footer-action-primary-hover-bg: #3b82f6;
80
+
81
+ /* Separator */
82
+ --rzero-combo-separator-color: #374151;
83
+
84
+ /* Tabs */
85
+ --rzero-combo-tab-border-color: #374151;
86
+ --rzero-combo-tab-color: #94a3b8;
87
+ --rzero-combo-tab-hover-bg: #374151;
88
+ --rzero-combo-tab-hover-color: #e5e7eb;
89
+ --rzero-combo-tab-focus-ring: #60a5fa;
90
+ --rzero-combo-tab-active-color: #f9fafb;
91
+ --rzero-combo-tab-active-border-color: #60a5fa;
92
+ --rzero-combo-tab-badge-bg: #374151;
93
+ --rzero-combo-tab-badge-color: #94a3b8;
94
+ --rzero-combo-tab-badge-active-bg: #1e3a5f;
95
+ --rzero-combo-tab-badge-active-color: #93c5fd;
96
+ }