@kenlo/sds-design-system 0.9.9

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 (246) hide show
  1. package/README.md +129 -0
  2. package/components/Accordion/Accordion.css +186 -0
  3. package/components/Accordion/accordion-variants.js +17 -0
  4. package/components/Accordion/accordion.marko +264 -0
  5. package/components/AccordionGroup/AccordionGroup.css +35 -0
  6. package/components/AccordionGroup/accordion-group.marko +17 -0
  7. package/components/Alert/Alert.css +104 -0
  8. package/components/Alert/alert-variants.js +35 -0
  9. package/components/Alert/alert.marko +94 -0
  10. package/components/AttachmentList/AttachmentList.css +92 -0
  11. package/components/AttachmentList/attachment-list.marko +51 -0
  12. package/components/AttachmentList/attachment-row.marko +167 -0
  13. package/components/Avatar/Avatar.css +110 -0
  14. package/components/Avatar/avatar-variants.js +12 -0
  15. package/components/Avatar/avatar.marko +71 -0
  16. package/components/Badge/Badge.css +381 -0
  17. package/components/Badge/badge-dismissible.marko +143 -0
  18. package/components/Badge/badge-variants.js +35 -0
  19. package/components/Badge/badge.marko +134 -0
  20. package/components/BottomSheet/BottomSheet.css +300 -0
  21. package/components/BottomSheet/bottom-sheet.marko +340 -0
  22. package/components/BulkActionBar/BulkActionBar.css +88 -0
  23. package/components/BulkActionBar/bulk-action-bar.marko +186 -0
  24. package/components/Button/Button.css +340 -0
  25. package/components/Button/button-variants.js +25 -0
  26. package/components/Button/button.marko +139 -0
  27. package/components/Card/Card.css +391 -0
  28. package/components/Card/card-body.marko +5 -0
  29. package/components/Card/card-collapsible.marko +94 -0
  30. package/components/Card/card-footer.marko +12 -0
  31. package/components/Card/card-header.marko +30 -0
  32. package/components/Card/card-info-pair.marko +17 -0
  33. package/components/Card/card-info-row.marko +12 -0
  34. package/components/Card/card-section.marko +14 -0
  35. package/components/Card/card-variants.js +24 -0
  36. package/components/Card/card.marko +124 -0
  37. package/components/Checkbox/Checkbox.css +249 -0
  38. package/components/Checkbox/checkbox-variants.js +21 -0
  39. package/components/Checkbox/checkbox.marko +128 -0
  40. package/components/ColorPicker/ColorPicker.css +253 -0
  41. package/components/ColorPicker/color-maths.js +104 -0
  42. package/components/ColorPicker/color-picker.marko +474 -0
  43. package/components/DatePicker/DatePicker.css +429 -0
  44. package/components/DatePicker/date-logic.js +225 -0
  45. package/components/DatePicker/date-picker.marko +492 -0
  46. package/components/Drawer/Drawer.css +174 -0
  47. package/components/Drawer/drawer.marko +262 -0
  48. package/components/Floating/Floating.css +28 -0
  49. package/components/Floating/floating-position.js +251 -0
  50. package/components/Floating/floating.marko +291 -0
  51. package/components/Form/Form.css +15 -0
  52. package/components/Form/form-context.js +69 -0
  53. package/components/Form/form.marko +73 -0
  54. package/components/GradientPicker/GradientPicker.css +179 -0
  55. package/components/GradientPicker/gradient-picker.marko +398 -0
  56. package/components/Grid/Grid.css +161 -0
  57. package/components/Grid/col.marko +36 -0
  58. package/components/Grid/grid-variants.js +43 -0
  59. package/components/Grid/grid.marko +25 -0
  60. package/components/Header/Header.css +457 -0
  61. package/components/Header/header.marko +523 -0
  62. package/components/Icon/Icon.css +44 -0
  63. package/components/Icon/icon-colors.js +31 -0
  64. package/components/Icon/icon.marko +56 -0
  65. package/components/IconButton/IconButton.css +85 -0
  66. package/components/IconButton/icon-button-variants.js +18 -0
  67. package/components/IconButton/icon-button.marko +103 -0
  68. package/components/Incrementer/Incrementer.css +94 -0
  69. package/components/Incrementer/incrementer.marko +70 -0
  70. package/components/Input/Input.css +547 -0
  71. package/components/Input/input-countries.js +109 -0
  72. package/components/Input/input-masks.js +218 -0
  73. package/components/Input/input-types.js +194 -0
  74. package/components/Input/input.marko +530 -0
  75. package/components/Menu/Menu.css +651 -0
  76. package/components/Menu/menu-variants.js +89 -0
  77. package/components/Menu/menu.marko +560 -0
  78. package/components/Modal/Modal.css +160 -0
  79. package/components/Modal/modal.marko +296 -0
  80. package/components/Nav/Nav.css +298 -0
  81. package/components/Nav/nav-variants.js +23 -0
  82. package/components/Nav/nav.marko +164 -0
  83. package/components/Notification/Notification.css +78 -0
  84. package/components/Notification/notification-variants.js +30 -0
  85. package/components/Notification/notification.marko +55 -0
  86. package/components/Pagination/Pagination.css +78 -0
  87. package/components/Pagination/pagination-pages.js +66 -0
  88. package/components/Pagination/pagination.marko +141 -0
  89. package/components/Pill/Pill.css +193 -0
  90. package/components/Pill/pill-variants.js +16 -0
  91. package/components/Pill/pill.marko +165 -0
  92. package/components/Popover/Popover.css +121 -0
  93. package/components/Popover/popover.marko +96 -0
  94. package/components/Radio/Radio.css +230 -0
  95. package/components/Radio/radio-variants.js +16 -0
  96. package/components/Radio/radio.marko +93 -0
  97. package/components/RadioGroup/RadioGroup.css +32 -0
  98. package/components/RadioGroup/radio-group.marko +93 -0
  99. package/components/SearchBar/SearchBar.css +181 -0
  100. package/components/SearchBar/search-bar-variants.js +24 -0
  101. package/components/SearchBar/search-bar.marko +157 -0
  102. package/components/Select/Select.css +427 -0
  103. package/components/Select/select-options.js +163 -0
  104. package/components/Select/select.marko +449 -0
  105. package/components/Separator/Separator.css +22 -0
  106. package/components/Separator/separator.marko +14 -0
  107. package/components/Skeleton/Skeleton.css +100 -0
  108. package/components/Skeleton/skeleton-avatar.marko +28 -0
  109. package/components/Skeleton/skeleton-button.marko +22 -0
  110. package/components/Skeleton/skeleton-image.marko +20 -0
  111. package/components/Skeleton/skeleton-input.marko +22 -0
  112. package/components/Skeleton/skeleton-variants.js +16 -0
  113. package/components/Skeleton/skeleton.marko +53 -0
  114. package/components/Spinner/Spinner.css +173 -0
  115. package/components/Spinner/spinner-geometry.js +39 -0
  116. package/components/Spinner/spinner-overlay.marko +97 -0
  117. package/components/Spinner/spinner.marko +88 -0
  118. package/components/StatCard/StatCard.css +90 -0
  119. package/components/StatCard/stat-card-group.marko +5 -0
  120. package/components/StatCard/stat-card.marko +57 -0
  121. package/components/Switch/Switch.css +209 -0
  122. package/components/Switch/switch.marko +99 -0
  123. package/components/Table/Table.css +270 -0
  124. package/components/Table/table-cell.marko +28 -0
  125. package/components/Table/table-header.marko +27 -0
  126. package/components/Table/table-logic.js +129 -0
  127. package/components/Table/table.marko +372 -0
  128. package/components/Text/Text.css +79 -0
  129. package/components/Text/text-variants.js +25 -0
  130. package/components/Text/text.marko +48 -0
  131. package/components/TextEditor/TextEditor.css +78 -0
  132. package/components/TextEditor/safe-url.js +50 -0
  133. package/components/TextEditor/text-editor-commands.js +22 -0
  134. package/components/TextEditor/text-editor.marko +253 -0
  135. package/components/Toast/Toast.css +88 -0
  136. package/components/Toast/toast-api.js +19 -0
  137. package/components/Toast/toast-store.js +118 -0
  138. package/components/Toast/toast-variants.js +16 -0
  139. package/components/Toast/toast.marko +93 -0
  140. package/components/Toast/toaster.marko +70 -0
  141. package/components/Toggle/Toggle.css +169 -0
  142. package/components/Toggle/toggle.marko +93 -0
  143. package/components/Tooltip/Tooltip.css +115 -0
  144. package/components/Tooltip/tooltip.marko +77 -0
  145. package/components/Uploader/Uploader.css +180 -0
  146. package/components/Uploader/crop-geometry.js +161 -0
  147. package/components/Uploader/uploader.marko +269 -0
  148. package/foundations/Scrollbar/Scrollbar.css +26 -0
  149. package/foundations/attrs.js +27 -0
  150. package/foundations/breakpoints.js +27 -0
  151. package/foundations/contrast.js +74 -0
  152. package/foundations/custom-scheme.js +222 -0
  153. package/foundations/overlay.js +108 -0
  154. package/foundations/styles.css +284 -0
  155. package/foundations/text.js +22 -0
  156. package/foundations/theme-server.js +81 -0
  157. package/foundations/theme.js +42 -0
  158. package/foundations/tokens.css +1656 -0
  159. package/icons/custom/custom-video-horizontal-filled.marko +6 -0
  160. package/icons/custom/custom-video-horizontal-regular.marko +6 -0
  161. package/icons/custom/custom-video-tour-3d-filled.marko +7 -0
  162. package/icons/custom/custom-video-tour-3d-regular.marko +9 -0
  163. package/icons/custom/custom-video-vertical-filled.marko +6 -0
  164. package/icons/custom/custom-video-vertical-regular.marko +6 -0
  165. package/icons/generated/fluent-add-regular.marko +3 -0
  166. package/icons/generated/fluent-alert-regular.marko +3 -0
  167. package/icons/generated/fluent-apps-regular.marko +3 -0
  168. package/icons/generated/fluent-arrow-download-regular.marko +3 -0
  169. package/icons/generated/fluent-arrow-left-regular.marko +3 -0
  170. package/icons/generated/fluent-arrow-right-regular.marko +3 -0
  171. package/icons/generated/fluent-arrow-rotate-clockwise-regular.marko +3 -0
  172. package/icons/generated/fluent-arrow-rotate-counterclockwise-regular.marko +3 -0
  173. package/icons/generated/fluent-border-all-regular.marko +3 -0
  174. package/icons/generated/fluent-calendar-regular.marko +3 -0
  175. package/icons/generated/fluent-checkmark-circle-filled.marko +3 -0
  176. package/icons/generated/fluent-checkmark-regular.marko +3 -0
  177. package/icons/generated/fluent-chevron-down-regular.marko +3 -0
  178. package/icons/generated/fluent-chevron-left-regular.marko +3 -0
  179. package/icons/generated/fluent-chevron-right-regular.marko +3 -0
  180. package/icons/generated/fluent-chevron-up-regular.marko +3 -0
  181. package/icons/generated/fluent-circle-half-fill-regular.marko +3 -0
  182. package/icons/generated/fluent-clock-regular.marko +3 -0
  183. package/icons/generated/fluent-color-regular.marko +3 -0
  184. package/icons/generated/fluent-crop-regular.marko +3 -0
  185. package/icons/generated/fluent-dark-theme-regular.marko +3 -0
  186. package/icons/generated/fluent-delete-regular.marko +3 -0
  187. package/icons/generated/fluent-desktop-regular.marko +3 -0
  188. package/icons/generated/fluent-dismiss-circle-filled.marko +3 -0
  189. package/icons/generated/fluent-dismiss-regular.marko +3 -0
  190. package/icons/generated/fluent-document-pdf-regular.marko +3 -0
  191. package/icons/generated/fluent-document-regular.marko +3 -0
  192. package/icons/generated/fluent-eye-off-regular.marko +3 -0
  193. package/icons/generated/fluent-eye-regular.marko +3 -0
  194. package/icons/generated/fluent-eyedropper-regular.marko +3 -0
  195. package/icons/generated/fluent-grid-regular.marko +3 -0
  196. package/icons/generated/fluent-history-regular.marko +3 -0
  197. package/icons/generated/fluent-home-regular.marko +3 -0
  198. package/icons/generated/fluent-image-regular.marko +3 -0
  199. package/icons/generated/fluent-info-regular.marko +3 -0
  200. package/icons/generated/fluent-link-regular.marko +3 -0
  201. package/icons/generated/fluent-more-horizontal-regular.marko +3 -0
  202. package/icons/generated/fluent-navigation-regular.marko +3 -0
  203. package/icons/generated/fluent-paint-brush-regular.marko +3 -0
  204. package/icons/generated/fluent-question-circle-regular.marko +3 -0
  205. package/icons/generated/fluent-search-regular.marko +3 -0
  206. package/icons/generated/fluent-settings-regular.marko +3 -0
  207. package/icons/generated/fluent-square-hint-regular.marko +3 -0
  208. package/icons/generated/fluent-subtract-regular.marko +3 -0
  209. package/icons/generated/fluent-text-align-center-regular.marko +3 -0
  210. package/icons/generated/fluent-text-align-left-regular.marko +3 -0
  211. package/icons/generated/fluent-text-align-right-regular.marko +3 -0
  212. package/icons/generated/fluent-text-bold-regular.marko +3 -0
  213. package/icons/generated/fluent-text-bullet-list-regular.marko +3 -0
  214. package/icons/generated/fluent-text-font-regular.marko +3 -0
  215. package/icons/generated/fluent-text-font-size-regular.marko +3 -0
  216. package/icons/generated/fluent-text-italic-regular.marko +3 -0
  217. package/icons/generated/fluent-text-number-list-ltr-regular.marko +3 -0
  218. package/icons/generated/fluent-video-regular.marko +3 -0
  219. package/icons/generated/fluent-warning-filled.marko +3 -0
  220. package/icons/generated/fluent-weather-moon-regular.marko +3 -0
  221. package/icons/generated/fluent-weather-sunny-regular.marko +3 -0
  222. package/icons/generated/fluent-zoom-in-regular.marko +3 -0
  223. package/icons/generated/fluent-zoom-out-regular.marko +3 -0
  224. package/icons/manifest.json +61 -0
  225. package/icons/social/social-facebook-color.marko +7 -0
  226. package/icons/social/social-facebook.marko +6 -0
  227. package/icons/social/social-google-color.marko +20 -0
  228. package/icons/social/social-google.marko +6 -0
  229. package/icons/social/social-instagram-color.marko +16 -0
  230. package/icons/social/social-instagram.marko +6 -0
  231. package/icons/social/social-linkedin-color.marko +8 -0
  232. package/icons/social/social-linkedin.marko +8 -0
  233. package/icons/social/social-messenger-color.marko +15 -0
  234. package/icons/social/social-messenger.marko +6 -0
  235. package/icons/social/social-pinterest-color.marko +6 -0
  236. package/icons/social/social-pinterest.marko +6 -0
  237. package/icons/social/social-telegram-color.marko +7 -0
  238. package/icons/social/social-telegram.marko +6 -0
  239. package/icons/social/social-whatsapp-color.marko +8 -0
  240. package/icons/social/social-whatsapp.marko +7 -0
  241. package/icons/social/social-x-color.marko +7 -0
  242. package/icons/social/social-x.marko +6 -0
  243. package/icons/social/social-youtube-color.marko +7 -0
  244. package/icons/social/social-youtube.marko +6 -0
  245. package/marko.json +203 -0
  246. package/package.json +55 -0
