@grantcodes/ui 2.15.6 → 2.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/custom-elements.json +476 -346
  3. package/package.json +23 -23
  4. package/src/components/accordion/accordion-item.component.js +17 -19
  5. package/src/components/accordion/accordion-item.css +40 -44
  6. package/src/components/accordion/accordion.component.js +7 -7
  7. package/src/components/accordion/accordion.css +5 -5
  8. package/src/components/accordion/accordion.js +6 -6
  9. package/src/components/accordion/accordion.react.js +6 -6
  10. package/src/components/accordion/accordion.stories.js +10 -10
  11. package/src/components/accordion/accordion.test.js +100 -100
  12. package/src/components/accordion/index.js +6 -6
  13. package/src/components/app-bar/app-bar.component.js +86 -92
  14. package/src/components/app-bar/app-bar.css +187 -203
  15. package/src/components/app-bar/app-bar.js +3 -3
  16. package/src/components/app-bar/app-bar.react.js +9 -9
  17. package/src/components/app-bar/app-bar.stories.js +20 -20
  18. package/src/components/app-bar/app-bar.test.js +190 -237
  19. package/src/components/app-bar/index.js +1 -1
  20. package/src/components/app-bar/nav-link.component.js +7 -9
  21. package/src/components/app-bar/nav-link.css +23 -28
  22. package/src/components/app-bar/nav-link.js +3 -3
  23. package/src/components/app-bar/nav-link.react.js +6 -6
  24. package/src/components/avatar/avatar.component.js +79 -68
  25. package/src/components/avatar/avatar.css +18 -18
  26. package/src/components/avatar/avatar.js +3 -3
  27. package/src/components/avatar/avatar.react.js +6 -6
  28. package/src/components/avatar/avatar.stories.js +24 -23
  29. package/src/components/avatar/avatar.test.js +128 -84
  30. package/src/components/avatar/index.js +1 -1
  31. package/src/components/badge/badge.component.js +23 -23
  32. package/src/components/badge/badge.css +39 -52
  33. package/src/components/badge/badge.js +3 -3
  34. package/src/components/badge/badge.react.js +6 -6
  35. package/src/components/badge/badge.stories.js +23 -23
  36. package/src/components/badge/badge.test.js +53 -68
  37. package/src/components/badge/index.js +1 -1
  38. package/src/components/breadcrumb/breadcrumb.component.js +95 -96
  39. package/src/components/breadcrumb/breadcrumb.css +42 -44
  40. package/src/components/breadcrumb/breadcrumb.js +6 -9
  41. package/src/components/breadcrumb/breadcrumb.react.js +9 -12
  42. package/src/components/breadcrumb/breadcrumb.stories.js +5 -5
  43. package/src/components/breadcrumb/breadcrumb.test.js +139 -151
  44. package/src/components/breadcrumb/index.js +1 -1
  45. package/src/components/button/button.component.js +101 -103
  46. package/src/components/button/button.css +90 -94
  47. package/src/components/button/button.js +3 -3
  48. package/src/components/button/button.react.js +6 -6
  49. package/src/components/button/button.stories.js +25 -25
  50. package/src/components/button/button.test.js +324 -344
  51. package/src/components/button/index.js +1 -1
  52. package/src/components/button-group/button-group.component.js +18 -18
  53. package/src/components/button-group/button-group.css +16 -16
  54. package/src/components/button-group/button-group.js +3 -3
  55. package/src/components/button-group/button-group.react.js +6 -6
  56. package/src/components/button-group/button-group.stories.js +23 -23
  57. package/src/components/button-group/button-group.test.js +54 -54
  58. package/src/components/button-group/index.js +1 -1
  59. package/src/components/card/card.component.js +31 -33
  60. package/src/components/card/card.css +85 -85
  61. package/src/components/card/card.js +4 -4
  62. package/src/components/card/card.react.js +6 -6
  63. package/src/components/card/card.stories.js +27 -26
  64. package/src/components/card/card.test.js +56 -56
  65. package/src/components/card/index.js +1 -1
  66. package/src/components/code-preview/code-preview.component.js +47 -48
  67. package/src/components/code-preview/code-preview.css +11 -11
  68. package/src/components/code-preview/code-preview.js +3 -3
  69. package/src/components/code-preview/code-preview.react.js +6 -6
  70. package/src/components/code-preview/code-preview.stories.js +29 -29
  71. package/src/components/code-preview/code-preview.test.js +112 -135
  72. package/src/components/code-preview/index.js +1 -1
  73. package/src/components/container/container.component.js +27 -27
  74. package/src/components/container/container.css +22 -22
  75. package/src/components/container/container.js +3 -3
  76. package/src/components/container/container.react.js +6 -6
  77. package/src/components/container/container.stories.js +44 -44
  78. package/src/components/container/container.test.js +54 -66
  79. package/src/components/container/index.js +1 -1
  80. package/src/components/countdown/countdown.component.js +160 -162
  81. package/src/components/countdown/countdown.css +50 -34
  82. package/src/components/countdown/countdown.js +3 -3
  83. package/src/components/countdown/countdown.react.js +6 -6
  84. package/src/components/countdown/countdown.stories.js +34 -34
  85. package/src/components/countdown/index.js +1 -1
  86. package/src/components/cta/cta.component.js +77 -81
  87. package/src/components/cta/cta.css +24 -24
  88. package/src/components/cta/cta.js +3 -3
  89. package/src/components/cta/cta.react.js +6 -6
  90. package/src/components/cta/cta.stories.js +32 -32
  91. package/src/components/cta/index.js +1 -1
  92. package/src/components/dialog/dialog.component.js +95 -58
  93. package/src/components/dialog/dialog.css +45 -50
  94. package/src/components/dialog/dialog.js +3 -3
  95. package/src/components/dialog/dialog.react.js +6 -6
  96. package/src/components/dialog/dialog.stories.js +38 -29
  97. package/src/components/dialog/dialog.test.js +188 -102
  98. package/src/components/dialog/index.js +1 -1
  99. package/src/components/dropdown/dropdown.component.js +220 -201
  100. package/src/components/dropdown/dropdown.css +60 -84
  101. package/src/components/dropdown/dropdown.js +6 -9
  102. package/src/components/dropdown/dropdown.react.js +15 -15
  103. package/src/components/dropdown/dropdown.stories.js +10 -10
  104. package/src/components/dropdown/dropdown.test.js +211 -157
  105. package/src/components/dropdown/index.js +1 -1
  106. package/src/components/dropzone/dropzone.component.js +126 -129
  107. package/src/components/dropzone/dropzone.css +35 -37
  108. package/src/components/dropzone/dropzone.js +3 -3
  109. package/src/components/dropzone/dropzone.react.js +6 -6
  110. package/src/components/dropzone/dropzone.stories.js +28 -28
  111. package/src/components/dropzone/dropzone.test.js +91 -99
  112. package/src/components/dropzone/index.js +1 -1
  113. package/src/components/feature-list/feature-list.component.js +78 -86
  114. package/src/components/feature-list/feature-list.css +37 -37
  115. package/src/components/feature-list/feature-list.js +3 -3
  116. package/src/components/feature-list/feature-list.react.js +6 -6
  117. package/src/components/feature-list/feature-list.stories.js +86 -90
  118. package/src/components/feature-list/index.js +1 -1
  119. package/src/components/footer/footer-column.component.js +7 -7
  120. package/src/components/footer/footer-column.css +22 -22
  121. package/src/components/footer/footer.component.js +19 -19
  122. package/src/components/footer/footer.css +38 -38
  123. package/src/components/footer/footer.js +8 -8
  124. package/src/components/footer/footer.react.js +9 -9
  125. package/src/components/footer/footer.stories.js +9 -9
  126. package/src/components/footer/footer.test.js +101 -111
  127. package/src/components/footer/index.js +2 -2
  128. package/src/components/form-field/form-field.component.js +167 -125
  129. package/src/components/form-field/form-field.css +24 -24
  130. package/src/components/form-field/form-field.js +3 -3
  131. package/src/components/form-field/form-field.react.js +6 -6
  132. package/src/components/form-field/form-field.stories.js +53 -44
  133. package/src/components/form-field/form-field.test.js +280 -134
  134. package/src/components/form-field/index.js +1 -1
  135. package/src/components/gallery/gallery-image.component.js +30 -30
  136. package/src/components/gallery/gallery-image.js +3 -3
  137. package/src/components/gallery/gallery-image.react.js +6 -6
  138. package/src/components/gallery/gallery.component.js +24 -17
  139. package/src/components/gallery/gallery.css +79 -72
  140. package/src/components/gallery/gallery.js +3 -3
  141. package/src/components/gallery/gallery.react.js +6 -6
  142. package/src/components/gallery/gallery.stories.js +69 -68
  143. package/src/components/gallery/gallery.test.js +95 -88
  144. package/src/components/gallery/index.js +2 -2
  145. package/src/components/hero/hero.component.js +53 -57
  146. package/src/components/hero/hero.css +27 -27
  147. package/src/components/hero/hero.js +3 -3
  148. package/src/components/hero/hero.react.js +6 -6
  149. package/src/components/hero/hero.stories.js +39 -39
  150. package/src/components/hero/index.js +1 -1
  151. package/src/components/icon/icon.component.js +10 -10
  152. package/src/components/icon/icon.css +13 -13
  153. package/src/components/icon/icon.js +4 -4
  154. package/src/components/icon/icon.react.js +6 -6
  155. package/src/components/icon/icon.stories.js +8 -9
  156. package/src/components/icon/icon.test.js +54 -55
  157. package/src/components/icon/index.js +1 -1
  158. package/src/components/loading/index.js +1 -1
  159. package/src/components/loading/loading.component.js +41 -14
  160. package/src/components/loading/loading.css +47 -30
  161. package/src/components/loading/loading.js +4 -4
  162. package/src/components/loading/loading.react.js +6 -6
  163. package/src/components/loading/loading.stories.js +19 -18
  164. package/src/components/loading/loading.test.js +77 -54
  165. package/src/components/logo-cloud/index.js +1 -1
  166. package/src/components/logo-cloud/logo-cloud.component.js +39 -43
  167. package/src/components/logo-cloud/logo-cloud.css +31 -31
  168. package/src/components/logo-cloud/logo-cloud.js +3 -3
  169. package/src/components/logo-cloud/logo-cloud.react.js +6 -6
  170. package/src/components/logo-cloud/logo-cloud.stories.js +80 -80
  171. package/src/components/map/index.js +1 -1
  172. package/src/components/map/map.component.js +103 -107
  173. package/src/components/map/map.css +22 -22
  174. package/src/components/map/map.js +3 -3
  175. package/src/components/map/map.react.js +6 -6
  176. package/src/components/map/map.stories.js +38 -38
  177. package/src/components/media-text/index.js +1 -1
  178. package/src/components/media-text/media-text.component.js +76 -70
  179. package/src/components/media-text/media-text.css +32 -32
  180. package/src/components/media-text/media-text.js +3 -3
  181. package/src/components/media-text/media-text.react.js +6 -6
  182. package/src/components/media-text/media-text.stories.js +45 -45
  183. package/src/components/media-text/media-text.test.js +72 -0
  184. package/src/components/newsletter/index.js +1 -1
  185. package/src/components/newsletter/newsletter.component.js +56 -64
  186. package/src/components/newsletter/newsletter.css +48 -48
  187. package/src/components/newsletter/newsletter.js +3 -3
  188. package/src/components/newsletter/newsletter.react.js +6 -6
  189. package/src/components/newsletter/newsletter.stories.js +35 -35
  190. package/src/components/notice/index.js +1 -1
  191. package/src/components/notice/notice.component.js +66 -55
  192. package/src/components/notice/notice.css +37 -38
  193. package/src/components/notice/notice.js +3 -3
  194. package/src/components/notice/notice.react.js +6 -6
  195. package/src/components/notice/notice.stories.js +54 -53
  196. package/src/components/notice/notice.test.js +210 -156
  197. package/src/components/pagination/index.js +1 -1
  198. package/src/components/pagination/pagination.component.js +44 -44
  199. package/src/components/pagination/pagination.css +6 -6
  200. package/src/components/pagination/pagination.js +4 -4
  201. package/src/components/pagination/pagination.react.js +6 -6
  202. package/src/components/pagination/pagination.stories.js +20 -18
  203. package/src/components/pagination/pagination.test.js +95 -102
  204. package/src/components/person/index.js +1 -1
  205. package/src/components/person/person.component.js +29 -29
  206. package/src/components/person/person.css +17 -17
  207. package/src/components/person/person.js +4 -4
  208. package/src/components/person/person.react.js +6 -6
  209. package/src/components/person/person.stories.js +34 -33
  210. package/src/components/person/person.test.js +55 -68
  211. package/src/components/pricing/index.js +1 -1
  212. package/src/components/pricing/pricing.component.js +58 -70
  213. package/src/components/pricing/pricing.css +65 -65
  214. package/src/components/pricing/pricing.js +3 -3
  215. package/src/components/pricing/pricing.react.js +6 -6
  216. package/src/components/pricing/pricing.stories.js +96 -96
  217. package/src/components/sidebar/index.js +1 -1
  218. package/src/components/sidebar/sidebar.component.js +157 -130
  219. package/src/components/sidebar/sidebar.css +118 -115
  220. package/src/components/sidebar/sidebar.js +4 -4
  221. package/src/components/sidebar/sidebar.react.js +10 -10
  222. package/src/components/sidebar/sidebar.stories.js +10 -10
  223. package/src/components/sidebar/sidebar.test.js +250 -230
  224. package/src/components/stats/index.js +1 -1
  225. package/src/components/stats/stats.component.js +42 -50
  226. package/src/components/stats/stats.css +29 -29
  227. package/src/components/stats/stats.js +3 -3
  228. package/src/components/stats/stats.react.js +6 -6
  229. package/src/components/stats/stats.stories.js +40 -40
  230. package/src/components/tabs/index.js +2 -2
  231. package/src/components/tabs/internal/tabs-button.component.js +16 -27
  232. package/src/components/tabs/internal/tabs-button.js +3 -3
  233. package/src/components/tabs/internal/tabs-button.react.js +6 -6
  234. package/src/components/tabs/internal/tabs-item.component.js +30 -30
  235. package/src/components/tabs/tab.component.js +9 -11
  236. package/src/components/tabs/tab.js +3 -3
  237. package/src/components/tabs/tab.react.js +6 -6
  238. package/src/components/tabs/tabs.component.js +107 -109
  239. package/src/components/tabs/tabs.css +50 -59
  240. package/src/components/tabs/tabs.js +4 -4
  241. package/src/components/tabs/tabs.react.js +6 -6
  242. package/src/components/tabs/tabs.stories.js +23 -22
  243. package/src/components/tabs/tabs.test.js +201 -147
  244. package/src/components/testimonials/index.js +1 -1
  245. package/src/components/testimonials/testimonials.component.js +47 -51
  246. package/src/components/testimonials/testimonials.css +31 -31
  247. package/src/components/testimonials/testimonials.js +3 -3
  248. package/src/components/testimonials/testimonials.react.js +6 -6
  249. package/src/components/testimonials/testimonials.stories.js +49 -51
  250. package/src/components/toast/index.js +1 -1
  251. package/src/components/toast/toast.component.js +217 -148
  252. package/src/components/toast/toast.css +101 -99
  253. package/src/components/toast/toast.js +6 -9
  254. package/src/components/toast/toast.react.js +12 -12
  255. package/src/components/toast/toast.stories.js +75 -49
  256. package/src/components/toast/toast.test.js +260 -221
  257. package/src/components/tooltip/index.js +1 -1
  258. package/src/components/tooltip/tooltip.component.js +56 -49
  259. package/src/components/tooltip/tooltip.css +61 -50
  260. package/src/components/tooltip/tooltip.js +4 -4
  261. package/src/components/tooltip/tooltip.react.js +6 -6
  262. package/src/components/tooltip/tooltip.stories.js +24 -23
  263. package/src/components/tooltip/tooltip.test.js +143 -155
  264. package/src/constructor.test.js +31 -0
  265. package/src/css/base.css +1 -0
  266. package/src/css/colors.stories.js +120 -122
  267. package/src/css/elements/a.css +28 -28
  268. package/src/css/elements/forms/button.css +1 -1
  269. package/src/css/elements/forms/input.css +80 -80
  270. package/src/css/elements/forms/label.css +3 -3
  271. package/src/css/elements/media/image.css +2 -2
  272. package/src/css/elements.stories.js +48 -49
  273. package/src/css/helpers.css +8 -8
  274. package/src/css/layers.stories.js +5 -5
  275. package/src/css/reset.css +102 -102
  276. package/src/css/themes/todomap.css +1 -1
  277. package/src/css/tokens.stories.js +50 -53
  278. package/src/css/typography.css +30 -32
  279. package/src/css/typography.stories.js +92 -92
  280. package/src/css/util/focus-ring.css +12 -20
  281. package/src/css/view-transitions.css +16 -0
  282. package/src/exports.test.js +89 -105
  283. package/src/icons.js +1 -1
  284. package/src/lib/generate-id.js +1 -1
  285. package/src/lib/slot-presence-controller.js +48 -0
  286. package/src/lib/styles/all.css +38 -37
  287. package/src/lib/styles/focus-ring.css +12 -20
  288. package/src/lib/view-transition.js +42 -0
  289. package/src/lib/view-transition.test.js +105 -0
  290. package/src/main.js +29 -29
  291. package/src/pages/agency.stories.js +59 -59
  292. package/src/pages/blog-post.stories.js +48 -48
  293. package/src/pages/saas-landing.stories.js +180 -180
  294. package/src/react.js +44 -44
  295. package/src/react.test.js +42 -51
  296. package/src/test-utils/assert-helpers.js +39 -58
  297. package/src/test-utils/events.js +32 -34
  298. package/src/test-utils/fixture.js +31 -31
  299. package/src/test-utils/index.js +3 -3
  300. package/src/types.d.ts +3 -3
