@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,104 @@
1
+ /* SDS Alert.
2
+ Ported from kdls-design-system v2.5.1 (0f3edf8) components/Alert/Alert.css
3
+ by scripts/port-css.mjs. Divergences beyond the mechanical rename are noted inline.
4
+ */.sds-alert {
5
+ display: flex;
6
+ align-items: center;
7
+ width: 100%;
8
+ border-radius: var(--radius-md);
9
+ }
10
+ .sds-alert__content {
11
+ flex: 1;
12
+ display: flex;
13
+ align-items: center;
14
+ gap: var(--spacing-4);
15
+ padding: var(--spacing-3) var(--spacing-4);
16
+ }
17
+ .sds-alert--hug { width: fit-content; }
18
+ .sds-alert--fill { width: 100%; }
19
+ .sds-alert__icon {
20
+ display: inline-flex;
21
+ align-items: center;
22
+ justify-content: center;
23
+ width: 24px;
24
+ height: 24px;
25
+ flex-shrink: 0;
26
+ font-size: 24px;
27
+ }
28
+ .sds-alert__message {
29
+ flex: 1;
30
+ }
31
+ .sds-alert__action {
32
+ margin-left: var(--spacing-1);
33
+ flex-shrink: 0;
34
+ }
35
+ .sds-alert__dismiss {
36
+ display: flex;
37
+ align-items: center;
38
+ flex-shrink: 0;
39
+ padding-right: var(--spacing-2);
40
+ }
41
+ .sds-alert--sm .sds-alert__dismiss {
42
+ padding-top: var(--spacing-1);
43
+ padding-bottom: var(--spacing-1);
44
+ padding-right: var(--spacing-1);
45
+ }
46
+ /* Alert variants — icon inherits the same text color as the container.
47
+
48
+ The background is `--surface-<family>`, not `--color-<family>-10`. They are the same
49
+ thing in the light theme; in the dark theme the surface stops one step short of the
50
+ palette's darkest tone, because the darkest tone next to a near-black page reads as a
51
+ hole rather than a tinted panel. The reasoning is at the token, in foundations. */
52
+ .sds-alert--success {
53
+ background: var(--surface-success);
54
+ color: var(--color-success-10-on);
55
+ }
56
+ .sds-alert--alert {
57
+ background: var(--surface-alert);
58
+ color: var(--color-alert-10-on);
59
+ }
60
+ .sds-alert--error {
61
+ background: var(--surface-error);
62
+ color: var(--color-error-10-on);
63
+ }
64
+ .sds-alert--info {
65
+ background: var(--surface-info);
66
+ color: var(--color-primary-90-solid);
67
+ }
68
+ /* Neutral variant (fallback) */
69
+ .sds-alert--neutral {
70
+ background: var(--surface-neutral);
71
+ color: var(--color-neutral-10-on);
72
+ }
73
+ /* ── Md size — icon 20px ── */
74
+ .sds-alert--md .sds-alert__icon,
75
+ .sds-alert--md .sds-alert__icon .sds-icon,
76
+ .sds-alert--md .sds-alert__icon svg {
77
+ width: 20px;
78
+ height: 20px;
79
+ font-size: 20px;
80
+ line-height: 1;
81
+ }
82
+ /* ── Small size ── */
83
+ .sds-alert--sm .sds-alert__content {
84
+ min-height: unset;
85
+ padding: var(--spacing-2) var(--spacing-3);
86
+ }
87
+ .sds-alert--sm .sds-alert__icon {
88
+ width: 16px;
89
+ height: 16px;
90
+ font-size: 16px;
91
+ }
92
+ .sds-alert--neutral.sds-alert--bordered { border: 1px solid var(--color-neutral-30); }
93
+ .sds-alert--success.sds-alert--bordered { border: 1px solid var(--color-success-30); }
94
+ .sds-alert--alert.sds-alert--bordered { border: 1px solid var(--color-alert-30); }
95
+ .sds-alert--error.sds-alert--bordered { border: 1px solid var(--color-error-30); }
96
+ .sds-alert--info.sds-alert--bordered { border: 1px solid var(--color-primary-30-solid); }
97
+ /* ── Dark mode ── */
98
+
99
+ /* A neutral alert paints itself in the same neutral the page uses, so on a neutral ground
100
+ the two merge and the alert reads as loose text. The other variants carry their own
101
+ hue and separate themselves. */
102
+ .sds-alert--neutral:not(.sds-alert--bordered) {
103
+ border: 1px solid var(--border-static-color);
104
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The Alert vocabulary.
3
+ * Ported from kdls-design-system v2.5.1 (0f3edf8) components/Alert/Alert.tsx.
4
+ */
5
+ export const ALERT_VARIANTS = ['info', 'success', 'alert', 'error', 'neutral'];
6
+ export const ALERT_SIZES = ['sm', 'md', 'lg'];
7
+ export const ALERT_WIDTHS = ['hug', 'fill'];
8
+
9
+ /** The Text variant each alert size reads at, so the scale lives in one place. */
10
+ export const ALERT_TEXT_VARIANT = { lg: 'bodyMd', md: 'bodySm', sm: 'labelRegular' };
11
+
12
+ function assertOneOf(label, value, allowed) {
13
+ if (!allowed.includes(value)) {
14
+ throw new Error(`Unknown alert ${label}: "${value}". Allowed: ${allowed.join(', ')}`);
15
+ }
16
+ return value;
17
+ }
18
+
19
+ export const assertAlertVariant = (v) => assertOneOf('variant', v, ALERT_VARIANTS);
20
+ export const assertAlertSize = (v) => assertOneOf('size', v, ALERT_SIZES);
21
+ export const assertAlertWidth = (v) => assertOneOf('width', v, ALERT_WIDTHS);
22
+
23
+ /**
24
+ * Which live region an alert belongs in.
25
+ *
26
+ * KDLS put role="alert" on every one. That is right for an error — it interrupts a screen
27
+ * reader immediately — and wrong for an informational notice, which then also interrupts.
28
+ * Anything that is not a problem is announced politely instead, when the user is between
29
+ * things.
30
+ */
31
+ export function liveRegionFor(variant) {
32
+ return variant === 'error' || variant === 'alert'
33
+ ? { role: 'alert', 'aria-live': 'assertive' }
34
+ : { role: 'status', 'aria-live': 'polite' };
35
+ }
@@ -0,0 +1,94 @@
1
+ <!-- sds-alert — ported from kdls-design-system v2.5.1 (0f3edf8) components/Alert/Alert.tsx.
2
+
3
+ No state. KDLS held useState for one thing: a spinner on the action button while an async
4
+ onAction resolved. That is the Badge's case again — the base component stays markup and
5
+ the caller, which is the thing that knows about its own async work, drives `actionLoading`
6
+ from outside.
7
+
8
+ Divergences:
9
+
10
+ Only an error or a warning takes role="alert". KDLS put it on every variant, so an
11
+ informational notice interrupted a screen reader mid-sentence exactly as an error does.
12
+ Anything that is not a problem is announced politely instead — see liveRegionFor.
13
+
14
+ `actionDisabledTooltip` and `actionDisabledTooltipPosition` are here now that Tooltip is.
15
+ They reach sds-button, which does the wrapping — a disabled button dispatches no mouse
16
+ events, so the tooltip has to sit outside it. -->
17
+
18
+ import { assertPlacement } from "../Floating/floating-position.js";
19
+ import { assertAlertVariant, assertAlertSize, assertAlertWidth, ALERT_TEXT_VARIANT,
20
+ liveRegionFor } from "./alert-variants.js";
21
+
22
+ $ {
23
+ const {
24
+ message,
25
+ variant = "info",
26
+ size = "lg",
27
+ width = "fill",
28
+ bordered = false,
29
+ icon,
30
+ actionLabel,
31
+ onAction,
32
+ actionLoading = false,
33
+ actionDisabled = false,
34
+ actionDisabledTooltip,
35
+ actionDisabledTooltipPosition = "top",
36
+ onDismiss,
37
+ dismissLabel = "Fechar",
38
+ class: extraClass = "",
39
+ } = input;
40
+
41
+ assertPlacement(actionDisabledTooltipPosition);
42
+
43
+ if (!message) throw new Error("sds-alert needs a `message`.");
44
+ assertAlertVariant(variant);
45
+ assertAlertSize(size);
46
+ assertAlertWidth(width);
47
+
48
+ const renderable = (value) => (value && value.renderBody) || value;
49
+ const live = liveRegionFor(variant);
50
+
51
+ const classes = [
52
+ "sds-alert",
53
+ `sds-alert--${variant}`,
54
+ size !== "lg" ? `sds-alert--${size}` : "",
55
+ `sds-alert--${width}`,
56
+ bordered ? "sds-alert--bordered" : "",
57
+ extraClass,
58
+ ].filter(Boolean).join(" ");
59
+ }
60
+
61
+ <div class=classes role=live.role aria-live=live["aria-live"]>
62
+ <div class="sds-alert__content">
63
+ <if(icon)>
64
+ <span class="sds-alert__icon" aria-hidden="true">
65
+ <sds-icon size=(size === "sm" ? "sm" : "md")><${renderable(icon)}/></sds-icon>
66
+ </span>
67
+ </if>
68
+
69
+ <sds-text as="span" class="sds-alert__message" color="inherit" variant=ALERT_TEXT_VARIANT[size]>
70
+ ${message}
71
+ </sds-text>
72
+
73
+ <if(actionLabel)>
74
+ <sds-button
75
+ variant="link"
76
+ label=actionLabel
77
+ onClick=onAction
78
+ isLoading=actionLoading
79
+ disabled=actionDisabled
80
+ disabledTooltip=actionDisabledTooltip
81
+ disabledTooltipPosition=actionDisabledTooltipPosition
82
+ class="sds-alert__action"
83
+ />
84
+ </if>
85
+ </div>
86
+
87
+ <if(onDismiss)>
88
+ <div class="sds-alert__dismiss">
89
+ <sds-icon-button variant="neutral" aria-label=dismissLabel onClick=onDismiss>
90
+ <@icon><fluent-dismiss-regular/></@icon>
91
+ </sds-icon-button>
92
+ </div>
93
+ </if>
94
+ </div>
@@ -0,0 +1,92 @@
1
+ /* SDS AttachmentList.
2
+ Ported from kdls-design-system v2.5.1 (0f3edf8) components/AttachmentList/AttachmentList.css
3
+ by scripts/port-css.mjs. Divergences beyond the mechanical rename are noted inline.
4
+ *//* ── AttachmentList ─────────────────────────────────────────────────────────
5
+ List of uploaded/attached files with icon, name+meta, and action buttons.
6
+ BEM root: .sds-attachment-list / .sds-attachment-row
7
+ ─────────────────────────────────────────────────────────────────────────── */
8
+
9
+ /* Divergence from KDLS: the surface is declared.
10
+
11
+ KDLS draws the stroke and the radius and leaves the fill to whatever is behind, which
12
+ works only while the thing behind is the component surface. Dropped on a page ground,
13
+ or on a stage with an alternate background, the list takes that colour and the frame
14
+ reads as an empty outline rather than a panel. A bordered container states its own
15
+ fill. */
16
+ .sds-attachment-list {
17
+ width: 100%;
18
+ background: var(--component-bg-color);
19
+ border-radius: var(--radius-md);
20
+ overflow: hidden;
21
+ }
22
+
23
+ /* The frame is a choice, not a fixture: a bordered container separates itself from a
24
+ ground of its own colour, and on a different ground the stroke is one line of noise.
25
+ Same rule the Card and the Accordion follow. */
26
+ .sds-attachment-list--bordered {
27
+ border: 1px solid var(--border-static-color);
28
+ }
29
+ /* ── Row ── */
30
+ .sds-attachment-row {
31
+ display: flex;
32
+ align-items: center;
33
+ gap: var(--spacing-4);
34
+ padding: var(--spacing-3) var(--spacing-4);
35
+ min-height: var(--spacing-16);
36
+ }
37
+ /* The divider. It is written against the <li>, not the row: KDLS stacks bare divs, so
38
+ `.row + .row` was adjacent there — here every row is wrapped in its own list item, and
39
+ two rows are never siblings. The selector matched nothing and the lines ran together. */
40
+ .sds-attachment-list > li + li > .sds-attachment-row {
41
+ border-top: 1px solid var(--border-static-color);
42
+ }
43
+ /* ── File type icon ── */
44
+ .sds-attachment-row__icon {
45
+ display: inline-flex;
46
+ font-size: 24px;
47
+ flex-shrink: 0;
48
+ }
49
+ /* ── File info ── */
50
+ .sds-attachment-row__info {
51
+ flex: 1;
52
+ display: flex;
53
+ flex-direction: column;
54
+ gap: var(--spacing-0-5);
55
+ min-width: 0;
56
+ }
57
+ .sds-attachment-row__name {
58
+ min-width: 200px;
59
+ }
60
+ .sds-attachment-row__name--truncate {
61
+ display: block;
62
+ white-space: nowrap;
63
+ overflow: hidden;
64
+ text-overflow: ellipsis;
65
+ }
66
+ /* Tooltip.wrapper is inline-flex by default (shrinks to content) — force it to take up
67
+ the available width so the name truncates correctly inside it */
68
+ .sds-attachment-row__name-tooltip {
69
+ display: block;
70
+ width: 100%;
71
+ min-width: 0;
72
+ }
73
+ /* No real overflow on the name — turns off the tooltip hover via CSS (without unmounting) */
74
+ .sds-attachment-row__name-tooltip--inactive {
75
+ pointer-events: none;
76
+ cursor: default;
77
+ }
78
+ /* ── Actions ── */
79
+ .sds-attachment-row__actions {
80
+ display: flex;
81
+ align-items: center;
82
+ gap: var(--spacing-4);
83
+ flex-shrink: 0;
84
+ }
85
+
86
+ /* The list is a <ul>, which brings bullets and indentation the design does not want —
87
+ these four lines are the whole price of a screen reader saying "list, 4 items". */
88
+ .sds-attachment-list {
89
+ list-style: none;
90
+ margin: 0;
91
+ padding: 0;
92
+ }
@@ -0,0 +1,51 @@
1
+ <!-- sds-attachment-list — ported from kdls-design-system v2.5.1 (0f3edf8)
2
+ components/AttachmentList/AttachmentList.tsx.
3
+
4
+ A list of files, each an sds-attachment-row. It holds no state of its own: the handlers
5
+ are the caller's and it only binds the document's id into them.
6
+
7
+ Divergence: it is a <ul> of <li>, not a stack of divs. A screen reader then says "list,
8
+ 4 items" and can move between them; KDLS's divs are four unrelated rows, and a visitor
9
+ who cannot see the page has no way to know how many attachments there are or when the
10
+ list ends. -->
11
+
12
+ $ {
13
+ const {
14
+ documents = [],
15
+ onDelete,
16
+ onDownload,
17
+ onViewDetails,
18
+ truncateName = true,
19
+ // Default true: the list draws a surface, and a bordered container is what it has
20
+ // always looked like. A caller dropping it on a different ground turns it off.
21
+ bordered = true,
22
+ label = "Anexos",
23
+ class: extraClass = "",
24
+ } = input;
25
+
26
+ const classes = [
27
+ "sds-attachment-list",
28
+ bordered ? "sds-attachment-list--bordered" : "",
29
+ extraClass,
30
+ ].filter(Boolean).join(" ");
31
+ }
32
+
33
+ <ul class=classes aria-label=label>
34
+ <for|doc| of=documents>
35
+ <li key=doc.id>
36
+ <sds-attachment-row
37
+ name=doc.name
38
+ icon=doc.icon
39
+ meta=doc.meta
40
+ badge=doc.badge
41
+ truncateName=truncateName
42
+ onDelete=(onDelete ? () => onDelete(doc.id) : undefined)
43
+ onDownload=(onDownload ? () => onDownload(doc.id) : undefined)
44
+ onViewDetails=(onViewDetails ? () => onViewDetails(doc.id) : undefined)
45
+ disabledDelete=doc.disabledDelete
46
+ disabledDownload=doc.disabledDownload
47
+ disabledViewDetails=doc.disabledViewDetails
48
+ />
49
+ </li>
50
+ </for>
51
+ </ul>
@@ -0,0 +1,167 @@
1
+ <!-- sds-attachment-row — ported from kdls-design-system v2.5.1 (0f3edf8)
2
+ components/AttachmentList/AttachmentList.tsx.
3
+
4
+ One file in a list: an icon, a name, a line of metadata, and up to three actions.
5
+
6
+ Divergences:
7
+
8
+ The name's tooltip only exists when the name is actually cut off. KDLS renders it
9
+ always and hides it with a class when it is not needed — which leaves a `cursor: help`
10
+ and a tooltip trigger on every row whose name fits, promising an explanation there is
11
+ no need for. The measurement is the same ResizeObserver; what changes is that the
12
+ result decides whether to render rather than how to style.
13
+
14
+ `onDelete` and `onDownload` follow a promise when the caller returns one, as KDLS
15
+ does, which is what lets a row show its own spinner without being told to. -->
16
+
17
+ import deleteIcon from "../../icons/generated/fluent-delete-regular.marko";
18
+ import downloadIcon from "../../icons/generated/fluent-arrow-download-regular.marko";
19
+ import detailsIcon from "../../icons/generated/fluent-chevron-right-regular.marko";
20
+
21
+ class {
22
+ onCreate() {
23
+ this.state = { deleting: false, downloading: false, truncated: false };
24
+ }
25
+
26
+ /**
27
+ * Is the name actually cut off? Only then is it worth a tooltip.
28
+ *
29
+ * This used to live entirely inside `onMount`, which made it a one-time answer to a
30
+ * question that changes: renaming a file left the tooltip showing the old name, and
31
+ * turning `truncateName` on after mount applied the ellipsis with no tooltip behind it,
32
+ * so the cut-off name became unreadable with no way to see it.
33
+ */
34
+ measure() {
35
+ const name = this.getEl("name");
36
+ if (!name) return;
37
+ this.state.truncated = this.input.truncateName !== false
38
+ && name.scrollWidth > name.clientWidth;
39
+ }
40
+
41
+ onMount() {
42
+ this.measure();
43
+ this.watched = this.input.name;
44
+ // The measurement above is the answer for a row that never changes width. The observer
45
+ // keeps it true when the column is resized — and is guarded because it does not exist
46
+ // in jsdom, or in a browser old enough to matter to nobody but which would otherwise
47
+ // take the whole row down with it.
48
+ if (typeof ResizeObserver === "undefined") return;
49
+ const name = this.getEl("name");
50
+ if (!name) return;
51
+ this.observer = new ResizeObserver(() => this.measure());
52
+ this.observer.observe(name);
53
+ }
54
+
55
+ onUpdate() {
56
+ // Re-measure when the thing being measured changed. Guarded on the name so that the
57
+ // state write inside `measure` does not re-enter this on every render.
58
+ if (this.watched === this.input.name) return;
59
+ this.watched = this.input.name;
60
+ this.measure();
61
+ }
62
+
63
+ onDestroy() {
64
+ this.observer?.disconnect();
65
+ }
66
+
67
+ async run(which, handler) {
68
+ const result = handler?.();
69
+ if (!(result instanceof Promise)) return;
70
+ this.state[which] = true;
71
+ try {
72
+ await result;
73
+ } finally {
74
+ this.state[which] = false;
75
+ }
76
+ }
77
+
78
+ handleDelete() { return this.run("deleting", this.input.onDelete); }
79
+ handleDownload() { return this.run("downloading", this.input.onDownload); }
80
+ }
81
+
82
+ $ {
83
+ const {
84
+ name,
85
+ icon,
86
+ meta,
87
+ badge,
88
+ onDelete,
89
+ onDownload,
90
+ onViewDetails,
91
+ disabledDelete = false,
92
+ disabledDownload = false,
93
+ disabledViewDetails = false,
94
+ truncateName = true,
95
+ deleteLabel = "Excluir",
96
+ downloadLabel = "Baixar",
97
+ detailsLabel = "Ver detalhes",
98
+ class: extraClass = "",
99
+ } = input;
100
+
101
+ if (!name) throw new Error("sds-attachment-row needs a `name`.");
102
+
103
+ const renderable = (value) => (value && value.renderBody) || value;
104
+ const hasActions = Boolean(badge || onDelete || onDownload || onViewDetails);
105
+
106
+ const nameClass = [
107
+ "sds-attachment-row__name",
108
+ truncateName ? "sds-attachment-row__name--truncate" : "",
109
+ ].filter(Boolean).join(" ");
110
+ }
111
+
112
+ <div class=`sds-attachment-row ${extraClass}`.trim()>
113
+ <if(icon)>
114
+ <span class="sds-attachment-row__icon" aria-hidden="true">
115
+ <sds-icon><${renderable(icon)}/></sds-icon>
116
+ </span>
117
+ </if>
118
+
119
+ <div class="sds-attachment-row__info">
120
+ <macro|{ }| name="theName">
121
+ <sds-text key="name" as="span" variant="bodyMd" class=nameClass>${name}</sds-text>
122
+ </macro>
123
+
124
+ <if(truncateName && state.truncated)>
125
+ <sds-tooltip content=name class="sds-attachment-row__name-tooltip">
126
+ <theName/>
127
+ </sds-tooltip>
128
+ </if>
129
+ <else>
130
+ <theName/>
131
+ </else>
132
+
133
+ <if(meta)>
134
+ <sds-text as="span" variant="bodySm" class="sds-attachment-row__meta">${meta}</sds-text>
135
+ </if>
136
+ </div>
137
+
138
+ <if(hasActions)>
139
+ <div class="sds-attachment-row__actions">
140
+ <if(badge)>
141
+ // A badge arrives two ways: as content to render, or as a plain status word like
142
+ // "Enviado". A string handed to `<${...}/>` is read as a TAG NAME, so the word
143
+ // vanished with no error — the same guard Popover and Table already apply.
144
+ <if(typeof badge === "string")>${badge}</if>
145
+ <else><${renderable(badge)}/></else>
146
+ </if>
147
+ <if(onDelete)>
148
+ <sds-icon-button aria-label=deleteLabel onClick=() => component.handleDelete()
149
+ isLoading=state.deleting disabled=disabledDelete>
150
+ <@icon><${deleteIcon}/></@icon>
151
+ </sds-icon-button>
152
+ </if>
153
+ <if(onDownload)>
154
+ <sds-icon-button aria-label=downloadLabel onClick=() => component.handleDownload()
155
+ isLoading=state.downloading disabled=disabledDownload>
156
+ <@icon><${downloadIcon}/></@icon>
157
+ </sds-icon-button>
158
+ </if>
159
+ <if(onViewDetails)>
160
+ <sds-icon-button variant="neutral" aria-label=detailsLabel onClick=onViewDetails
161
+ disabled=disabledViewDetails>
162
+ <@icon><${detailsIcon}/></@icon>
163
+ </sds-icon-button>
164
+ </if>
165
+ </div>
166
+ </if>
167
+ </div>
@@ -0,0 +1,110 @@
1
+ /* SDS Avatar.
2
+ Ported from kdls-design-system v2.5.1 (0f3edf8) components/Avatar/Avatar.css
3
+ by scripts/port-css.mjs. Divergences beyond the mechanical rename are noted inline.
4
+
5
+ Rules removed with the brand families they targeted:
6
+ [data-color-scheme="lya"] .sds-avatar--clickable
7
+ [data-color-scheme="lya"] .sds-avatar--clickable::before
8
+ [data-color-scheme="lya"] .sds-avatar--clickable:hover
9
+ [data-color-scheme="lya"] .sds-avatar--clickable:hover::before
10
+ [data-color-scheme="lya"] .sds-avatar--clickable:active
11
+ [data-color-scheme="lya"] .sds-avatar--clickable:active::before
12
+ [data-color-scheme="lya"] .sds-avatar__placeholder
13
+ */.sds-avatar {
14
+ position: relative;
15
+ display: inline-flex;
16
+ align-items: center;
17
+ justify-content: center;
18
+ border-radius: var(--radius-full);
19
+ overflow: visible;
20
+ flex-shrink: 0;
21
+ width: var(--avatar-size, 40px);
22
+ height: var(--avatar-size, 40px);
23
+ /* The root is a <button> whenever it has a click handler, and SDS ships no element
24
+ reset — so a clickable avatar was wearing the browser's button chrome: a 2px outset
25
+ border, a #efefef ButtonFace ground and 1px/6px of padding, measured in Chrome. That
26
+ squeezes the photo inside the circle and paints a grey ring around it. IconButton and
27
+ Badge already reset this; Avatar did not. */
28
+ appearance: none;
29
+ border: none;
30
+ padding: 0;
31
+ background: transparent;
32
+ font: inherit;
33
+ color: inherit;
34
+ }
35
+ /* ── Sizes ── */
36
+ .sds-avatar--sm { --avatar-size: var(--spacing-10); }
37
+ .sds-avatar--md { --avatar-size: var(--spacing-12); }
38
+ .sds-avatar--lg { --avatar-size: var(--spacing-14); }
39
+ .sds-avatar__image {
40
+ position: relative; /* stays above the lya gradient ring ::before (position:absolute
41
+ always stacks above static content, even when it comes first in the DOM) */
42
+ width: 100%;
43
+ height: 100%;
44
+ border-radius: var(--radius-full);
45
+ object-fit: cover;
46
+ display: block;
47
+ }
48
+ .sds-avatar__placeholder {
49
+ position: relative; /* same reason as __image above */
50
+ width: 100%;
51
+ height: 100%;
52
+ border-radius: var(--radius-full);
53
+ background: var(--color-primary-50);
54
+ color: var(--color-on-primary);
55
+ display: flex;
56
+ align-items: center;
57
+ justify-content: center;
58
+ font-family: var(--font-family-base);
59
+ font-weight: var(--font-weight-semibold);
60
+ font-size: calc(var(--avatar-size) * 0.46); /* proporcional ao size */
61
+ user-select: none;
62
+ }
63
+ /* ── Interactive states (onClick provided) ── */
64
+ .sds-avatar--clickable {
65
+ cursor: pointer;
66
+ outline: none;
67
+ transition: filter 120ms ease, box-shadow 120ms ease;
68
+ }
69
+ .sds-avatar--clickable:hover {
70
+ filter: brightness(0.92);
71
+ box-shadow: 0 0 0 2px var(--component-bg-color), 0 0 0 4px var(--color-primary-40-solid);
72
+ }
73
+ .sds-avatar--clickable:active {
74
+ filter: brightness(0.78);
75
+ box-shadow: 0 0 0 2px var(--component-bg-color), 0 0 0 4px var(--color-primary-50-solid);
76
+ }
77
+ .sds-avatar--clickable:focus-visible {
78
+ box-shadow: var(--focus-ring);
79
+ }
80
+ /* Eleven rules used to sit here (and in ten other stylesheets) guarding on
81
+ `[data-color-scheme="neutral"][data-theme="dark"]`, repainting the text on a filled
82
+ primary surface. Not one of them could ever match: `neutral` is the install default, and
83
+ a value equal to the default is written by REMOVING the attribute, never by setting it.
84
+ They were the patch for `--color-on-primary` being a fixed white while
85
+ `--color-primary-50` inverts — which is fixed at the token now, so the patch is gone. */
86
+ /* ── Image and fallback, without JavaScript ────────────────────────────────────
87
+ KDLS tracked a broken image in component state: an onError handler set imgFailed and
88
+ the initials rendered instead. That is one serialised component per avatar — and an
89
+ avatar list is exactly the place that repeats.
90
+
91
+ Both are rendered here instead, the initials underneath and the image over them. A
92
+ broken <img> paints nothing, so the initials show through on their own. No handler, no
93
+ state, and it works before any JavaScript arrives — which is also when a slow image is
94
+ most likely to be missing. */
95
+ .sds-avatar__placeholder {
96
+ position: absolute;
97
+ inset: 0;
98
+ display: inline-flex;
99
+ align-items: center;
100
+ justify-content: center;
101
+ }
102
+
103
+ .sds-avatar__image {
104
+ position: relative;
105
+ z-index: 1;
106
+ width: 100%;
107
+ height: 100%;
108
+ object-fit: cover;
109
+ border-radius: inherit;
110
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The Avatar vocabulary.
3
+ * Ported from kdls-design-system v2.5.1 (0f3edf8) components/Avatar/Avatar.tsx.
4
+ */
5
+ export const AVATAR_SIZES = ['sm', 'md', 'lg'];
6
+
7
+ export function assertAvatarSize(size) {
8
+ if (!AVATAR_SIZES.includes(size)) {
9
+ throw new Error(`Unknown avatar size: "${size}". Allowed: ${AVATAR_SIZES.join(', ')}`);
10
+ }
11
+ return size;
12
+ }