@@ -0,0 +1,181 @@
1
+ /* SDS SearchBar.
2
+ Ported from kdls-design-system v2.5.1 (0f3edf8) components/Field/Field.css — the shell.
3
+ KDLS's own SearchBar.css adds only the suggestions panel, which lands here in Phase 2.
4
+ by scripts/port-css.mjs. Divergences beyond the mechanical rename are noted inline.
5
+
6
+ Rules removed with the brand families they targeted:
7
+ [data-color-scheme="lya"] .sds-search-bar__input
8
+ [data-color-scheme="lya"] .sds-search-bar:focus-within
9
+ *//* ══════════════════════════════════════════════════════════════════════════
10
+ SEARCH BAR — the field shell (Input has its own; see the component header)
11
+ Structure mirrored from Input: flex row (gap + padding), 24px icons
12
+ as children, floating label or placeholder.
13
+ Sizes: lg 56px · md 48px · sm 40px
14
+ ══════════════════════════════════════════════════════════════════════════ */
15
+
16
+ .sds-search-bar {
17
+ position: relative;
18
+ display: flex;
19
+ align-items: center;
20
+ gap: var(--spacing-3);
21
+ padding: 0 var(--spacing-4);
22
+ border: 1px solid var(--border-interactive-color);
23
+ border-radius: var(--radius-md);
24
+ background-color: var(--component-bg-color);
25
+ transition: border-color 120ms ease, box-shadow 120ms ease;
26
+ cursor: text;
27
+ box-sizing: border-box;
28
+ }
29
+ /* ── Heights ── */
30
+ .sds-search-bar--lg { height: 56px; min-height: 56px; }
31
+ .sds-search-bar--md { height: 48px; min-height: 48px; }
32
+ .sds-search-bar--sm { height: 40px; min-height: 40px; }
33
+ /* ── Hover / Focus ── */
34
+ .sds-search-bar:hover:not(:focus-within):not(.sds-search-bar--disabled) {
35
+ border-color: var(--color-primary-50-solid);
36
+ }
37
+ .sds-search-bar:focus-within {
38
+ border-color: var(--color-primary-50-solid);
39
+ box-shadow: var(--focus-ring);
40
+ }
41
+ /* ── Feedback ── */
42
+ .sds-search-bar--feedback-success { border-color: var(--color-success-50); }
43
+ .sds-search-bar--feedback-alert { border-color: var(--color-alert-50); }
44
+ .sds-search-bar--feedback-error { border-color: var(--color-error-50); }
45
+ .sds-search-bar--feedback-success:focus-within,
46
+ .sds-search-bar--feedback-alert:focus-within,
47
+ .sds-search-bar--feedback-error:focus-within {
48
+ box-shadow: var(--focus-ring);
49
+ }
50
+ /* ── Icons (flex children, 24px) ── */
51
+ .sds-search-bar__icon {
52
+ display: inline-flex;
53
+ align-items: center;
54
+ justify-content: center;
55
+ font-size: 24px;
56
+ flex-shrink: 0;
57
+ width: 24px;
58
+ height: 24px;
59
+ color: var(--color-neutral-90);
60
+ }
61
+ /* KDLS styled a bare <button> here. SDS renders sds-icon-button instead — a handler in
62
+ this template would make Marko serialise every field on the page — so the icon slot only
63
+ has to make room for it. */
64
+ .sds-search-bar__icon--right:has(.sds-icon-button) {
65
+ width: auto;
66
+ height: auto;
67
+ }
68
+ /* ── Spinner (loading, in place of iconRight) ── */
69
+ .sds-search-bar__spinner {
70
+ display: inline-flex;
71
+ align-items: center;
72
+ justify-content: center;
73
+ flex-shrink: 0;
74
+ width: 24px;
75
+ height: 24px;
76
+ color: var(--color-neutral-50);
77
+ }
78
+ /* ── The field itself (label + control) ── */
79
+ .sds-search-bar__field {
80
+ flex: 1;
81
+ position: relative;
82
+ display: flex;
83
+ flex-direction: column;
84
+ justify-content: center;
85
+ min-width: 0;
86
+ }
87
+ .sds-search-bar--lg .sds-search-bar__field { min-height: 52px; }
88
+ /* Invisible spacer — same technique as Input (hug grows with the label) */
89
+ .sds-search-bar__field::before {
90
+ content: attr(data-label);
91
+ font-family: var(--font-family-base);
92
+ font-size: var(--font-size-body-md);
93
+ font-weight: var(--font-weight-regular);
94
+ white-space: nowrap;
95
+ display: block;
96
+ height: 0;
97
+ overflow: hidden;
98
+ visibility: hidden;
99
+ pointer-events: none;
100
+ }
101
+ /* ── Controle nativo ── */
102
+ .sds-search-bar__input {
103
+ border: none;
104
+ outline: none;
105
+ background: transparent;
106
+ font-family: var(--font-family-base);
107
+ font-size: var(--font-size-body-md);
108
+ font-weight: var(--font-weight-regular);
109
+ color: var(--color-neutral-90);
110
+ width: 100%;
111
+ line-height: 1;
112
+ caret-color: var(--color-primary-50-solid);
113
+ }
114
+ /* ── Label base ── */
115
+ .sds-search-bar__label {
116
+ position: absolute;
117
+ left: 0;
118
+ top: 50%;
119
+ transform: translateY(-50%);
120
+ pointer-events: none;
121
+ white-space: nowrap;
122
+ }
123
+ .sds-search-bar__field .sds-search-bar__label {
124
+ line-height: 1;
125
+ }
126
+ /* ── Label 'float' — rises on focus/fill ── */
127
+ .sds-search-bar--label-float .sds-search-bar__label {
128
+ color: var(--color-neutral-50);
129
+ transform-origin: top left;
130
+ transition: top 120ms ease, font-size 120ms ease, color 120ms ease, transform 120ms ease;
131
+ }
132
+ .sds-search-bar--label-float.sds-search-bar--filled .sds-search-bar__label,
133
+ .sds-search-bar--label-float:focus-within .sds-search-bar__label {
134
+ top: var(--spacing-2);
135
+ transform: none;
136
+ font-size: var(--font-size-label-sm);
137
+ line-height: var(--lh-label-sm);
138
+ }
139
+ .sds-search-bar--label-float .sds-search-bar__input {
140
+ padding-top: var(--spacing-5);
141
+ padding-bottom: var(--spacing-1);
142
+ }
143
+ /* ── Label 'placeholder' — disappears on focus/fill ── */
144
+ .sds-search-bar--label-placeholder .sds-search-bar__label {
145
+ color: var(--placeholder-color);
146
+ transition: opacity 120ms ease;
147
+ }
148
+ .sds-search-bar--label-placeholder.sds-search-bar--filled .sds-search-bar__label,
149
+ .sds-search-bar--label-placeholder:focus-within .sds-search-bar__label {
150
+ opacity: 0;
151
+ }
152
+ /* ── Loading — wait cursor on the root (mouse target); pointer-events only on the input
153
+ blocks click/typing without canceling the cursor (mirrors Select) ── */
154
+ .sds-search-bar--loading { cursor: wait; }
155
+ .sds-search-bar--loading .sds-search-bar__input {
156
+ cursor: wait;
157
+ pointer-events: none;
158
+ }
159
+ /* ── Disabled ── */
160
+ .sds-search-bar--disabled {
161
+ border-color: var(--border-static-color);
162
+ background-color: var(--color-neutral-10);
163
+ cursor: not-allowed;
164
+ }
165
+ .sds-search-bar--disabled > * { pointer-events: none; }
166
+ .sds-search-bar--disabled .sds-search-bar__input,
167
+ .sds-search-bar--disabled .sds-search-bar__label,
168
+ .sds-search-bar--disabled .sds-search-bar__icon {
169
+ color: var(--color-neutral-40);
170
+ }
171
+
172
+ /* `hug` is the old behaviour: the shell is as wide as what is put in it. `fill` takes the
173
+ width it is given, which is what a search box inside a menu or a toolbar needs. */
174
+ .sds-search-bar--hug {
175
+ width: max-content;
176
+ max-width: 100%;
177
+ }
178
+
179
+ .sds-search-bar--fill {
180
+ width: 100%;
181
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The SearchBar's vocabulary.
3
+ *
4
+ * Ported from kdls-design-system v2.5.1 (0f3edf8) components/Field/Field.tsx, where these
5
+ * were TypeScript unions. TypeScript checks them at build time and disappears; in Marko
6
+ * they have to be checked at render time or an unknown value renders an unstyled shell.
7
+ */
8
+
9
+ export const SEARCH_BAR_SIZES = ['sm', 'md', 'lg'];
10
+ export const SEARCH_BAR_FEEDBACK = ['none', 'success', 'alert', 'error'];
11
+ export const SEARCH_BAR_LABEL_MODES = ['float', 'placeholder'];
12
+ export const SEARCH_BAR_WIDTHS = ['hug', 'fill'];
13
+
14
+ const assert = (name, allowed) => (value) => {
15
+ if (!allowed.includes(value)) {
16
+ throw new Error(`Unknown search bar ${name}: "${value}". Allowed: ${allowed.join(', ')}`);
17
+ }
18
+ return value;
19
+ };
20
+
21
+ export const assertSearchBarSize = assert('size', SEARCH_BAR_SIZES);
22
+ export const assertSearchBarFeedback = assert('feedback', SEARCH_BAR_FEEDBACK);
23
+ export const assertSearchBarLabelMode = assert('labelMode', SEARCH_BAR_LABEL_MODES);
24
+ export const assertSearchBarWidth = assert('width', SEARCH_BAR_WIDTHS);
@@ -0,0 +1,157 @@
1
+ <!-- sds-search-bar — ported from kdls-design-system v2.5.1 (0f3edf8), from two files:
2
+ components/Field/Field.tsx is the shell you see here, and components/SearchBar's
3
+ suggestions panel lands in this same component rather than wrapping it.
4
+
5
+ KDLS split them because its Field was meant as a general primitive. It never became
6
+ one — SearchBar is the only thing that imports it, and Input carries its own copy of
7
+ the markup. Rafael, 2026-09-10, named the reason that split has no future here: a
8
+ field in a Kenlo site is 56px, full stop, and SearchBar is the only one that varies,
9
+ at 40 and 48. A shell whose only job is to be three heights for one consumer is that
10
+ consumer. So Input refuses `size` and points here, and there is one component instead
11
+ of two that look the same.
12
+
13
+ Today it is the box, the icons either side, the floating label and the focus ring,
14
+ with the native control supplied as the body.
15
+
16
+ The search behaviour is not here yet, and that is the shape of what is missing:
17
+ `onChange`, `onSearch`, `onSuggestionSelect`, `onKeyDown`, `recentSearches` and the
18
+ panel they drive. They arrive together, in this component, when the panel does — not
19
+ wrapped around it. At that point it stops taking an <input> from the caller and owns
20
+ its own. `width` is here already because the shell has to be placeable. It
21
+ defaults to `fill`: the shell takes the width it is given, which is what a search box
22
+ in a menu, a toolbar or a control column needs, and what Input has always had. `hug`
23
+ is the opt-out, for the rare case where the shell should be as wide as its contents;
24
+ it was the default and it was the wrong one — a menu's search box sized itself to the
25
+ word "Buscar" and left the panel beside it.
26
+
27
+ It holds no state, and does not want any. `filled` is the caller's answer to "does
28
+ this control have a value", because the caller is the one holding the value. A shell
29
+ that tracked that itself would serialise its whole input on every instance.
30
+
31
+ Divergence: a clickable right icon is an sds-icon-button, not a handler in this
32
+ template. That is not cosmetic. An `on-*` bound to a value makes Marko serialise the
33
+ component holding it, and it decides at compile time — so a handler here would
34
+ serialise every instance on the page, clickable icon or not. Icon was found doing
35
+ exactly this. The button also arrives with focus, Enter, Space and a required name,
36
+ which KDLS's bare <button> with an optional aria-label did not guarantee. -->
37
+
38
+ import searchIcon from "../../icons/generated/fluent-search-regular.marko";
39
+ import dismissIcon from "../../icons/generated/fluent-dismiss-regular.marko";
40
+ import { assertSearchBarSize, assertSearchBarFeedback, assertSearchBarLabelMode, assertSearchBarWidth } from "./search-bar-variants.js";
41
+
42
+ $ {
43
+ const {
44
+ size = "lg",
45
+ label,
46
+ labelMode = "float",
47
+ width = "fill",
48
+ htmlFor,
49
+ filled = false,
50
+ onClear,
51
+ clearLabel = "Limpar busca",
52
+ disabled = false,
53
+ isLoading = false,
54
+ feedback = "none",
55
+ iconLeft = searchIcon,
56
+ iconRight,
57
+ onClickIconRight,
58
+ iconRightLabel,
59
+ class: extraClass = "",
60
+ } = input;
61
+
62
+ assertSearchBarSize(size);
63
+ assertSearchBarFeedback(feedback);
64
+ assertSearchBarLabelMode(labelMode);
65
+ assertSearchBarWidth(width);
66
+
67
+ if (onClickIconRight && !iconRightLabel) {
68
+ throw new Error("sds-search-bar needs an `iconRightLabel` when `onClickIconRight` is set — an icon button with no name is announced as just \"button\".");
69
+ }
70
+
71
+ const renderable = (value) => (value && value.renderBody) || value;
72
+
73
+ // The two halves of the same gesture. Empty, the magnifier on the left says what the box
74
+ // is for; typing, it has said it, and the space belongs to the text — so it steps aside
75
+ // and the way back to empty appears on the right instead. This holds for any `iconLeft`,
76
+ // not just the default one: in a search box the left edge is the text's the moment there
77
+ // is text.
78
+ //
79
+ // No state is needed for either. `filled` is the caller's answer to "is there a value",
80
+ // which the caller already holds, and a shell that tracked it itself would serialise its
81
+ // whole input on every instance.
82
+ const showIconLeft = iconLeft && !filled;
83
+
84
+ // A clear button is only offered when there is something to clear AND someone to clear
85
+ // it. Drawing one without `onClear` is the KDLS Uploader's download button: it renders,
86
+ // it takes focus, and clicking does nothing.
87
+ const showClear = filled && onClear && !disabled && !isLoading;
88
+
89
+ const classes = [
90
+ "sds-search-bar",
91
+ `sds-search-bar--${size}`,
92
+ `sds-search-bar--label-${labelMode}`,
93
+ `sds-search-bar--${width}`,
94
+ filled ? "sds-search-bar--filled" : "",
95
+ disabled ? "sds-search-bar--disabled" : "",
96
+ isLoading ? "sds-search-bar--loading" : "",
97
+ feedback !== "none" ? `sds-search-bar--feedback-${feedback}` : "",
98
+ extraClass,
99
+ ].filter(Boolean).join(" ");
100
+ }
101
+
102
+ <div class=classes>
103
+ <if(showIconLeft)>
104
+ <span class="sds-search-bar__icon sds-search-bar__icon--left" aria-hidden="true">
105
+ <sds-icon><${renderable(iconLeft)}/></sds-icon>
106
+ </span>
107
+ </if>
108
+
109
+ <!-- data-label feeds the invisible ::before spacer, which is how the box grows to fit a
110
+ label longer than its value. -->
111
+ <div class="sds-search-bar__field" data-label=label>
112
+ <if(label)>
113
+ <sds-text as="label" variant="bodyMd" class="sds-search-bar__label" for=htmlFor>
114
+ ${label}
115
+ </sds-text>
116
+ </if>
117
+ <${input.renderBody}/>
118
+ </div>
119
+
120
+ <if(isLoading)>
121
+ <span class="sds-search-bar__spinner" aria-hidden="true">
122
+ <sds-spinner size="sm" decorative/>
123
+ </span>
124
+ </if>
125
+ <else-if(showClear)>
126
+ // Ahead of `iconRight`: whatever else the right side was for, clearing the search the
127
+ // reader is looking at is the more immediate thing to offer.
128
+ <span class="sds-search-bar__icon sds-search-bar__icon--right">
129
+ <sds-icon-button
130
+ variant="neutral"
131
+ size="sm"
132
+ aria-label=clearLabel
133
+ onClick=onClear
134
+ >
135
+ <@icon><${dismissIcon}/></@icon>
136
+ </sds-icon-button>
137
+ </span>
138
+ </else-if>
139
+ <else-if(iconRight && onClickIconRight)>
140
+ <span class="sds-search-bar__icon sds-search-bar__icon--right">
141
+ <sds-icon-button
142
+ variant="neutral"
143
+ size="sm"
144
+ aria-label=iconRightLabel
145
+ disabled=(disabled || null)
146
+ onClick=onClickIconRight
147
+ >
148
+ <@icon><${renderable(iconRight)}/></@icon>
149
+ </sds-icon-button>
150
+ </span>
151
+ </else-if>
152
+ <else-if(iconRight)>
153
+ <span class="sds-search-bar__icon sds-search-bar__icon--right" aria-hidden="true">
154
+ <sds-icon><${renderable(iconRight)}/></sds-icon>
155
+ </span>
156
+ </else-if>
157
+ </div>