@nldd/design-system 0.8.49 → 0.8.51

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 (245) hide show
  1. package/dist/components/actions/button/button.styles.d.ts.map +1 -1
  2. package/dist/components/actions/button/button.styles.js +1 -0
  3. package/dist/components/actions/button/button.styles.js.map +1 -1
  4. package/dist/components/actions/icon-button/icon-button.styles.d.ts.map +1 -1
  5. package/dist/components/actions/icon-button/icon-button.styles.js +1 -0
  6. package/dist/components/actions/icon-button/icon-button.styles.js.map +1 -1
  7. package/dist/components/components.js +93 -86
  8. package/dist/components/content/code-viewer/code-viewer.d.ts +37 -0
  9. package/dist/components/content/code-viewer/code-viewer.d.ts.map +1 -1
  10. package/dist/components/content/code-viewer/code-viewer.i18n.d.ts +8 -0
  11. package/dist/components/content/code-viewer/code-viewer.i18n.d.ts.map +1 -0
  12. package/dist/components/content/code-viewer/code-viewer.i18n.js +7 -0
  13. package/dist/components/content/code-viewer/code-viewer.i18n.js.map +1 -0
  14. package/dist/components/content/code-viewer/code-viewer.js +104 -1
  15. package/dist/components/content/code-viewer/code-viewer.js.map +1 -1
  16. package/dist/components/content/code-viewer/code-viewer.styles.d.ts.map +1 -1
  17. package/dist/components/content/code-viewer/code-viewer.styles.js +112 -1
  18. package/dist/components/content/code-viewer/code-viewer.styles.js.map +1 -1
  19. package/dist/components/content/code-viewer/code-viewer.template.d.ts.map +1 -1
  20. package/dist/components/content/code-viewer/code-viewer.template.js +50 -3
  21. package/dist/components/content/code-viewer/code-viewer.template.js.map +1 -1
  22. package/dist/components/content/icon/icon-aliases.d.ts.map +1 -1
  23. package/dist/components/content/icon/icon-aliases.js +17 -0
  24. package/dist/components/content/icon/icon-aliases.js.map +1 -1
  25. package/dist/components/content/icon/icon-registry.d.ts.map +1 -1
  26. package/dist/components/content/icon/icon-registry.js +12 -0
  27. package/dist/components/content/icon/icon-registry.js.map +1 -1
  28. package/dist/components/content/image/image.d.ts +151 -0
  29. package/dist/components/content/image/image.d.ts.map +1 -0
  30. package/dist/components/content/image/image.i18n.d.ts +8 -0
  31. package/dist/components/content/image/image.i18n.d.ts.map +1 -0
  32. package/dist/components/content/image/image.i18n.js +7 -0
  33. package/dist/components/content/image/image.i18n.js.map +1 -0
  34. package/dist/components/content/image/image.js +320 -0
  35. package/dist/components/content/image/image.js.map +1 -0
  36. package/dist/components/content/image/image.styles.d.ts +2 -0
  37. package/dist/components/content/image/image.styles.d.ts.map +1 -0
  38. package/dist/components/content/image/image.styles.js +402 -0
  39. package/dist/components/content/image/image.styles.js.map +1 -0
  40. package/dist/components/content/image/image.template.d.ts +3 -0
  41. package/dist/components/content/image/image.template.d.ts.map +1 -0
  42. package/dist/components/content/image/image.template.js +109 -0
  43. package/dist/components/content/image/image.template.js.map +1 -0
  44. package/dist/components/content/image/lqip-encoder-element.d.ts +39 -0
  45. package/dist/components/content/image/lqip-encoder-element.d.ts.map +1 -0
  46. package/dist/components/content/image/lqip-encoder-element.js +240 -0
  47. package/dist/components/content/image/lqip-encoder-element.js.map +1 -0
  48. package/dist/components/content/image/lqip-encoder.d.ts +59 -0
  49. package/dist/components/content/image/lqip-encoder.d.ts.map +1 -0
  50. package/dist/components/content/image/lqip-encoder.js +279 -0
  51. package/dist/components/content/image/lqip-encoder.js.map +1 -0
  52. package/dist/components/content/rich-text/rich-text.d.ts +9 -0
  53. package/dist/components/content/rich-text/rich-text.d.ts.map +1 -1
  54. package/dist/components/content/rich-text/rich-text.i18n.d.ts +5 -0
  55. package/dist/components/content/rich-text/rich-text.i18n.d.ts.map +1 -0
  56. package/dist/components/content/rich-text/rich-text.i18n.js +4 -0
  57. package/dist/components/content/rich-text/rich-text.i18n.js.map +1 -0
  58. package/dist/components/content/rich-text/rich-text.js +61 -0
  59. package/dist/components/content/rich-text/rich-text.js.map +1 -1
  60. package/dist/components/content/tooltip/tooltip.d.ts +10 -0
  61. package/dist/components/content/tooltip/tooltip.d.ts.map +1 -1
  62. package/dist/components/content/tooltip/tooltip.js +74 -2
  63. package/dist/components/content/tooltip/tooltip.js.map +1 -1
  64. package/dist/components/{index-BVOcPb8I.js → index-DYnIl3ts.js} +5435 -2205
  65. package/dist/components/index.d.ts +5 -0
  66. package/dist/components/index.d.ts.map +1 -1
  67. package/dist/components/index.js +5 -0
  68. package/dist/components/index.js.map +1 -1
  69. package/dist/components/inputs/checkbox/checkbox.styles.d.ts.map +1 -1
  70. package/dist/components/inputs/checkbox/checkbox.styles.js +1 -0
  71. package/dist/components/inputs/checkbox/checkbox.styles.js.map +1 -1
  72. package/dist/components/inputs/combo-box/combo-box.styles.d.ts.map +1 -1
  73. package/dist/components/inputs/combo-box/combo-box.styles.js +4 -1
  74. package/dist/components/inputs/combo-box/combo-box.styles.js.map +1 -1
  75. package/dist/components/inputs/dropdown/dropdown.styles.d.ts.map +1 -1
  76. package/dist/components/inputs/dropdown/dropdown.styles.js +1 -0
  77. package/dist/components/inputs/dropdown/dropdown.styles.js.map +1 -1
  78. package/dist/components/inputs/multi-line-text-field/multi-line-text-field.styles.d.ts.map +1 -1
  79. package/dist/components/inputs/multi-line-text-field/multi-line-text-field.styles.js +4 -1
  80. package/dist/components/inputs/multi-line-text-field/multi-line-text-field.styles.js.map +1 -1
  81. package/dist/components/inputs/password-field/password-field.styles.d.ts.map +1 -1
  82. package/dist/components/inputs/password-field/password-field.styles.js +4 -1
  83. package/dist/components/inputs/password-field/password-field.styles.js.map +1 -1
  84. package/dist/components/inputs/radio-button/radio-button.styles.d.ts.map +1 -1
  85. package/dist/components/inputs/radio-button/radio-button.styles.js +1 -0
  86. package/dist/components/inputs/radio-button/radio-button.styles.js.map +1 -1
  87. package/dist/components/inputs/search-field/search-field.styles.d.ts.map +1 -1
  88. package/dist/components/inputs/search-field/search-field.styles.js +4 -1
  89. package/dist/components/inputs/search-field/search-field.styles.js.map +1 -1
  90. package/dist/components/inputs/segmented-control/segmented-control.styles.d.ts.map +1 -1
  91. package/dist/components/inputs/segmented-control/segmented-control.styles.js +1 -0
  92. package/dist/components/inputs/segmented-control/segmented-control.styles.js.map +1 -1
  93. package/dist/components/inputs/stepper/stepper.styles.d.ts.map +1 -1
  94. package/dist/components/inputs/stepper/stepper.styles.js +1 -0
  95. package/dist/components/inputs/stepper/stepper.styles.js.map +1 -1
  96. package/dist/components/inputs/switch/switch.styles.d.ts.map +1 -1
  97. package/dist/components/inputs/switch/switch.styles.js +1 -0
  98. package/dist/components/inputs/switch/switch.styles.js.map +1 -1
  99. package/dist/components/inputs/text-field/text-field.styles.d.ts.map +1 -1
  100. package/dist/components/inputs/text-field/text-field.styles.js +4 -1
  101. package/dist/components/inputs/text-field/text-field.styles.js.map +1 -1
  102. package/dist/components/inputs/toggle-button/toggle-button.d.ts +1 -1
  103. package/dist/components/inputs/toggle-button/toggle-button.d.ts.map +1 -1
  104. package/dist/components/inputs/toggle-button/toggle-button.js +22 -14
  105. package/dist/components/inputs/toggle-button/toggle-button.js.map +1 -1
  106. package/dist/components/inputs/toggle-button/toggle-button.styles.d.ts.map +1 -1
  107. package/dist/components/inputs/toggle-button/toggle-button.styles.js +16 -11
  108. package/dist/components/inputs/toggle-button/toggle-button.styles.js.map +1 -1
  109. package/dist/components/inputs/toggle-button/toggle-button.template.d.ts.map +1 -1
  110. package/dist/components/inputs/toggle-button/toggle-button.template.js +32 -16
  111. package/dist/components/inputs/toggle-button/toggle-button.template.js.map +1 -1
  112. package/dist/components/inputs/token/token.styles.d.ts.map +1 -1
  113. package/dist/components/inputs/token/token.styles.js +1 -0
  114. package/dist/components/inputs/token/token.styles.js.map +1 -1
  115. package/dist/components/layout/box/box.d.ts +6 -0
  116. package/dist/components/layout/box/box.d.ts.map +1 -1
  117. package/dist/components/layout/box/box.js +12 -1
  118. package/dist/components/layout/box/box.js.map +1 -1
  119. package/dist/components/layout/box/box.styles.d.ts.map +1 -1
  120. package/dist/components/layout/box/box.styles.js +40 -1
  121. package/dist/components/layout/box/box.styles.js.map +1 -1
  122. package/dist/components/layout/collection/collection.d.ts +1 -0
  123. package/dist/components/layout/collection/collection.d.ts.map +1 -1
  124. package/dist/components/layout/collection/collection.i18n.d.ts +1 -0
  125. package/dist/components/layout/collection/collection.i18n.d.ts.map +1 -1
  126. package/dist/components/layout/collection/collection.i18n.js +1 -0
  127. package/dist/components/layout/collection/collection.i18n.js.map +1 -1
  128. package/dist/components/layout/collection/collection.js +14 -0
  129. package/dist/components/layout/collection/collection.js.map +1 -1
  130. package/dist/components/layout/collection/collection.styles.d.ts.map +1 -1
  131. package/dist/components/layout/collection/collection.styles.js +27 -0
  132. package/dist/components/layout/collection/collection.styles.js.map +1 -1
  133. package/dist/components/layout/collection/collection.template.d.ts.map +1 -1
  134. package/dist/components/layout/collection/collection.template.js +12 -4
  135. package/dist/components/layout/collection/collection.template.js.map +1 -1
  136. package/dist/components/lists-and-menus/list/list.d.ts +10 -3
  137. package/dist/components/lists-and-menus/list/list.d.ts.map +1 -1
  138. package/dist/components/lists-and-menus/list/list.js +8 -3
  139. package/dist/components/lists-and-menus/list/list.js.map +1 -1
  140. package/dist/components/lists-and-menus/list/list.styles.d.ts.map +1 -1
  141. package/dist/components/lists-and-menus/list/list.styles.js +33 -6
  142. package/dist/components/lists-and-menus/list/list.styles.js.map +1 -1
  143. package/dist/components/lists-and-menus/list-item/list-item.js +1 -1
  144. package/dist/components/lists-and-menus/list-item/list-item.js.map +1 -1
  145. package/dist/components/lists-and-menus/menu/menu.styles.d.ts.map +1 -1
  146. package/dist/components/lists-and-menus/menu/menu.styles.js +1 -0
  147. package/dist/components/lists-and-menus/menu/menu.styles.js.map +1 -1
  148. package/dist/components/navigation/document-tab-bar/document-tab-bar.styles.d.ts.map +1 -1
  149. package/dist/components/navigation/document-tab-bar/document-tab-bar.styles.js +1 -0
  150. package/dist/components/navigation/document-tab-bar/document-tab-bar.styles.js.map +1 -1
  151. package/dist/components/navigation/menu-bar/menu-bar.styles.d.ts.map +1 -1
  152. package/dist/components/navigation/menu-bar/menu-bar.styles.js +1 -0
  153. package/dist/components/navigation/menu-bar/menu-bar.styles.js.map +1 -1
  154. package/dist/components/navigation/menu-bar-item/menu-bar-item.styles.d.ts.map +1 -1
  155. package/dist/components/navigation/menu-bar-item/menu-bar-item.styles.js +1 -0
  156. package/dist/components/navigation/menu-bar-item/menu-bar-item.styles.js.map +1 -1
  157. package/dist/components/navigation/pagination/pagination.styles.d.ts.map +1 -1
  158. package/dist/components/navigation/pagination/pagination.styles.js +1 -0
  159. package/dist/components/navigation/pagination/pagination.styles.js.map +1 -1
  160. package/dist/components/navigation/tab-bar/tab-bar.styles.d.ts.map +1 -1
  161. package/dist/components/navigation/tab-bar/tab-bar.styles.js +1 -0
  162. package/dist/components/navigation/tab-bar/tab-bar.styles.js.map +1 -1
  163. package/dist/components/navigation/top-navigation-bar/top-navigation-bar.styles.d.ts.map +1 -1
  164. package/dist/components/navigation/top-navigation-bar/top-navigation-bar.styles.js +10 -1
  165. package/dist/components/navigation/top-navigation-bar/top-navigation-bar.styles.js.map +1 -1
  166. package/dist/components/{prism-python-C1D7CYBc.js → prism-python-C9GXiQLy.js} +1 -1
  167. package/dist/components/{prism-rust-DI5vrP6V.js → prism-rust-De_U8Otq.js} +1 -1
  168. package/dist/components/{prism-typescript-DBgOPJ4M.js → prism-typescript-BJbUDHsa.js} +1 -1
  169. package/dist/components/sample-images/butterfly-1200.jpg +0 -0
  170. package/dist/components/sample-images/butterfly-1600.jpg +0 -0
  171. package/dist/components/sample-images/butterfly-480.jpg +0 -0
  172. package/dist/components/sample-images/butterfly-960.jpg +0 -0
  173. package/dist/components/status-and-feedback/badge/badge.d.ts +2 -2
  174. package/dist/components/status-and-feedback/badge/badge.d.ts.map +1 -1
  175. package/dist/components/status-and-feedback/badge/badge.js +1 -1
  176. package/dist/components/status-and-feedback/badge/badge.js.map +1 -1
  177. package/dist/components/status-and-feedback/badge/badge.styles.d.ts.map +1 -1
  178. package/dist/components/status-and-feedback/badge/badge.styles.js +126 -1
  179. package/dist/components/status-and-feedback/badge/badge.styles.js.map +1 -1
  180. package/dist/components/status-and-feedback/banner/banner.d.ts +89 -0
  181. package/dist/components/status-and-feedback/banner/banner.d.ts.map +1 -0
  182. package/dist/components/status-and-feedback/banner/banner.i18n.d.ts +5 -0
  183. package/dist/components/status-and-feedback/banner/banner.i18n.d.ts.map +1 -0
  184. package/dist/components/status-and-feedback/banner/banner.i18n.js +4 -0
  185. package/dist/components/status-and-feedback/banner/banner.i18n.js.map +1 -0
  186. package/dist/components/status-and-feedback/banner/banner.js +206 -0
  187. package/dist/components/status-and-feedback/banner/banner.js.map +1 -0
  188. package/dist/components/status-and-feedback/banner/banner.styles.d.ts +2 -0
  189. package/dist/components/status-and-feedback/banner/banner.styles.d.ts.map +1 -0
  190. package/dist/components/status-and-feedback/banner/banner.styles.js +160 -0
  191. package/dist/components/status-and-feedback/banner/banner.styles.js.map +1 -0
  192. package/dist/components/status-and-feedback/banner/banner.template.d.ts +3 -0
  193. package/dist/components/status-and-feedback/banner/banner.template.d.ts.map +1 -0
  194. package/dist/components/status-and-feedback/banner/banner.template.js +51 -0
  195. package/dist/components/status-and-feedback/banner/banner.template.js.map +1 -0
  196. package/dist/components/status-and-feedback/progress/progress.d.ts +77 -0
  197. package/dist/components/status-and-feedback/progress/progress.d.ts.map +1 -0
  198. package/dist/components/status-and-feedback/progress/progress.i18n.d.ts +5 -0
  199. package/dist/components/status-and-feedback/progress/progress.i18n.d.ts.map +1 -0
  200. package/dist/components/status-and-feedback/progress/progress.i18n.js +4 -0
  201. package/dist/components/status-and-feedback/progress/progress.i18n.js.map +1 -0
  202. package/dist/components/status-and-feedback/progress/progress.js +137 -0
  203. package/dist/components/status-and-feedback/progress/progress.js.map +1 -0
  204. package/dist/components/status-and-feedback/progress/progress.styles.d.ts +2 -0
  205. package/dist/components/status-and-feedback/progress/progress.styles.d.ts.map +1 -0
  206. package/dist/components/status-and-feedback/progress/progress.styles.js +57 -0
  207. package/dist/components/status-and-feedback/progress/progress.styles.js.map +1 -0
  208. package/dist/components/status-and-feedback/progress/progress.template.d.ts +4 -0
  209. package/dist/components/status-and-feedback/progress/progress.template.d.ts.map +1 -0
  210. package/dist/components/status-and-feedback/progress/progress.template.js +26 -0
  211. package/dist/components/status-and-feedback/progress/progress.template.js.map +1 -0
  212. package/dist/components/status-and-feedback/progress-bar/progress-bar.d.ts +131 -0
  213. package/dist/components/status-and-feedback/progress-bar/progress-bar.d.ts.map +1 -0
  214. package/dist/components/status-and-feedback/progress-bar/progress-bar.i18n.d.ts +8 -0
  215. package/dist/components/status-and-feedback/progress-bar/progress-bar.i18n.d.ts.map +1 -0
  216. package/dist/components/status-and-feedback/progress-bar/progress-bar.i18n.js +7 -0
  217. package/dist/components/status-and-feedback/progress-bar/progress-bar.i18n.js.map +1 -0
  218. package/dist/components/status-and-feedback/progress-bar/progress-bar.js +426 -0
  219. package/dist/components/status-and-feedback/progress-bar/progress-bar.js.map +1 -0
  220. package/dist/components/status-and-feedback/progress-bar/progress-bar.styles.d.ts +3 -0
  221. package/dist/components/status-and-feedback/progress-bar/progress-bar.styles.d.ts.map +1 -0
  222. package/dist/components/status-and-feedback/progress-bar/progress-bar.styles.js +400 -0
  223. package/dist/components/status-and-feedback/progress-bar/progress-bar.styles.js.map +1 -0
  224. package/dist/components/status-and-feedback/progress-bar/progress-bar.template.d.ts +3 -0
  225. package/dist/components/status-and-feedback/progress-bar/progress-bar.template.d.ts.map +1 -0
  226. package/dist/components/status-and-feedback/progress-bar/progress-bar.template.js +60 -0
  227. package/dist/components/status-and-feedback/progress-bar/progress-bar.template.js.map +1 -0
  228. package/dist/components/status-and-feedback/progress-circle/progress-circle.d.ts +132 -0
  229. package/dist/components/status-and-feedback/progress-circle/progress-circle.d.ts.map +1 -0
  230. package/dist/components/status-and-feedback/progress-circle/progress-circle.i18n.d.ts +8 -0
  231. package/dist/components/status-and-feedback/progress-circle/progress-circle.i18n.d.ts.map +1 -0
  232. package/dist/components/status-and-feedback/progress-circle/progress-circle.i18n.js +7 -0
  233. package/dist/components/status-and-feedback/progress-circle/progress-circle.i18n.js.map +1 -0
  234. package/dist/components/status-and-feedback/progress-circle/progress-circle.js +437 -0
  235. package/dist/components/status-and-feedback/progress-circle/progress-circle.js.map +1 -0
  236. package/dist/components/status-and-feedback/progress-circle/progress-circle.styles.d.ts +3 -0
  237. package/dist/components/status-and-feedback/progress-circle/progress-circle.styles.d.ts.map +1 -0
  238. package/dist/components/status-and-feedback/progress-circle/progress-circle.styles.js +245 -0
  239. package/dist/components/status-and-feedback/progress-circle/progress-circle.styles.js.map +1 -0
  240. package/dist/components/status-and-feedback/progress-circle/progress-circle.template.d.ts +14 -0
  241. package/dist/components/status-and-feedback/progress-circle/progress-circle.template.d.ts.map +1 -0
  242. package/dist/components/status-and-feedback/progress-circle/progress-circle.template.js +115 -0
  243. package/dist/components/status-and-feedback/progress-circle/progress-circle.template.js.map +1 -0
  244. package/dist/css/settings.css +346 -18
  245. package/package.json +21 -1
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Storybook UI element wrapping `encodeLqip()` in a file-picker. Kept in a
3
+ * separate module from the pure encoder so consumers importing just the
4
+ * functions (e.g. a Node build pipeline) don't accidentally register the
5
+ * `nldd-lqip-encoder` custom element into their registry.
6
+ *
7
+ * Intentionally NOT exported from `src/components/index.ts` — this is a
8
+ * developer tool, not a design-system component.
9
+ */
10
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
11
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
12
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
13
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
14
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
15
+ };
16
+ import { LitElement, html, css } from 'lit';
17
+ import { customElement, property, state } from 'lit/decorators.js';
18
+ import { encodeLqip } from './lqip-encoder.js';
19
+ import './image.js';
20
+ /* --------------------------------------------------------------------- *
21
+ * i18n
22
+ * --------------------------------------------------------------------- */
23
+ export const nlddLqipEncoderTranslations = {
24
+ 'components.lqip-encoder.upload-label': 'Kies een afbeelding',
25
+ 'components.lqip-encoder.file-prefix-text': 'Bestand:',
26
+ 'components.lqip-encoder.copy-instruction-text': 'Kopieer deze waarde naar je lqip attribuut:',
27
+ 'components.lqip-encoder.preview-heading-text': 'Visuele controle:',
28
+ 'components.lqip-encoder.preview-lqip-label': 'LQIP placeholder',
29
+ 'components.lqip-encoder.preview-image-label': 'Originele afbeelding',
30
+ };
31
+ /* --------------------------------------------------------------------- *
32
+ * Element
33
+ * --------------------------------------------------------------------- */
34
+ let NLDDLqipEncoder = class NLDDLqipEncoder extends LitElement {
35
+ constructor() {
36
+ super(...arguments);
37
+ this.translations = {};
38
+ this._lqip = null;
39
+ this._imageUrl = '';
40
+ this._filename = '';
41
+ this._aspectRatio = '16/9';
42
+ this._error = '';
43
+ this._handleFileChange = async (e) => {
44
+ const input = e.target;
45
+ const file = input.files?.[0];
46
+ if (!file)
47
+ return;
48
+ this._error = '';
49
+ try {
50
+ // Free the previous object URL before allocating a new one.
51
+ if (this._imageUrl)
52
+ URL.revokeObjectURL(this._imageUrl);
53
+ this._imageUrl = URL.createObjectURL(file);
54
+ this._filename = file.name;
55
+ // Decode once to read dimensions for the comparison preview, then
56
+ // reuse the bitmap for the actual encode so the file isn't decoded
57
+ // twice. We pass an ImageBitmap (not the File) so encodeLqip treats
58
+ // it as caller-owned and won't close it — hence the try/finally
59
+ // here closes it ourselves, including when encodeLqip throws.
60
+ const bitmap = await createImageBitmap(file);
61
+ try {
62
+ this._aspectRatio = `${bitmap.width}/${bitmap.height}`;
63
+ this._lqip = await encodeLqip(bitmap);
64
+ }
65
+ finally {
66
+ bitmap.close();
67
+ }
68
+ }
69
+ catch (err) {
70
+ this._error = err instanceof Error ? err.message : String(err);
71
+ this._lqip = null;
72
+ }
73
+ };
74
+ }
75
+ _t(key) {
76
+ return this.translations[key] ?? nlddLqipEncoderTranslations[key];
77
+ }
78
+ disconnectedCallback() {
79
+ super.disconnectedCallback();
80
+ if (this._imageUrl)
81
+ URL.revokeObjectURL(this._imageUrl);
82
+ }
83
+ render() {
84
+ return html `
85
+ <div>
86
+ <label class="encoder__upload-label">
87
+ ${this._t('components.lqip-encoder.upload-label')}
88
+ <input class="encoder__upload-input"
89
+ type="file"
90
+ accept="image/*"
91
+ @change=${this._handleFileChange}
92
+ >
93
+ </label>
94
+ </div>
95
+ ${this._error ? html `<div class="encoder__error" role="alert">${this._error}</div>` : ''}
96
+ ${this._lqip !== null ? html `
97
+ <div class="encoder__result">
98
+ <div>${this._t('components.lqip-encoder.file-prefix-text')} <strong>${this._filename}</strong></div>
99
+ <div>${this._t('components.lqip-encoder.copy-instruction-text')}</div>
100
+ <div class="encoder__code">lqip="${this._lqip}"</div>
101
+ </div>
102
+ <div class="encoder__preview">
103
+ <div>${this._t('components.lqip-encoder.preview-heading-text')}</div>
104
+ <div class="encoder__preview-pair">
105
+ <div class="encoder__preview-item">
106
+ <nldd-image
107
+ alt=${this._t('components.lqip-encoder.preview-lqip-label')}
108
+ aspect-ratio=${this._aspectRatio}
109
+ lqip=${this._lqip}
110
+ ></nldd-image>
111
+ <span class="encoder__preview-item-label">${this._t('components.lqip-encoder.preview-lqip-label')}</span>
112
+ </div>
113
+ <div class="encoder__preview-item">
114
+ <nldd-image
115
+ src=${this._imageUrl}
116
+ alt=${this._filename}
117
+ aspect-ratio=${this._aspectRatio}
118
+ lqip=${this._lqip}
119
+ ></nldd-image>
120
+ <span class="encoder__preview-item-label">${this._t('components.lqip-encoder.preview-image-label')}</span>
121
+ </div>
122
+ </div>
123
+ </div>
124
+ ` : ''}
125
+ `;
126
+ }
127
+ };
128
+ NLDDLqipEncoder.styles = css `
129
+ :host {
130
+ box-sizing: border-box;
131
+ display: flex;
132
+ flex-direction: column;
133
+ gap: var(--primitives-space-16);
134
+ padding: var(--primitives-space-16);
135
+ border: var(--primitives-border-width-thin) solid var(--semantics-content-secondary-color);
136
+ border-radius: var(--primitives-corner-radius-md);
137
+ background-color: var(--semantics-surfaces-background-color);
138
+ color: var(--semantics-content-color);
139
+ font: var(--primitives-font-body-md-regular-snug);
140
+ }
141
+
142
+ .encoder__upload-label {
143
+ display: inline-block;
144
+ padding: var(--primitives-space-8) var(--primitives-space-16);
145
+ border-radius: var(--primitives-corner-radius-md);
146
+ background-color: var(--semantics-buttons-accent-filled-background-color);
147
+ color: var(--semantics-buttons-accent-filled-content-color);
148
+ cursor: pointer;
149
+ font: var(--primitives-font-body-md-regular-snug);
150
+ }
151
+
152
+ /* The native <input type="file"> is visually hidden but still focusable;
153
+ reflect its focus state on the surrounding label so keyboard users
154
+ see where focus is. Same focus-ring tokens the rest of the design
155
+ system uses on buttons. */
156
+ .encoder__upload-label:focus-within {
157
+ outline: var(--semantics-focus-ring-outline);
158
+ outline-offset: var(--semantics-focus-ring-outline-offset);
159
+ box-shadow: var(--semantics-focus-ring-box-shadow);
160
+ }
161
+
162
+ .encoder__upload-input {
163
+ position: absolute;
164
+ width: 1px;
165
+ height: 1px;
166
+ overflow: hidden;
167
+ clip-path: inset(50%);
168
+ white-space: nowrap;
169
+ }
170
+
171
+ .encoder__result {
172
+ display: flex;
173
+ flex-direction: column;
174
+ gap: var(--primitives-space-8);
175
+ }
176
+
177
+ .encoder__code {
178
+ padding: var(--primitives-space-8) var(--primitives-space-12);
179
+ border-radius: var(--primitives-corner-radius-sm);
180
+ background-color: var(--semantics-surfaces-tinted-background-color);
181
+ font: var(--primitives-font-monospace-sm-regular-snug);
182
+ user-select: all;
183
+ word-break: break-all;
184
+ }
185
+
186
+ .encoder__preview {
187
+ display: flex;
188
+ flex-direction: column;
189
+ gap: var(--primitives-space-8);
190
+ }
191
+
192
+ .encoder__preview-pair {
193
+ display: grid;
194
+ grid-template-columns: 1fr 1fr;
195
+ gap: var(--primitives-space-16);
196
+ max-width: calc(var(--primitives-area-480) * 2 + var(--primitives-space-16));
197
+ }
198
+
199
+ .encoder__preview-item {
200
+ display: flex;
201
+ flex-direction: column;
202
+ gap: var(--primitives-space-4);
203
+ min-width: 0;
204
+ }
205
+
206
+ .encoder__preview-item-label {
207
+ font: var(--primitives-font-body-sm-regular-tight);
208
+ color: var(--semantics-content-secondary-color);
209
+ }
210
+
211
+ .encoder__error {
212
+ color: var(--semantics-content-color);
213
+ background-color: var(--components-banner-critical-background-color);
214
+ padding: var(--primitives-space-8) var(--primitives-space-12);
215
+ border-radius: var(--primitives-corner-radius-sm);
216
+ }
217
+ `;
218
+ __decorate([
219
+ property({ type: Object })
220
+ ], NLDDLqipEncoder.prototype, "translations", void 0);
221
+ __decorate([
222
+ state()
223
+ ], NLDDLqipEncoder.prototype, "_lqip", void 0);
224
+ __decorate([
225
+ state()
226
+ ], NLDDLqipEncoder.prototype, "_imageUrl", void 0);
227
+ __decorate([
228
+ state()
229
+ ], NLDDLqipEncoder.prototype, "_filename", void 0);
230
+ __decorate([
231
+ state()
232
+ ], NLDDLqipEncoder.prototype, "_aspectRatio", void 0);
233
+ __decorate([
234
+ state()
235
+ ], NLDDLqipEncoder.prototype, "_error", void 0);
236
+ NLDDLqipEncoder = __decorate([
237
+ customElement('nldd-lqip-encoder')
238
+ ], NLDDLqipEncoder);
239
+ export { NLDDLqipEncoder };
240
+ //# sourceMappingURL=lqip-encoder-element.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lqip-encoder-element.js","sourceRoot":"","sources":["../../../../src/components/content/image/lqip-encoder-element.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Encoder for the CSS-only multi-color LQIP technique used by nldd-image.
3
+ *
4
+ * Takes an image (File, ImageBitmap, or HTMLImageElement) and returns the
5
+ * comma-separated string the `lqip` attribute expects:
6
+ *
7
+ * "base,c1,c2,c3,c4,c5,c6"
8
+ *
9
+ * — seven 0-255 bytes, each packing an 8-bit Oklab triplet (2 bits L,
10
+ * 3 bits a, 3 bits b). The first byte is the base color shown behind /
11
+ * outside the cell gradients; the other six are per-cell colors laid out
12
+ * row-major in a 3×2 grid (c1 top-left, c6 bottom-right).
13
+ *
14
+ * Inspired by Lean Rada's CSS-only LQIP technique
15
+ * (https://leanrada.com/notes/css-only-lqip/) but intentionally
16
+ * incompatible with his wire format: his 20-bit packed integer carries
17
+ * only grayscale per cell, whereas this 56-bit CSV carries one quantized
18
+ * Oklab color per cell so multi-hue subjects survive the placeholder.
19
+ * Decoder lives in image.styles.ts and renders 7 background layers
20
+ * (6 radial-gradient cells + base) natively, no JS / blend modes.
21
+ *
22
+ * This module is intentionally pure — no Lit imports, no custom-element
23
+ * registration side effects. Consumers (Node build pipelines, Workers,
24
+ * tests) can import just the encoder without dragging in the Storybook
25
+ * UI. The file-picker UI lives in `lqip-encoder-element.ts` and registers
26
+ * `<nldd-lqip-encoder>` separately.
27
+ */
28
+ /**
29
+ * Pure LQIP encoder. Operates on a raw RGBA pixel buffer (no DOM, no Node
30
+ * APIs) so the same function can be called from a browser, a Node script,
31
+ * a Worker, or any other runtime.
32
+ *
33
+ * Returns a comma-separated string `"base,c1,c2,c3,c4,c5,c6"` where each
34
+ * value is an integer 0-255 packing 2 bits L + 3 bits a + 3 bits b. The
35
+ * `nldd-image` component parses this string into seven CSS variables; its
36
+ * decoder turns each one back into an `oklab()` color.
37
+ *
38
+ * Buffer layout matches ImageData.data: 4 bytes per pixel in RGBA, row-major.
39
+ *
40
+ * Algorithm:
41
+ * 1. Per cell: linear-light average → Oklab → findOklabBits → packed byte.
42
+ * 2. Base: histogram-dominant linear-RGB → Oklab → findOklabBits → packed
43
+ * byte. The base shows through cell-gradient transparent edges and
44
+ * sets the placeholder's overall mood; the cells carry the per-zone
45
+ * hue variation.
46
+ */
47
+ export declare function encodePixelDataToLqip(data: Uint8ClampedArray | Uint8Array, width: number, height: number): string;
48
+ /**
49
+ * Browser-friendly wrapper around `encodePixelDataToLqip`. Accepts a File
50
+ * (e.g. from <input type="file">), an HTMLImageElement, or an ImageBitmap;
51
+ * decodes it to pixels via the Canvas API; and forwards to the pure encoder.
52
+ *
53
+ * When passing an HTMLImageElement loaded from a different origin, set
54
+ * `img.crossOrigin = 'anonymous'` BEFORE assigning `src` — otherwise the
55
+ * underlying canvas read taints and throws a SecurityError. Files and
56
+ * same-origin ImageBitmaps are unaffected.
57
+ */
58
+ export declare function encodeLqip(source: File | HTMLImageElement | ImageBitmap): Promise<string>;
59
+ //# sourceMappingURL=lqip-encoder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lqip-encoder.d.ts","sourceRoot":"","sources":["../../../../src/components/content/image/lqip-encoder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAiLH;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CACpC,IAAI,EAAE,iBAAiB,GAAG,UAAU,EACpC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACZ,MAAM,CAoCR;AAED;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,IAAI,GAAG,gBAAgB,GAAG,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAuB/F"}
@@ -0,0 +1,279 @@
1
+ /**
2
+ * Encoder for the CSS-only multi-color LQIP technique used by nldd-image.
3
+ *
4
+ * Takes an image (File, ImageBitmap, or HTMLImageElement) and returns the
5
+ * comma-separated string the `lqip` attribute expects:
6
+ *
7
+ * "base,c1,c2,c3,c4,c5,c6"
8
+ *
9
+ * — seven 0-255 bytes, each packing an 8-bit Oklab triplet (2 bits L,
10
+ * 3 bits a, 3 bits b). The first byte is the base color shown behind /
11
+ * outside the cell gradients; the other six are per-cell colors laid out
12
+ * row-major in a 3×2 grid (c1 top-left, c6 bottom-right).
13
+ *
14
+ * Inspired by Lean Rada's CSS-only LQIP technique
15
+ * (https://leanrada.com/notes/css-only-lqip/) but intentionally
16
+ * incompatible with his wire format: his 20-bit packed integer carries
17
+ * only grayscale per cell, whereas this 56-bit CSV carries one quantized
18
+ * Oklab color per cell so multi-hue subjects survive the placeholder.
19
+ * Decoder lives in image.styles.ts and renders 7 background layers
20
+ * (6 radial-gradient cells + base) natively, no JS / blend modes.
21
+ *
22
+ * This module is intentionally pure — no Lit imports, no custom-element
23
+ * registration side effects. Consumers (Node build pipelines, Workers,
24
+ * tests) can import just the encoder without dragging in the Storybook
25
+ * UI. The file-picker UI lives in `lqip-encoder-element.ts` and registers
26
+ * `<nldd-lqip-encoder>` separately.
27
+ */
28
+ /* --------------------------------------------------------------------- *
29
+ * Encoder
30
+ *
31
+ * Extends Lean Rada's CSS-only LQIP technique with per-cell color.
32
+ * Lean's original format packs 6 × 2-bit grayscale cells + 1 × 8-bit
33
+ * Oklab base color into 20 bits, then uses CSS blend modes to fake
34
+ * multi-color from a grayscale payload. The cells can only modulate
35
+ * the base's lightness — there is no actual hue variation across the
36
+ * placeholder, so a photo with blue sky + warm lights collapses to "a
37
+ * bit darker / a bit lighter than that one dominant blue".
38
+ *
39
+ * This encoder gives every cell its own 8-bit Oklab triplet (2 bits L,
40
+ * 3 bits a, 3 bits b) — the same resolution Lean uses for his single
41
+ * global base — plus an 8-bit base color for pixels outside the cell
42
+ * gradients. Total payload: 7 × 8 = 56 bits, serialised as a
43
+ * comma-separated string "base,c1,c2,c3,c4,c5,c6" (each int 0-255).
44
+ *
45
+ * Trade-off: wire-incompatible with Lean's reference tool. In exchange
46
+ * the CSS decoder is simpler (no blend modes), and the placeholder
47
+ * carries genuine multi-hue information.
48
+ *
49
+ * Key implementation choices:
50
+ *
51
+ * 1. Per cell: linear-light average → Oklab → findOklabBits. Same
52
+ * brute-force scaled-Euclidean quantizer used for the base, applied
53
+ * 6 more times.
54
+ *
55
+ * 2. Base color: histogram-dominant Oklab bucket → findOklabBits.
56
+ * The base shows through cell-gradient edges and outside the
57
+ * gradient radii, so it sets the "overall mood" of the placeholder.
58
+ *
59
+ * 3. Linear-light averaging via a 256-entry LUT keeps per-cell mean
60
+ * color photonically correct.
61
+ *
62
+ * Pure entry point: `encodePixelDataToLqip(data, width, height)` →
63
+ * string. Async `encodeLqip(source)` is a browser wrapper that gets
64
+ * pixels via canvas getImageData. Node users can build their own
65
+ * wrapper using sharp / pngjs / etc.
66
+ * --------------------------------------------------------------------- */
67
+ /** sRGB byte (0-255) → linear-light (0-1) lookup table. Precomputed because
68
+ * large images run this hundreds of thousands of times. */
69
+ const SRGB_TO_LINEAR_LUT = new Float32Array(256);
70
+ for (let i = 0; i < 256; i++) {
71
+ const n = i / 255;
72
+ SRGB_TO_LINEAR_LUT[i] = n <= 0.04045 ? n / 12.92 : Math.pow((n + 0.055) / 1.055, 2.4);
73
+ }
74
+ /** Linear-light RGB triplet (0-1 per channel) → Oklab. Same constants as the
75
+ * spec, but skips the sRGB→linear step since the input is already linear. */
76
+ function linearRgbToOklab(rl, gl, bl) {
77
+ const l = 0.4122214708 * rl + 0.5363325363 * gl + 0.0514459929 * bl;
78
+ const m = 0.2119034982 * rl + 0.6806995451 * gl + 0.1073969566 * bl;
79
+ const s = 0.0883024619 * rl + 0.2817188376 * gl + 0.6299787005 * bl;
80
+ const l_ = Math.cbrt(l), m_ = Math.cbrt(m), s_ = Math.cbrt(s);
81
+ return {
82
+ L: 0.2104542553 * l_ + 0.7936177850 * m_ - 0.0040720468 * s_,
83
+ a: 1.9779984951 * l_ - 2.4285922050 * m_ + 0.4505937099 * s_,
84
+ b: 0.0259040371 * l_ + 0.7827717662 * m_ - 0.8086757660 * s_,
85
+ };
86
+ }
87
+ function clamp(v, lo, hi) {
88
+ return Math.max(lo, Math.min(hi, v));
89
+ }
90
+ /** Decode an 8-bit color triplet (2 + 3 + 3 bits) back to the Oklab L/a/b
91
+ * values the CSS decoder will reconstruct. Used by the brute-force quantizer
92
+ * to evaluate candidates and by the cell encoder for the cell's own bits.
93
+ *
94
+ * Per-byte L/a/b decoding matches Lean Rada's reference implementation
95
+ * exactly — the same formulas produce the same colors. The overall wire
96
+ * format is NOT interchangeable though: Lean packs 6 grayscale cells + a
97
+ * single base color into one 20-bit integer; we ship 7 independent bytes
98
+ * in a CSV string. So an individual byte can be decoded with either
99
+ * decoder, but the LQIP attribute strings can't be swapped between tools. */
100
+ function bitsToLab(ll, aaa, bbb) {
101
+ return {
102
+ L: (ll / 0b11) * 0.6 + 0.2,
103
+ a: (aaa / 0b1000) * 0.7 - 0.35,
104
+ b: ((bbb + 1) / 0b1000) * 0.7 - 0.35,
105
+ };
106
+ }
107
+ /** Find the 2 + 3 + 3 bit Oklab triplet whose decoded color is visually
108
+ * closest to the target. Brute-forces all 256 combinations because the
109
+ * search space is tiny and round-to-nearest under-performs near the
110
+ * quantization boundaries.
111
+ *
112
+ * Distance is *scaled* Euclidean: a/b are divided by sqrt(chroma) before
113
+ * comparison so that vivid targets aren't crushed in favour of nearby
114
+ * lower-chroma buckets that happen to have a smaller raw delta. Matches
115
+ * Lean's reference implementation. */
116
+ function findOklabBits(targetL, targetA, targetB) {
117
+ const targetChroma = Math.hypot(targetA, targetB);
118
+ const scaledTargetA = targetA / (1e-6 + Math.pow(targetChroma, 0.5));
119
+ const scaledTargetB = targetB / (1e-6 + Math.pow(targetChroma, 0.5));
120
+ let bestBits = [0, 0, 0];
121
+ let bestDifference = Infinity;
122
+ for (let lli = 0; lli <= 0b11; lli++) {
123
+ for (let aaai = 0; aaai <= 0b111; aaai++) {
124
+ for (let bbbi = 0; bbbi <= 0b111; bbbi++) {
125
+ const { L, a, b } = bitsToLab(lli, aaai, bbbi);
126
+ const chroma = Math.hypot(a, b);
127
+ const scaledA = a / (1e-6 + Math.pow(chroma, 0.5));
128
+ const scaledB = b / (1e-6 + Math.pow(chroma, 0.5));
129
+ const difference = Math.hypot(L - targetL, scaledA - scaledTargetA, scaledB - scaledTargetB);
130
+ if (difference < bestDifference) {
131
+ bestDifference = difference;
132
+ bestBits = [lli, aaai, bbbi];
133
+ }
134
+ }
135
+ }
136
+ }
137
+ return { ll: bestBits[0], aaa: bestBits[1], bbb: bestBits[2] };
138
+ }
139
+ /** Find the dominant color in the image by histogram bucketing in Oklab
140
+ * space. Quantises every pixel into an 8×8×8 grid (512 buckets), then
141
+ * returns the linear-RGB mean of the most-populated bucket.
142
+ *
143
+ * Using "dominant" instead of "average" is what makes the placeholder pick
144
+ * up the subject's color (sky blue, foliage green, the pier's amber
145
+ * lights) rather than collapsing foreground and background into a muddy
146
+ * midpoint. The bucket grid resolution is intentionally coarse so that
147
+ * perceptually-similar tones share a bucket — fine quantization would
148
+ * scatter near-identical pixels across many tiny buckets and lose the
149
+ * dominance signal we want. */
150
+ function findDominantColor(data, width, height) {
151
+ const L_BUCKETS = 8, A_BUCKETS = 8, B_BUCKETS = 8;
152
+ const TOTAL = L_BUCKETS * A_BUCKETS * B_BUCKETS;
153
+ const counts = new Uint32Array(TOTAL);
154
+ const sumR = new Float64Array(TOTAL);
155
+ const sumG = new Float64Array(TOTAL);
156
+ const sumB = new Float64Array(TOTAL);
157
+ const pixelCount = width * height;
158
+ for (let p = 0; p < pixelCount; p++) {
159
+ const i = p * 4;
160
+ const rl = SRGB_TO_LINEAR_LUT[data[i]];
161
+ const gl = SRGB_TO_LINEAR_LUT[data[i + 1]];
162
+ const bl = SRGB_TO_LINEAR_LUT[data[i + 2]];
163
+ const { L, a, b } = linearRgbToOklab(rl, gl, bl);
164
+ // L is roughly [0, 1]; a/b for sRGB stay roughly within [-0.4, 0.4].
165
+ const li = clamp(Math.floor(L * L_BUCKETS), 0, L_BUCKETS - 1);
166
+ const ai = clamp(Math.floor((a + 0.4) / 0.8 * A_BUCKETS), 0, A_BUCKETS - 1);
167
+ const bi = clamp(Math.floor((b + 0.4) / 0.8 * B_BUCKETS), 0, B_BUCKETS - 1);
168
+ const idx = li * A_BUCKETS * B_BUCKETS + ai * B_BUCKETS + bi;
169
+ counts[idx]++;
170
+ sumR[idx] += rl;
171
+ sumG[idx] += gl;
172
+ sumB[idx] += bl;
173
+ }
174
+ let bestIdx = 0, bestCount = 0;
175
+ for (let i = 0; i < TOTAL; i++) {
176
+ if (counts[i] > bestCount) {
177
+ bestCount = counts[i];
178
+ bestIdx = i;
179
+ }
180
+ }
181
+ const n = counts[bestIdx];
182
+ return { rl: sumR[bestIdx] / n, gl: sumG[bestIdx] / n, bl: sumB[bestIdx] / n };
183
+ }
184
+ /** Pack a quantized Oklab triplet into a single byte: 2 bits L (high), 3 bits
185
+ * a, 3 bits b (low). Matches the format Lean uses for his global base
186
+ * color, so the CSS decoder for each cell uses the same math as Lean's
187
+ * base decoder — just one byte at a time, seven times. */
188
+ function packLab(ll, aaa, bbb) {
189
+ return (ll << 6) | (aaa << 3) | bbb;
190
+ }
191
+ /**
192
+ * Pure LQIP encoder. Operates on a raw RGBA pixel buffer (no DOM, no Node
193
+ * APIs) so the same function can be called from a browser, a Node script,
194
+ * a Worker, or any other runtime.
195
+ *
196
+ * Returns a comma-separated string `"base,c1,c2,c3,c4,c5,c6"` where each
197
+ * value is an integer 0-255 packing 2 bits L + 3 bits a + 3 bits b. The
198
+ * `nldd-image` component parses this string into seven CSS variables; its
199
+ * decoder turns each one back into an `oklab()` color.
200
+ *
201
+ * Buffer layout matches ImageData.data: 4 bytes per pixel in RGBA, row-major.
202
+ *
203
+ * Algorithm:
204
+ * 1. Per cell: linear-light average → Oklab → findOklabBits → packed byte.
205
+ * 2. Base: histogram-dominant linear-RGB → Oklab → findOklabBits → packed
206
+ * byte. The base shows through cell-gradient transparent edges and
207
+ * sets the placeholder's overall mood; the cells carry the per-zone
208
+ * hue variation.
209
+ */
210
+ export function encodePixelDataToLqip(data, width, height) {
211
+ if (width < 3 || height < 2) {
212
+ throw new Error(`encodePixelDataToLqip: image must be at least 3×2, got ${width}×${height}`);
213
+ }
214
+ // Per-cell quantized Oklab bytes.
215
+ const cellBytes = [];
216
+ for (let cellRow = 0; cellRow < 2; cellRow++) {
217
+ const yStart = Math.floor(cellRow * height / 2);
218
+ const yEnd = Math.floor((cellRow + 1) * height / 2);
219
+ for (let cellCol = 0; cellCol < 3; cellCol++) {
220
+ const xStart = Math.floor(cellCol * width / 3);
221
+ const xEnd = Math.floor((cellCol + 1) * width / 3);
222
+ let cellR = 0, cellG = 0, cellB = 0, cellCount = 0;
223
+ for (let y = yStart; y < yEnd; y++) {
224
+ for (let x = xStart; x < xEnd; x++) {
225
+ const i = (y * width + x) * 4;
226
+ cellR += SRGB_TO_LINEAR_LUT[data[i]];
227
+ cellG += SRGB_TO_LINEAR_LUT[data[i + 1]];
228
+ cellB += SRGB_TO_LINEAR_LUT[data[i + 2]];
229
+ cellCount++;
230
+ }
231
+ }
232
+ const { L, a, b } = linearRgbToOklab(cellR / cellCount, cellG / cellCount, cellB / cellCount);
233
+ const { ll, aaa, bbb } = findOklabBits(L, a, b);
234
+ cellBytes.push(packLab(ll, aaa, bbb));
235
+ }
236
+ }
237
+ // Base: dominant Oklab bucket → quantized byte.
238
+ const dom = findDominantColor(data, width, height);
239
+ const domLab = linearRgbToOklab(dom.rl, dom.gl, dom.bl);
240
+ const base = findOklabBits(domLab.L, domLab.a, domLab.b);
241
+ const baseByte = packLab(base.ll, base.aaa, base.bbb);
242
+ return [baseByte, ...cellBytes].join(',');
243
+ }
244
+ /**
245
+ * Browser-friendly wrapper around `encodePixelDataToLqip`. Accepts a File
246
+ * (e.g. from <input type="file">), an HTMLImageElement, or an ImageBitmap;
247
+ * decodes it to pixels via the Canvas API; and forwards to the pure encoder.
248
+ *
249
+ * When passing an HTMLImageElement loaded from a different origin, set
250
+ * `img.crossOrigin = 'anonymous'` BEFORE assigning `src` — otherwise the
251
+ * underlying canvas read taints and throws a SecurityError. Files and
252
+ * same-origin ImageBitmaps are unaffected.
253
+ */
254
+ export async function encodeLqip(source) {
255
+ // When we create the bitmap from a File / HTMLImageElement we own its
256
+ // lifecycle and must close it. When the caller passes an ImageBitmap they
257
+ // own it (the Storybook encoder element, for one, reuses + closes its own),
258
+ // so we leave that case untouched.
259
+ const ownsBitmap = source instanceof File || source instanceof HTMLImageElement;
260
+ const bitmap = ownsBitmap ? await createImageBitmap(source) : source;
261
+ try {
262
+ const canvas = document.createElement('canvas');
263
+ canvas.width = bitmap.width;
264
+ canvas.height = bitmap.height;
265
+ const ctx = canvas.getContext('2d');
266
+ if (!ctx)
267
+ throw new Error('encodeLqip: could not acquire a 2D canvas context');
268
+ ctx.drawImage(bitmap, 0, 0);
269
+ const { data } = ctx.getImageData(0, 0, bitmap.width, bitmap.height);
270
+ return encodePixelDataToLqip(data, bitmap.width, bitmap.height);
271
+ }
272
+ finally {
273
+ // Close only the bitmap we allocated; never the caller's. Runs on the
274
+ // success path and on a thrown SecurityError / context failure.
275
+ if (ownsBitmap)
276
+ bitmap.close();
277
+ }
278
+ }
279
+ //# sourceMappingURL=lqip-encoder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lqip-encoder.js","sourceRoot":"","sources":["../../../../src/components/content/image/lqip-encoder.ts"],"names":[],"mappings":"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"}
@@ -9,13 +9,22 @@
9
9
  *
10
10
  * @attr {string} spacing - Spacing between elements: 'flat' | 'tight' | 'snug' (default) | 'loose'
11
11
  * @attr {boolean} centered - Centers the main column inside the container; without it, content is left-aligned
12
+ * @attr {object} translations - Override translation keys; unset keys fall back to Dutch
12
13
  */
13
14
  import { LitElement } from 'lit';
15
+ import type { NLDDRichTextTranslations } from './rich-text.i18n.js';
14
16
  type Spacing = 'flat' | 'tight' | 'snug' | 'loose';
15
17
  export declare class NLDDRichText extends LitElement {
16
18
  spacing: Spacing;
17
19
  centered: boolean;
20
+ translations: Partial<NLDDRichTextTranslations>;
21
+ _t(key: keyof NLDDRichTextTranslations): string;
22
+ private _mutationObserver?;
23
+ private _resizeObserver?;
18
24
  createRenderRoot(): this;
25
+ connectedCallback(): void;
26
+ disconnectedCallback(): void;
27
+ private _syncTables;
19
28
  }
20
29
  declare global {
21
30
  interface HTMLElementTagNameMap {
@@ -1 +1 @@
1
- {"version":3,"file":"rich-text.d.ts","sourceRoot":"","sources":["../../../../src/components/content/rich-text/rich-text.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAGjC,KAAK,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;AAEnD,qBACa,YAAa,SAAQ,UAAU;IAE3C,OAAO,EAAE,OAAO,CAAU;IAG1B,QAAQ,UAAS;IAER,gBAAgB;CAGzB;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,gBAAgB,EAAE,YAAY,CAAC;KAC/B;CACD"}
1
+ {"version":3,"file":"rich-text.d.ts","sourceRoot":"","sources":["../../../../src/components/content/rich-text/rich-text.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAGjC,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAEpE,KAAK,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;AAInD,qBACa,YAAa,SAAQ,UAAU;IAE3C,OAAO,EAAE,OAAO,CAAU;IAG1B,QAAQ,UAAS;IAGjB,YAAY,EAAE,OAAO,CAAC,wBAAwB,CAAC,CAAM;IAE9C,EAAE,CAAC,GAAG,EAAE,MAAM,wBAAwB,GAAG,MAAM;IAItD,OAAO,CAAC,iBAAiB,CAAC,CAAmB;IAC7C,OAAO,CAAC,eAAe,CAAC,CAAiB;IAEhC,gBAAgB;IAIhB,iBAAiB,IAAI,IAAI;IAezB,oBAAoB,IAAI,IAAI;IAQrC,OAAO,CAAC,WAAW;CA+BnB;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,gBAAgB,EAAE,YAAY,CAAC;KAC/B;CACD"}
@@ -0,0 +1,5 @@
1
+ export declare const nlddRichTextTranslations: {
2
+ 'components.rich-text.table-scroll-label': string;
3
+ };
4
+ export type NLDDRichTextTranslations = typeof nlddRichTextTranslations;
5
+ //# sourceMappingURL=rich-text.i18n.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rich-text.i18n.d.ts","sourceRoot":"","sources":["../../../../src/components/content/rich-text/rich-text.i18n.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,wBAAwB;;CAEpC,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,OAAO,wBAAwB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export const nlddRichTextTranslations = {
2
+ 'components.rich-text.table-scroll-label': 'Scrollbare tabel',
3
+ };
4
+ //# sourceMappingURL=rich-text.i18n.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rich-text.i18n.js","sourceRoot":"","sources":["../../../../src/components/content/rich-text/rich-text.i18n.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACvC,yCAAyC,EAAE,kBAAkB;CAC7D,CAAC"}