@@ -1,82 +1,70 @@
1
- import { describe, it, afterEach } from "node:test";
2
- import { strict as assert } from "node:assert";
3
- import { fixture, cleanup } from "../../test-utils/index.js";
4
- import "./container.js";
1
+ import { strict as assert } from 'node:assert';
2
+ import { afterEach, describe, it } from 'node:test';
3
+ import { cleanup, fixture } from '../../test-utils/index.js';
4
+ import './container.js';
5
5
 
6
- describe("Container Component", () => {
7
- let element;
6
+ describe('Container Component', () => {
7
+ let element;
8
8
 
9
- afterEach(() => {
10
- cleanup(element);
11
- });
9
+ afterEach(() => {
10
+ cleanup(element);
11
+ });
12
12
 
13
- it("should render with default properties", async () => {
14
- element = await fixture("grantcodes-container");
15
- assert.ok(element, "Element should be created");
16
- assert.ok(element.shadowRoot, "Element should have shadow root");
17
- });
13
+ it('should render with default properties', async () => {
14
+ element = await fixture('grantcodes-container');
15
+ assert.ok(element, 'Element should be created');
16
+ assert.ok(element.shadowRoot, 'Element should have shadow root');
17
+ });
18
18
 
19
- it("should render default align variant", async () => {
20
- element = await fixture("grantcodes-container");
21
- const container = element.shadowRoot.querySelector(".container");
22
- assert.ok(container, "Container element should exist");
23
- });
19
+ it('should render default align variant', async () => {
20
+ element = await fixture('grantcodes-container');
21
+ const container = element.shadowRoot.querySelector('.container');
22
+ assert.ok(container, 'Container element should exist');
23
+ });
24
24
 
25
- it("should apply wide alignment class", async () => {
26
- element = await fixture("grantcodes-container", {
27
- align: "wide",
28
- });
25
+ it('should apply wide alignment class', async () => {
26
+ element = await fixture('grantcodes-container', {
27
+ align: 'wide',
28
+ });
29
29
 
30
- const container = element.shadowRoot.querySelector(".container");
31
- assert.ok(
32
- container.classList.contains("container--wide"),
33
- "Should have wide class",
34
- );
35
- });
30
+ const container = element.shadowRoot.querySelector('.container');
31
+ assert.ok(container.classList.contains('container--wide'), 'Should have wide class');
32
+ });
36
33
 
37
- it("should apply full alignment class", async () => {
38
- element = await fixture("grantcodes-container", {
39
- align: "full",
40
- });
34
+ it('should apply full alignment class', async () => {
35
+ element = await fixture('grantcodes-container', {
36
+ align: 'full',
37
+ });
41
38
 
42
- const container = element.shadowRoot.querySelector(".container");
43
- assert.ok(
44
- container.classList.contains("container--full"),
45
- "Should have full class",
46
- );
47
- });
39
+ const container = element.shadowRoot.querySelector('.container');
40
+ assert.ok(container.classList.contains('container--full'), 'Should have full class');
41
+ });
48
42
 
49
- it("should apply viewport alignment class", async () => {
50
- element = await fixture("grantcodes-container", {
51
- align: "viewport",
52
- });
43
+ it('should apply viewport alignment class', async () => {
44
+ element = await fixture('grantcodes-container', {
45
+ align: 'viewport',
46
+ });
53
47
 
54
- const container = element.shadowRoot.querySelector(".container");
55
- assert.ok(
56
- container.classList.contains("container--viewport"),
57
- "Should have viewport class",
58
- );
59
- });
48
+ const container = element.shadowRoot.querySelector('.container');
49
+ assert.ok(container.classList.contains('container--viewport'), 'Should have viewport class');
50
+ });
60
51
 
61
- it("should apply nopad class when nopad is true", async () => {
62
- element = await fixture("grantcodes-container", {
63
- nopad: true,
64
- });
52
+ it('should apply nopad class when nopad is true', async () => {
53
+ element = await fixture('grantcodes-container', {
54
+ nopad: true,
55
+ });
65
56
 
66
- const container = element.shadowRoot.querySelector(".container");
67
- assert.ok(
68
- container.classList.contains("container--nopad"),
69
- "Should have nopad class",
70
- );
71
- });
57
+ const container = element.shadowRoot.querySelector('.container');
58
+ assert.ok(container.classList.contains('container--nopad'), 'Should have nopad class');
59
+ });
72
60
 
73
- it("should render slotted content", async () => {
74
- element = await fixture("grantcodes-container");
75
- element.innerHTML = "<p>Container content</p>";
61
+ it('should render slotted content', async () => {
62
+ element = await fixture('grantcodes-container');
63
+ element.innerHTML = '<p>Container content</p>';
76
64
 
77
- await element.updateComplete;
65
+ await element.updateComplete;
78
66
 
79
- const slot = element.shadowRoot.querySelector("slot");
80
- assert.ok(slot, "Slot should exist");
81
- });
67
+ const slot = element.shadowRoot.querySelector('slot');
68
+ assert.ok(slot, 'Slot should exist');
69
+ });
82
70
  });
@@ -1 +1 @@
1
- export * from "./container.js";
1
+ export * from './container.js';
@@ -1,182 +1,180 @@
1
- import { LitElement, html, nothing } from "lit";
2
- import countdownStyles from "./countdown.css" with { type: "css" };
1
+ import { html, LitElement, nothing } from 'lit';
2
+ import countdownStyles from './countdown.css' with { type: 'css' };
3
3
 
4
4
  export class GrantCodesCountdown extends LitElement {
5
- static styles = [countdownStyles];
6
-
7
- static properties = {
8
- /**
9
- * ISO 8601 datetime string for the countdown target.
10
- * @type {string}
11
- */
12
- target: { type: String },
13
- /**
14
- * Label for the days unit.
15
- * @type {string}
16
- */
17
- "days-label": { type: String, attribute: "days-label" },
18
- /**
19
- * Label for the hours unit.
20
- * @type {string}
21
- */
22
- "hours-label": { type: String, attribute: "hours-label" },
23
- /**
24
- * Label for the minutes unit.
25
- * @type {string}
26
- */
27
- "minutes-label": { type: String, attribute: "minutes-label" },
28
- /**
29
- * Label for the seconds unit (only shown when show-seconds is true).
30
- * @type {string}
31
- */
32
- "seconds-label": { type: String, attribute: "seconds-label" },
33
- /**
34
- * Message displayed when the target date has passed.
35
- * @type {string}
36
- */
37
- "past-message": { type: String, attribute: "past-message" },
38
- /**
39
- * Whether to show a seconds unit.
40
- * @type {boolean}
41
- */
42
- "show-seconds": { type: Boolean, attribute: "show-seconds" },
43
-
44
- /** @internal */
45
- _days: { state: true },
46
- /** @internal */
47
- _hours: { state: true },
48
- /** @internal */
49
- _minutes: { state: true },
50
- /** @internal */
51
- _seconds: { state: true },
52
- /** @internal */
53
- _past: { state: true },
54
- };
55
-
56
- constructor() {
57
- super();
58
- this.target = "";
59
- this["days-label"] = "days";
60
- this["hours-label"] = "hours";
61
- this["minutes-label"] = "minutes";
62
- this["seconds-label"] = "seconds";
63
- this["past-message"] = "The event has started!";
64
- this["show-seconds"] = false;
65
-
66
- this._days = "---";
67
- this._hours = "--";
68
- this._minutes = "--";
69
- this._seconds = "--";
70
- this._past = false;
71
- this._intervalId = null;
72
- }
73
-
74
- connectedCallback() {
75
- super.connectedCallback();
76
- this._tick();
77
- this._startInterval();
78
- }
79
-
80
- disconnectedCallback() {
81
- super.disconnectedCallback();
82
- this._stopInterval();
83
- }
84
-
85
- updated(changed) {
86
- if (changed.has("target") || changed.has("show-seconds")) {
87
- this._stopInterval();
88
- this._tick();
89
- this._startInterval();
90
- }
91
- this._animateChangedValues(changed);
92
- }
93
-
94
- _startInterval() {
95
- const interval = this["show-seconds"] ? 1000 : 60_000;
96
- this._intervalId = setInterval(() => this._tick(), interval);
97
- }
98
-
99
- _stopInterval() {
100
- if (this._intervalId !== null) {
101
- clearInterval(this._intervalId);
102
- this._intervalId = null;
103
- }
104
- }
105
-
106
- _tick() {
107
- if (!this.target) return;
108
-
109
- const targetDate = new Date(this.target);
110
- const now = new Date();
111
- const diff = targetDate - now;
112
-
113
- if (diff <= 0) {
114
- this._past = true;
115
- this._stopInterval();
116
- return;
117
- }
118
-
119
- this._past = false;
120
- const totalSeconds = Math.floor(diff / 1000);
121
- const totalMinutes = Math.floor(totalSeconds / 60);
122
- const totalHours = Math.floor(totalMinutes / 60);
123
-
124
- const pad = (n) => String(n).padStart(2, "0");
125
-
126
- this._days = Math.floor(totalHours / 24);
127
- this._hours = pad(totalHours % 24);
128
- this._minutes = pad(totalMinutes % 60);
129
- this._seconds = pad(totalSeconds % 60);
130
- }
131
-
132
- /** Re-trigger the flip animation on value elements that changed. */
133
- _animateChangedValues(changed) {
134
- const keys = ["_days", "_hours", "_minutes", "_seconds"];
135
- for (const key of keys) {
136
- if (!changed.has(key)) continue;
137
- const el = this.shadowRoot?.querySelector(
138
- `[data-unit="${key.slice(1)}"]`,
139
- );
140
- if (!el) continue;
141
- el.classList.remove("countdown__value--flip");
142
- // Force reflow so re-adding the class restarts the animation
143
- void el.offsetWidth;
144
- el.classList.add("countdown__value--flip");
145
- }
146
- }
147
-
148
- render() {
149
- if (!this.target) return nothing;
150
-
151
- if (this._past) {
152
- return html`<div class="countdown__past">${this["past-message"]}</div>`;
153
- }
154
-
155
- return html`
5
+ static styles = [countdownStyles];
6
+
7
+ static properties = {
8
+ /**
9
+ * ISO 8601 datetime string for the countdown target.
10
+ * @type {string}
11
+ */
12
+ target: { type: String },
13
+ /**
14
+ * Label for the days unit.
15
+ * @type {string}
16
+ */
17
+ 'days-label': { type: String, attribute: 'days-label' },
18
+ /**
19
+ * Label for the hours unit.
20
+ * @type {string}
21
+ */
22
+ 'hours-label': { type: String, attribute: 'hours-label' },
23
+ /**
24
+ * Label for the minutes unit.
25
+ * @type {string}
26
+ */
27
+ 'minutes-label': { type: String, attribute: 'minutes-label' },
28
+ /**
29
+ * Label for the seconds unit (only shown when show-seconds is true).
30
+ * @type {string}
31
+ */
32
+ 'seconds-label': { type: String, attribute: 'seconds-label' },
33
+ /**
34
+ * Message displayed when the target date has passed.
35
+ * @type {string}
36
+ */
37
+ 'past-message': { type: String, attribute: 'past-message' },
38
+ /**
39
+ * Whether to show a seconds unit.
40
+ * @type {boolean}
41
+ */
42
+ 'show-seconds': { type: Boolean, attribute: 'show-seconds' },
43
+
44
+ /** @internal */
45
+ _days: { state: true },
46
+ /** @internal */
47
+ _hours: { state: true },
48
+ /** @internal */
49
+ _minutes: { state: true },
50
+ /** @internal */
51
+ _seconds: { state: true },
52
+ /** @internal */
53
+ _past: { state: true },
54
+ };
55
+
56
+ constructor() {
57
+ super();
58
+ this.target = '';
59
+ this['days-label'] = 'days';
60
+ this['hours-label'] = 'hours';
61
+ this['minutes-label'] = 'minutes';
62
+ this['seconds-label'] = 'seconds';
63
+ this['past-message'] = 'The event has started!';
64
+ this['show-seconds'] = false;
65
+
66
+ this._days = '---';
67
+ this._hours = '--';
68
+ this._minutes = '--';
69
+ this._seconds = '--';
70
+ this._past = false;
71
+ this._intervalId = null;
72
+ }
73
+
74
+ connectedCallback() {
75
+ super.connectedCallback();
76
+ this._tick();
77
+ this._startInterval();
78
+ }
79
+
80
+ disconnectedCallback() {
81
+ super.disconnectedCallback();
82
+ this._stopInterval();
83
+ }
84
+
85
+ updated(changed) {
86
+ if (changed.has('target') || changed.has('show-seconds')) {
87
+ this._stopInterval();
88
+ this._tick();
89
+ this._startInterval();
90
+ }
91
+ this._animateChangedValues(changed);
92
+ }
93
+
94
+ _startInterval() {
95
+ const interval = this['show-seconds'] ? 1000 : 60_000;
96
+ this._intervalId = setInterval(() => this._tick(), interval);
97
+ }
98
+
99
+ _stopInterval() {
100
+ if (this._intervalId !== null) {
101
+ clearInterval(this._intervalId);
102
+ this._intervalId = null;
103
+ }
104
+ }
105
+
106
+ _tick() {
107
+ if (!this.target) return;
108
+
109
+ const targetDate = new Date(this.target);
110
+ const now = new Date();
111
+ const diff = targetDate - now;
112
+
113
+ if (diff <= 0) {
114
+ this._past = true;
115
+ this._stopInterval();
116
+ return;
117
+ }
118
+
119
+ this._past = false;
120
+ const totalSeconds = Math.floor(diff / 1000);
121
+ const totalMinutes = Math.floor(totalSeconds / 60);
122
+ const totalHours = Math.floor(totalMinutes / 60);
123
+
124
+ const pad = (n) => String(n).padStart(2, '0');
125
+
126
+ this._days = Math.floor(totalHours / 24);
127
+ this._hours = pad(totalHours % 24);
128
+ this._minutes = pad(totalMinutes % 60);
129
+ this._seconds = pad(totalSeconds % 60);
130
+ }
131
+
132
+ /** Re-trigger the flip animation on value elements that changed. */
133
+ _animateChangedValues(changed) {
134
+ const keys = ['_days', '_hours', '_minutes', '_seconds'];
135
+ for (const key of keys) {
136
+ if (!changed.has(key)) continue;
137
+ const el = this.shadowRoot?.querySelector(`[data-unit="${key.slice(1)}"]`);
138
+ if (!el) continue;
139
+ el.classList.remove('countdown__value--flip');
140
+ // Force reflow so re-adding the class restarts the animation
141
+ void el.offsetWidth;
142
+ el.classList.add('countdown__value--flip');
143
+ }
144
+ }
145
+
146
+ render() {
147
+ if (!this.target) return nothing;
148
+
149
+ if (this._past) {
150
+ return html`<div class="countdown__past">${this['past-message']}</div>`;
151
+ }
152
+
153
+ return html`
156
154
  <div class="countdown" role="timer" aria-label="Countdown">
157
155
  <div class="countdown__unit">
158
156
  <span class="countdown__value" data-unit="days">${this._days}</span>
159
- <span class="countdown__label">${this["days-label"]}</span>
157
+ <span class="countdown__label">${this['days-label']}</span>
160
158
  </div>
161
159
  <div class="countdown__unit">
162
160
  <span class="countdown__value" data-unit="hours">${this._hours}</span>
163
- <span class="countdown__label">${this["hours-label"]}</span>
161
+ <span class="countdown__label">${this['hours-label']}</span>
164
162
  </div>
165
163
  <div class="countdown__unit">
166
164
  <span class="countdown__value" data-unit="minutes">${this._minutes}</span>
167
- <span class="countdown__label">${this["minutes-label"]}</span>
165
+ <span class="countdown__label">${this['minutes-label']}</span>
168
166
  </div>
169
167
  ${
170
- this["show-seconds"]
171
- ? html`
168
+ this['show-seconds']
169
+ ? html`
172
170
  <div class="countdown__unit">
173
171
  <span class="countdown__value" data-unit="seconds">${this._seconds}</span>
174
- <span class="countdown__label">${this["seconds-label"]}</span>
172
+ <span class="countdown__label">${this['seconds-label']}</span>
175
173
  </div>
176
174
  `
177
- : nothing
178
- }
175
+ : nothing
176
+ }
179
177
  </div>
180
178
  `;
181
- }
179
+ }
182
180
  }
@@ -1,62 +1,78 @@
1
1
  *,
2
2
  *::before,
3
3
  *::after {
4
- box-sizing: border-box;
4
+ box-sizing: border-box;
5
5
  }
6
6
 
7
7
  :host {
8
- display: block;
8
+ display: block;
9
9
  }
10
10
 
11
11
  .countdown {
12
- display: flex;
13
- justify-content: center;
14
- gap: var(--g-spacing-lg, 2rem);
15
- padding: var(--g-spacing-lg, 2rem) var(--g-spacing-md, 1rem);
16
- text-align: center;
12
+ display: flex;
13
+ justify-content: center;
14
+ gap: var(--g-spacing-lg, 2rem);
15
+ padding: var(--g-spacing-lg, 2rem) var(--g-spacing-md, 1rem);
16
+ text-align: center;
17
17
  }
18
18
 
19
19
  .countdown__unit {
20
- display: flex;
21
- flex-direction: column;
22
- align-items: center;
23
- gap: var(--g-spacing-xs, 0.25rem);
20
+ display: flex;
21
+ flex-direction: column;
22
+ align-items: center;
23
+ gap: var(--g-spacing-xs, 0.25rem);
24
24
  }
25
25
 
26
26
  .countdown__value {
27
- font-size: var(--g-typography-h1-font-size, 6.25rem);
28
- font-weight: var(--g-typography-h1-font-weight, 900);
29
- line-height: var(--g-typography-h1-line-height, 1.1);
30
- color: var(--g-color-content-default);
31
- font-variant-numeric: tabular-nums;
27
+ font-size: var(--g-typography-h1-font-size, 6.25rem);
28
+ font-weight: var(--g-typography-h1-font-weight, 900);
29
+ line-height: var(--g-typography-h1-line-height, 1.1);
30
+ color: var(--g-color-content-default);
31
+ font-variant-numeric: tabular-nums;
32
32
  }
33
33
 
34
34
  .countdown__value--flip {
35
- animation: countdown-flip 0.35s ease-out;
35
+ animation: countdown-flip 0.35s ease-out;
36
+ }
37
+
38
+ /* Only the movement is dropped; the fade stays for everyone. */
39
+ @media (prefers-reduced-motion: reduce) {
40
+ .countdown__value--flip {
41
+ animation-name: countdown-fade;
42
+ }
36
43
  }
37
44
 
38
45
  .countdown__label {
39
- font-size: var(--g-typography-body-sm-font-size, 0.875rem);
40
- color: var(--g-color-content-subtle);
41
- text-transform: uppercase;
42
- letter-spacing: 0.05em;
46
+ font-size: var(--g-typography-body-sm-font-size, 0.875rem);
47
+ color: var(--g-color-content-subtle);
48
+ text-transform: uppercase;
49
+ letter-spacing: 0.05em;
43
50
  }
44
51
 
45
52
  .countdown__past {
46
- font-size: var(--g-typography-h2-font-size, 3rem);
47
- font-weight: var(--g-typography-h2-font-weight, 900);
48
- color: var(--g-color-content-default);
49
- text-align: center;
50
- padding: var(--g-spacing-lg, 2rem) var(--g-spacing-md, 1rem);
53
+ font-size: var(--g-typography-h2-font-size, 3rem);
54
+ font-weight: var(--g-typography-h2-font-weight, 900);
55
+ color: var(--g-color-content-default);
56
+ text-align: center;
57
+ padding: var(--g-spacing-lg, 2rem) var(--g-spacing-md, 1rem);
51
58
  }
52
59
 
53
60
  @keyframes countdown-flip {
54
- 0% {
55
- opacity: 0;
56
- transform: translateY(0.25em);
57
- }
58
- 100% {
59
- opacity: 1;
60
- transform: translateY(0);
61
- }
61
+ 0% {
62
+ opacity: 0;
63
+ transform: translateY(0.25em);
64
+ }
65
+ 100% {
66
+ opacity: 1;
67
+ transform: translateY(0);
68
+ }
69
+ }
70
+
71
+ @keyframes countdown-fade {
72
+ 0% {
73
+ opacity: 0;
74
+ }
75
+ 100% {
76
+ opacity: 1;
77
+ }
62
78
  }
@@ -1,6 +1,6 @@
1
- import { GrantCodesCountdown } from "./countdown.component.js";
1
+ import { GrantCodesCountdown } from './countdown.component.js';
2
2
 
3
- export * from "./countdown.component.js";
3
+ export * from './countdown.component.js';
4
4
  export default GrantCodesCountdown;
5
5
 
6
- customElements.define("grantcodes-countdown", GrantCodesCountdown);
6
+ customElements.define('grantcodes-countdown', GrantCodesCountdown);
@@ -1,9 +1,9 @@
1
- import React from "react";
2
- import { createComponent } from "@lit/react";
3
- import { GrantCodesCountdown } from "./countdown.js";
1
+ import { createComponent } from '@lit/react';
2
+ import React from 'react';
3
+ import { GrantCodesCountdown } from './countdown.js';
4
4
 
5
5
  export const Countdown = createComponent({
6
- tagName: "grantcodes-countdown",
7
- elementClass: GrantCodesCountdown,
8
- react: React,
6
+ tagName: 'grantcodes-countdown',
7
+ elementClass: GrantCodesCountdown,
8
+ react: React,
9
9
  });