@orangesk/orange-design-system 3.1.0 → 3.3.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 (135) hide show
  1. package/build/components/Avatar/style.css +2 -0
  2. package/build/components/Avatar/style.css.map +1 -0
  3. package/build/components/BodyBanner/style.css +1 -1
  4. package/build/components/BodyBanner/style.css.map +1 -1
  5. package/build/components/Carousel/style.css +1 -1
  6. package/build/components/Carousel/style.css.map +1 -1
  7. package/build/components/Countdown/style.css +2 -0
  8. package/build/components/Countdown/style.css.map +1 -0
  9. package/build/components/DocumentationSidebar/style.css +1 -1
  10. package/build/components/DocumentationSidebar/style.css.map +1 -1
  11. package/build/components/Footer/style.css +1 -1
  12. package/build/components/Footer/style.css.map +1 -1
  13. package/build/components/List/style.css +1 -1
  14. package/build/components/List/style.css.map +1 -1
  15. package/build/components/Megamenu/style.css +1 -1
  16. package/build/components/Megamenu/style.css.map +1 -1
  17. package/build/components/Pill/style.css +1 -1
  18. package/build/components/Pill/style.css.map +1 -1
  19. package/build/components/Preview/style.css +1 -1
  20. package/build/components/Preview/style.css.map +1 -1
  21. package/build/components/StarRating/style.css +2 -0
  22. package/build/components/StarRating/style.css.map +1 -0
  23. package/build/components/index.js +1 -1
  24. package/build/components/index.js.map +1 -1
  25. package/build/components/tsconfig.tsbuildinfo +1 -1
  26. package/build/components/types/index.d.ts +65 -15
  27. package/build/components/types/src/components/Avatar/Avatar.d.ts +7 -0
  28. package/build/components/types/src/components/Avatar/index.d.ts +2 -0
  29. package/build/components/types/src/components/Carousel/Carousel.d.ts +10 -3
  30. package/build/components/types/src/components/Carousel/constants.d.ts +2 -0
  31. package/build/components/types/src/components/Countdown/Countdown.d.ts +10 -0
  32. package/build/components/types/src/components/Countdown/Countdown.react.d.ts +8 -0
  33. package/build/components/types/src/components/Countdown/Countdown.shared.d.ts +54 -0
  34. package/build/components/types/src/components/Countdown/Countdown.static.d.ts +21 -0
  35. package/build/components/types/src/components/Countdown/CountdownTag.d.ts +9 -0
  36. package/build/components/types/src/components/Countdown/CountdownTag.static.d.ts +16 -0
  37. package/build/components/types/src/components/Countdown/constants.d.ts +28 -0
  38. package/build/components/types/src/components/Countdown/index.d.ts +3 -0
  39. package/build/components/types/src/components/DocumentationSidebar/DocumentationSidebar.d.ts +1 -0
  40. package/build/components/types/src/components/Preview/PreviewGenerator.d.ts +1 -0
  41. package/build/components/types/src/components/PromoBanner/PromoBanner.d.ts +5 -1
  42. package/build/components/types/src/components/StarRating/StarRating.d.ts +9 -0
  43. package/build/components/types/src/components/StarRating/index.d.ts +1 -0
  44. package/build/components/types/src/components/index.d.ts +5 -3
  45. package/build/components/types/src/scripts/index.d.ts +10 -0
  46. package/build/components/types/src/styles/export/color.d.ts +2 -0
  47. package/build/lib/base.css +1 -1
  48. package/build/lib/base.css.map +1 -1
  49. package/build/lib/components.css +1 -1
  50. package/build/lib/components.css.map +1 -1
  51. package/build/lib/footer.css +1 -1
  52. package/build/lib/footer.css.map +1 -1
  53. package/build/lib/megamenu.css +1 -1
  54. package/build/lib/megamenu.css.map +1 -1
  55. package/build/lib/scripts.js +1 -1
  56. package/build/lib/scripts.js.map +1 -1
  57. package/build/lib/style.css +1 -1
  58. package/build/lib/style.css.map +1 -1
  59. package/build/lib/tsconfig.tsbuildinfo +1 -1
  60. package/build/lib/utilities.css +1 -1
  61. package/build/lib/utilities.css.map +1 -1
  62. package/build/sprite.svg +1 -1
  63. package/package.json +30 -29
  64. package/src/assets/icons/add.svg +4 -1
  65. package/src/assets/icons/close.svg +3 -3
  66. package/src/assets/icons/menu.svg +4 -1
  67. package/src/assets/icons/pause.svg +3 -3
  68. package/src/assets/icons/play.svg +2 -2
  69. package/src/assets/icons/quote.svg +1 -1
  70. package/src/assets/icons/remove.svg +4 -1
  71. package/src/assets/icons/search.svg +4 -1
  72. package/src/assets/icons/star-empty-thin.svg +4 -0
  73. package/src/components/Avatar/Avatar.tsx +36 -0
  74. package/src/components/Avatar/index.ts +2 -0
  75. package/src/components/Avatar/styles/config.scss +11 -0
  76. package/src/components/Avatar/styles/mixins.scss +53 -0
  77. package/src/components/Avatar/styles/style.scss +20 -0
  78. package/src/components/Avatar/tests/Avatar.conformance.test.jsx +23 -0
  79. package/src/components/Avatar/tests/Avatar.unit.test.jsx +45 -0
  80. package/src/components/BodyBanner/styles/mixins.scss +5 -1
  81. package/src/components/Carousel/Carousel.static.ts +17 -3
  82. package/src/components/Carousel/Carousel.tsx +55 -32
  83. package/src/components/Carousel/constants.ts +2 -0
  84. package/src/components/Carousel/styles/config.scss +5 -8
  85. package/src/components/Carousel/styles/mixins.scss +48 -78
  86. package/src/components/Carousel/styles/style.scss +8 -21
  87. package/src/components/Carousel/tests/Carousel.unit.test.jsx +77 -5
  88. package/src/components/Countdown/Countdown.react.ts +67 -0
  89. package/src/components/Countdown/Countdown.shared.ts +228 -0
  90. package/src/components/Countdown/Countdown.static.ts +201 -0
  91. package/src/components/Countdown/Countdown.tsx +105 -0
  92. package/src/components/Countdown/CountdownTag.static.ts +140 -0
  93. package/src/components/Countdown/CountdownTag.tsx +99 -0
  94. package/src/components/Countdown/constants.ts +36 -0
  95. package/src/components/Countdown/index.ts +3 -0
  96. package/src/components/Countdown/styles/config.scss +17 -0
  97. package/src/components/Countdown/styles/mixins.scss +193 -0
  98. package/src/components/Countdown/styles/style.scss +82 -0
  99. package/src/components/{Testimonial/tests/Testimonial.conformance.test.jsx → Countdown/tests/Countdown.conformance.test.jsx} +7 -2
  100. package/src/components/Countdown/tests/Countdown.static.test.jsx +117 -0
  101. package/src/components/Countdown/tests/Countdown.unit.test.jsx +101 -0
  102. package/src/components/Countdown/tests/CountdownTag.conformance.test.jsx +21 -0
  103. package/src/components/Countdown/tests/CountdownTag.static.test.jsx +30 -0
  104. package/src/components/Countdown/tests/CountdownTag.unit.test.jsx +67 -0
  105. package/src/components/DocumentationSidebar/DocumentationSidebar.tsx +14 -1
  106. package/src/components/DocumentationSidebar/styles/style.scss +11 -0
  107. package/src/components/Forms/Autocomplete/Autocomplete.static.ts +14 -1
  108. package/src/components/Forms/Autocomplete/styles/mixins.scss +3 -2
  109. package/src/components/Forms/Autocomplete/tests/Autocomplete.styles.unit.test.jsx +20 -0
  110. package/src/components/List/styles/config.scss +1 -1
  111. package/src/components/Pill/styles/style.scss +2 -1
  112. package/src/components/Preview/PreviewGenerator.tsx +137 -103
  113. package/src/components/Preview/styles/style.scss +15 -9
  114. package/src/components/PromoBanner/PromoBanner.tsx +24 -7
  115. package/src/components/PromoBanner/tests/PromoBanner.unit.test.jsx +31 -0
  116. package/src/components/StarRating/StarRating.tsx +52 -0
  117. package/src/components/StarRating/index.ts +1 -0
  118. package/src/components/StarRating/styles/style.scss +28 -0
  119. package/src/components/StarRating/tests/StarRating.unit.test.jsx +62 -0
  120. package/src/components/index.ts +9 -3
  121. package/src/styles/export/color.js +2 -0
  122. package/src/styles/tokens/color.scss +2 -0
  123. package/src/styles/typography/mixins.scss +4 -0
  124. package/src/styles/utilities/text.scss +9 -0
  125. package/build/components/Testimonial/style.css +0 -2
  126. package/build/components/Testimonial/style.css.map +0 -1
  127. package/build/components/types/src/components/Testimonial/Testimonial.d.ts +0 -10
  128. package/build/components/types/src/components/Testimonial/index.d.ts +0 -1
  129. package/build/search-index.json +0 -430
  130. package/src/components/Testimonial/Testimonial.tsx +0 -38
  131. package/src/components/Testimonial/index.ts +0 -1
  132. package/src/components/Testimonial/styles/config.scss +0 -42
  133. package/src/components/Testimonial/styles/mixins.scss +0 -44
  134. package/src/components/Testimonial/styles/style.scss +0 -16
  135. package/src/components/Testimonial/tests/Testimonial.unit.test.jsx +0 -43
@@ -0,0 +1,201 @@
1
+ import {
2
+ BaseCountdownStatic,
3
+ CountdownConfig,
4
+ CountdownLabels,
5
+ CountdownUnit,
6
+ calculateState,
7
+ countdownUnits,
8
+ DayLabels,
9
+ getVisibleUnits,
10
+ normalizeDayLabels,
11
+ } from "./Countdown.shared";
12
+ import {
13
+ ATTR_COUNTDOWN_UNIT,
14
+ CLASS_FLIP,
15
+ SELECTOR_LABEL,
16
+ SELECTOR_UNIT,
17
+ SELECTOR_VALUE_LAYERS,
18
+ } from "./constants";
19
+
20
+ interface CountdownLeaf {
21
+ element: HTMLElement;
22
+ unit: CountdownUnit;
23
+ labels: HTMLElement[];
24
+ isFlipping: boolean;
25
+ flipTimeoutId?: number;
26
+ }
27
+
28
+ const defaultDayLabels: DayLabels = {
29
+ many: "dni",
30
+ few: "dni",
31
+ one: "deň",
32
+ };
33
+
34
+ const defaultLabels: Required<CountdownLabels> = {
35
+ days: defaultDayLabels,
36
+ hours: "hod",
37
+ minutes: "min",
38
+ seconds: "sek",
39
+ };
40
+
41
+ const defaultConfig: CountdownConfig = {
42
+ targetDate: "",
43
+ units: [...countdownUnits],
44
+ labels: defaultLabels,
45
+ };
46
+
47
+ const FLIP_DURATION_MS = 600;
48
+
49
+ const pad = (value: number): string =>
50
+ String(Math.max(0, value)).padStart(2, "0");
51
+
52
+ const getUnitLabel = (
53
+ unit: CountdownUnit,
54
+ value: number,
55
+ labels: Required<CountdownLabels>,
56
+ ): string => {
57
+ if (unit !== "days") {
58
+ return labels[unit];
59
+ }
60
+
61
+ const { many, few, one } = normalizeDayLabels(labels.days, defaultDayLabels);
62
+
63
+ if (value === 1) return one;
64
+ if (value >= 2 && value <= 4) return few;
65
+ return many;
66
+ };
67
+
68
+ export default class CountdownStatic extends BaseCountdownStatic {
69
+ leaves!: Map<CountdownUnit, CountdownLeaf>;
70
+
71
+ constructor(element: HTMLElement, config?: Partial<CountdownConfig>) {
72
+ super(element, config, {
73
+ defaultConfig,
74
+ instanceKey: "ODS_Countdown",
75
+ });
76
+ this.leaves = new Map();
77
+ this.init();
78
+ }
79
+
80
+ private getLabels(): Required<CountdownLabels> {
81
+ return {
82
+ ...defaultLabels,
83
+ ...(this.config.labels ?? {}),
84
+ days: normalizeDayLabels(this.config.labels?.days, defaultDayLabels),
85
+ };
86
+ }
87
+
88
+ protected collectElements(): void {
89
+ this.leaves.clear();
90
+
91
+ const visibleUnits = getVisibleUnits(this.config.units);
92
+
93
+ Array.from(
94
+ this.element.querySelectorAll<HTMLElement>(SELECTOR_UNIT),
95
+ ).forEach((leaf) => {
96
+ const unit = leaf.getAttribute(
97
+ ATTR_COUNTDOWN_UNIT,
98
+ ) as CountdownUnit | null;
99
+
100
+ if (!unit || !visibleUnits.includes(unit)) return;
101
+
102
+ this.leaves.set(unit, {
103
+ element: leaf,
104
+ unit,
105
+ labels: Array.from(leaf.querySelectorAll(SELECTOR_VALUE_LAYERS)),
106
+ isFlipping: false,
107
+ });
108
+ });
109
+ }
110
+
111
+ private setLeafValue(leaf: CountdownLeaf, value: string): void {
112
+ leaf.labels.forEach((label) => {
113
+ label.replaceChildren(document.createTextNode(value));
114
+ });
115
+ }
116
+
117
+ private flipLeaf(leaf: CountdownLeaf, value: string): void {
118
+ const [topLabel, frontLabel, backLabel, bottomLabel] = leaf.labels;
119
+
120
+ if (!topLabel || !frontLabel || !backLabel || !bottomLabel) {
121
+ return;
122
+ }
123
+
124
+ if (!topLabel.textContent) {
125
+ this.setLeafValue(leaf, value);
126
+ return;
127
+ }
128
+
129
+ if (leaf.isFlipping || topLabel.textContent === value) {
130
+ return;
131
+ }
132
+
133
+ if (leaf.flipTimeoutId) {
134
+ window.clearTimeout(leaf.flipTimeoutId);
135
+ }
136
+
137
+ leaf.isFlipping = true;
138
+ topLabel.replaceChildren(document.createTextNode(value));
139
+ backLabel.replaceChildren(document.createTextNode(value));
140
+ leaf.element.classList.add(CLASS_FLIP);
141
+
142
+ leaf.flipTimeoutId = window.setTimeout(() => {
143
+ frontLabel.replaceChildren(document.createTextNode(value));
144
+ bottomLabel.replaceChildren(document.createTextNode(value));
145
+ leaf.element.classList.remove(CLASS_FLIP);
146
+ leaf.isFlipping = false;
147
+ leaf.flipTimeoutId = undefined;
148
+ }, FLIP_DURATION_MS);
149
+ }
150
+
151
+ protected render(): void {
152
+ const units = getVisibleUnits(this.config.units);
153
+ const labels = this.getLabels();
154
+ const state = calculateState(this.config.targetDate, units);
155
+
156
+ units.forEach((unit) => {
157
+ const leaf = this.leaves.get(unit);
158
+
159
+ if (!leaf) {
160
+ return;
161
+ }
162
+
163
+ const value = state[unit];
164
+ const label = getUnitLabel(unit, value, labels);
165
+
166
+ this.flipLeaf(leaf, pad(value));
167
+ const labelElement = leaf.element.querySelector(SELECTOR_LABEL);
168
+ if (labelElement) {
169
+ labelElement.textContent = label;
170
+ }
171
+ });
172
+
173
+ if (this.liveRegion) {
174
+ const announcement = units
175
+ .map(
176
+ (unit) => `${state[unit]} ${getUnitLabel(unit, state[unit], labels)}`,
177
+ )
178
+ .join(", ");
179
+ this.liveRegion.textContent = announcement;
180
+ }
181
+ }
182
+
183
+ protected destroyElements(): void {
184
+ this.leaves.forEach((leaf) => {
185
+ if (leaf.flipTimeoutId) {
186
+ window.clearTimeout(leaf.flipTimeoutId);
187
+ leaf.flipTimeoutId = undefined;
188
+ }
189
+
190
+ leaf.isFlipping = false;
191
+ leaf.element.classList.remove(CLASS_FLIP);
192
+ });
193
+ }
194
+
195
+ static getInstance(el: HTMLElement): CountdownStatic | null {
196
+ return el && (el as any).ODS_Countdown ? (el as any).ODS_Countdown : null;
197
+ }
198
+ }
199
+
200
+ export type { CountdownConfig, CountdownLabels, CountdownUnit };
201
+ export { countdownUnits };
@@ -0,0 +1,105 @@
1
+ "use client";
2
+
3
+ import cx from "classnames";
4
+ import React from "react";
5
+
6
+ import { useStatic } from "../../utils/hooks";
7
+
8
+ import {
9
+ buildCountdownConfig,
10
+ defaultCountdownLabels,
11
+ getAriaLive,
12
+ getLabelText,
13
+ normalizeUnits,
14
+ } from "./Countdown.react";
15
+ import CountdownStatic, {
16
+ CountdownConfig,
17
+ CountdownLabels,
18
+ CountdownUnit,
19
+ countdownUnits,
20
+ } from "./Countdown.static";
21
+ import {
22
+ ATTR_COUNTDOWN,
23
+ ATTR_COUNTDOWN_CONFIG,
24
+ ATTR_COUNTDOWN_UNIT,
25
+ CLASS_ANNOUNCEMENT,
26
+ CLASS_BOTTOM,
27
+ CLASS_CARD,
28
+ CLASS_LABEL,
29
+ CLASS_LEAF_BACK,
30
+ CLASS_LEAF_FRONT,
31
+ CLASS_ROOT,
32
+ CLASS_TOP,
33
+ CLASS_UNIT,
34
+ CLASS_VALUE,
35
+ } from "./constants";
36
+
37
+ export interface CountdownProps
38
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
39
+ labels?: CountdownLabels;
40
+ targetDate: string;
41
+ units?: CountdownUnit[];
42
+ }
43
+
44
+ const Countdown: React.FC<CountdownProps> = ({
45
+ className,
46
+ labels,
47
+ targetDate,
48
+ units = [...countdownUnits],
49
+ ...other
50
+ }) => {
51
+ const normalizedUnits = normalizeUnits(units);
52
+ const config: CountdownConfig = buildCountdownConfig(
53
+ targetDate,
54
+ normalizedUnits,
55
+ labels,
56
+ );
57
+
58
+ const [elementRef] = useStatic(CountdownStatic, config);
59
+
60
+ return (
61
+ <div
62
+ className={cx(CLASS_ROOT, className)}
63
+ {...{ [ATTR_COUNTDOWN]: "true" }}
64
+ {...{ [ATTR_COUNTDOWN_CONFIG]: JSON.stringify(config) }}
65
+ ref={elementRef}
66
+ role="timer"
67
+ aria-live={getAriaLive(normalizedUnits)}
68
+ aria-atomic="true"
69
+ {...other}
70
+ >
71
+ <span className={`sr-only ${CLASS_ANNOUNCEMENT}`} />
72
+ {normalizedUnits.map((unit) => (
73
+ <div
74
+ key={unit}
75
+ className={CLASS_UNIT}
76
+ {...{ [ATTR_COUNTDOWN_UNIT]: unit }}
77
+ aria-hidden="true"
78
+ >
79
+ <div className={CLASS_CARD}>
80
+ <div className={CLASS_TOP}>
81
+ <span className={CLASS_VALUE}>00</span>
82
+ </div>
83
+ <div className={CLASS_LEAF_FRONT}>
84
+ <span className={CLASS_VALUE}>00</span>
85
+ </div>
86
+ <div className={CLASS_LEAF_BACK}>
87
+ <span className={CLASS_VALUE}>00</span>
88
+ </div>
89
+ <div className={CLASS_BOTTOM}>
90
+ <span className={CLASS_VALUE}>00</span>
91
+ </div>
92
+ </div>
93
+ <span className={CLASS_LABEL}>
94
+ {getLabelText(unit, labels, defaultCountdownLabels)}
95
+ </span>
96
+ </div>
97
+ ))}
98
+ </div>
99
+ );
100
+ };
101
+
102
+ Countdown.displayName = "Countdown";
103
+
104
+ export type { CountdownLabels, CountdownUnit };
105
+ export { Countdown, countdownUnits };
@@ -0,0 +1,140 @@
1
+ import {
2
+ BaseCountdownStatic,
3
+ CountdownConfig,
4
+ CountdownLabels,
5
+ CountdownUnit,
6
+ calculateState,
7
+ DayLabels,
8
+ getVisibleUnits,
9
+ normalizeDayLabels,
10
+ } from "./Countdown.shared";
11
+ import {
12
+ ATTR_COUNTDOWN_UNIT,
13
+ SELECTOR_TAG_LABEL,
14
+ SELECTOR_TAG_UNIT,
15
+ SELECTOR_TAG_VALUE,
16
+ } from "./constants";
17
+
18
+ interface CountdownTagUnit {
19
+ element: HTMLElement;
20
+ unit: CountdownUnit;
21
+ valueElement: HTMLElement | null;
22
+ }
23
+
24
+ const defaultDayLabels: DayLabels = {
25
+ many: "d",
26
+ few: "d",
27
+ one: "d",
28
+ };
29
+
30
+ const defaultLabels: Required<CountdownLabels> = {
31
+ days: defaultDayLabels,
32
+ hours: "h",
33
+ minutes: "m",
34
+ seconds: "s",
35
+ };
36
+
37
+ const defaultConfig: CountdownConfig = {
38
+ targetDate: "",
39
+ units: ["days", "hours", "minutes", "seconds"],
40
+ labels: defaultLabels,
41
+ };
42
+
43
+ const getUnitLabel = (
44
+ unit: CountdownUnit,
45
+ labels: Required<CountdownLabels>,
46
+ ): string => {
47
+ if (unit === "days") {
48
+ return normalizeDayLabels(labels.days, defaultDayLabels).many;
49
+ }
50
+
51
+ return labels[unit];
52
+ };
53
+
54
+ const formatValue = (value: number): string =>
55
+ value < 100
56
+ ? String(Math.max(0, value)).padStart(2, "0")
57
+ : String(Math.max(0, value));
58
+
59
+ export default class CountdownTagStatic extends BaseCountdownStatic {
60
+ units!: Map<CountdownUnit, CountdownTagUnit>;
61
+
62
+ constructor(element: HTMLElement, config?: Partial<CountdownConfig>) {
63
+ super(element, config, {
64
+ defaultConfig,
65
+ instanceKey: "ODS_CountdownTag",
66
+ });
67
+ this.units = new Map();
68
+ this.init();
69
+ }
70
+
71
+ private getLabels(): Required<CountdownLabels> {
72
+ return {
73
+ ...defaultLabels,
74
+ ...(this.config.labels ?? {}),
75
+ days: normalizeDayLabels(this.config.labels?.days, defaultDayLabels),
76
+ };
77
+ }
78
+
79
+ protected collectElements(): void {
80
+ this.units.clear();
81
+
82
+ const visibleUnits = getVisibleUnits(this.config.units);
83
+
84
+ Array.from(
85
+ this.element.querySelectorAll<HTMLElement>(SELECTOR_TAG_UNIT),
86
+ ).forEach((unitElement) => {
87
+ const unit = unitElement.getAttribute(
88
+ ATTR_COUNTDOWN_UNIT,
89
+ ) as CountdownUnit | null;
90
+
91
+ if (!unit || !visibleUnits.includes(unit)) return;
92
+
93
+ this.units.set(unit, {
94
+ element: unitElement,
95
+ unit,
96
+ valueElement: unitElement.querySelector(SELECTOR_TAG_VALUE),
97
+ });
98
+ });
99
+ }
100
+
101
+ protected render(): void {
102
+ const visibleUnits = getVisibleUnits(this.config.units);
103
+ const labels = this.getLabels();
104
+ const state = calculateState(this.config.targetDate, visibleUnits);
105
+
106
+ visibleUnits.forEach((unit) => {
107
+ const currentUnit = this.units.get(unit);
108
+
109
+ if (!currentUnit) {
110
+ return;
111
+ }
112
+
113
+ const value = state[unit];
114
+
115
+ if (currentUnit.valueElement) {
116
+ currentUnit.valueElement.textContent = formatValue(value);
117
+ }
118
+
119
+ const labelElement =
120
+ currentUnit.element.querySelector(SELECTOR_TAG_LABEL);
121
+ if (labelElement) {
122
+ labelElement.textContent = getUnitLabel(unit, labels);
123
+ }
124
+ });
125
+
126
+ if (this.liveRegion) {
127
+ this.liveRegion.textContent = visibleUnits
128
+ .map((unit) => `${state[unit]} ${getUnitLabel(unit, labels)}`)
129
+ .join(", ");
130
+ }
131
+ }
132
+
133
+ protected destroyElements(): void {}
134
+
135
+ static getInstance(el: HTMLElement): CountdownTagStatic | null {
136
+ return el && (el as any).ODS_CountdownTag
137
+ ? (el as any).ODS_CountdownTag
138
+ : null;
139
+ }
140
+ }
@@ -0,0 +1,99 @@
1
+ "use client";
2
+
3
+ import cx from "classnames";
4
+ import React from "react";
5
+
6
+ import { useStatic } from "../../utils/hooks";
7
+
8
+ import {
9
+ buildCountdownConfig,
10
+ defaultCountdownTagLabels,
11
+ getAriaLive,
12
+ getLabelText,
13
+ normalizeUnits,
14
+ } from "./Countdown.react";
15
+ import {
16
+ CountdownConfig,
17
+ CountdownLabels,
18
+ CountdownUnit,
19
+ } from "./Countdown.static";
20
+ import CountdownTagStatic from "./CountdownTag.static";
21
+ import {
22
+ ATTR_COUNTDOWN_CONFIG,
23
+ ATTR_COUNTDOWN_TAG,
24
+ ATTR_COUNTDOWN_TAG_VALUE,
25
+ ATTR_COUNTDOWN_UNIT,
26
+ CLASS_ANNOUNCEMENT,
27
+ CLASS_TAG_LABEL,
28
+ CLASS_TAG_PART,
29
+ CLASS_TAG_ROOT,
30
+ CLASS_TAG_SEPARATOR,
31
+ CLASS_TAG_VALUE,
32
+ } from "./constants";
33
+
34
+ export interface CountdownTagProps
35
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
36
+ labels?: CountdownLabels;
37
+ targetDate: string;
38
+ units?: CountdownUnit[];
39
+ }
40
+
41
+ const CountdownTag: React.FC<CountdownTagProps> = ({
42
+ className,
43
+ labels,
44
+ targetDate,
45
+ units = ["days", "hours", "minutes", "seconds"],
46
+ ...other
47
+ }) => {
48
+ const normalizedUnits = normalizeUnits(units);
49
+ const config: CountdownConfig = buildCountdownConfig(
50
+ targetDate,
51
+ normalizedUnits,
52
+ labels,
53
+ );
54
+
55
+ const [elementRef] = useStatic(CountdownTagStatic, config);
56
+
57
+ return (
58
+ <div
59
+ className={cx(CLASS_TAG_ROOT, className)}
60
+ {...{ [ATTR_COUNTDOWN_TAG]: "true" }}
61
+ {...{ [ATTR_COUNTDOWN_CONFIG]: JSON.stringify(config) }}
62
+ ref={elementRef}
63
+ role="timer"
64
+ aria-live={getAriaLive(normalizedUnits)}
65
+ aria-atomic="true"
66
+ {...other}
67
+ >
68
+ <span className={`sr-only ${CLASS_ANNOUNCEMENT}`} />
69
+ {normalizedUnits.map((unit, index) => (
70
+ <React.Fragment key={unit}>
71
+ <span
72
+ className={CLASS_TAG_PART}
73
+ {...{ [ATTR_COUNTDOWN_UNIT]: unit }}
74
+ aria-hidden="true"
75
+ >
76
+ <span
77
+ className={CLASS_TAG_VALUE}
78
+ {...{ [ATTR_COUNTDOWN_TAG_VALUE]: "true" }}
79
+ >
80
+ 00
81
+ </span>
82
+ <span className={CLASS_TAG_LABEL}>
83
+ {getLabelText(unit, labels, defaultCountdownTagLabels)}
84
+ </span>
85
+ </span>
86
+ {index < normalizedUnits.length - 1 && (
87
+ <span className={CLASS_TAG_SEPARATOR} aria-hidden="true">
88
+ :
89
+ </span>
90
+ )}
91
+ </React.Fragment>
92
+ ))}
93
+ </div>
94
+ );
95
+ };
96
+
97
+ CountdownTag.displayName = "CountdownTag";
98
+
99
+ export { CountdownTag };
@@ -0,0 +1,36 @@
1
+ export const CLASS_ROOT = "countdown";
2
+
3
+ export const CLASS_ANNOUNCEMENT = `${CLASS_ROOT}__announcement`;
4
+ export const CLASS_UNIT = `${CLASS_ROOT}__unit`;
5
+ export const CLASS_CARD = `${CLASS_ROOT}__card`;
6
+ export const CLASS_TOP = `${CLASS_ROOT}__top`;
7
+ export const CLASS_LEAF_FRONT = `${CLASS_ROOT}__leaf-front`;
8
+ export const CLASS_LEAF_BACK = `${CLASS_ROOT}__leaf-back`;
9
+ export const CLASS_BOTTOM = `${CLASS_ROOT}__bottom`;
10
+ export const CLASS_VALUE = `${CLASS_ROOT}__value`;
11
+ export const CLASS_LABEL = `${CLASS_ROOT}__label`;
12
+ export const CLASS_FLIP = "is-flipping";
13
+ export const CLASS_TAG_ROOT = "countdown-tag";
14
+ export const CLASS_TAG_PART = `${CLASS_TAG_ROOT}__part`;
15
+ export const CLASS_TAG_VALUE = `${CLASS_TAG_ROOT}__value`;
16
+ export const CLASS_TAG_LABEL = `${CLASS_TAG_ROOT}__label`;
17
+ export const CLASS_TAG_SEPARATOR = `${CLASS_TAG_ROOT}__separator`;
18
+
19
+ export const ATTR_COUNTDOWN = "data-countdown";
20
+ export const ATTR_COUNTDOWN_CONFIG = "data-countdown-config";
21
+ export const ATTR_COUNTDOWN_UNIT = "data-countdown-unit";
22
+ export const ATTR_COUNTDOWN_TAG = "data-countdown-tag";
23
+ export const ATTR_COUNTDOWN_TAG_VALUE = "data-countdown-tag-value";
24
+
25
+ export const SELECTOR_ANNOUNCEMENT = `.${CLASS_ANNOUNCEMENT}`;
26
+ export const SELECTOR_UNIT = `.${CLASS_UNIT}`;
27
+ export const SELECTOR_LABEL = `.${CLASS_LABEL}`;
28
+ export const SELECTOR_TAG_UNIT = `[${ATTR_COUNTDOWN_UNIT}]`;
29
+ export const SELECTOR_TAG_LABEL = `.${CLASS_TAG_LABEL}`;
30
+ export const SELECTOR_TAG_VALUE = `[${ATTR_COUNTDOWN_TAG_VALUE}]`;
31
+ export const SELECTOR_VALUE_LAYERS = [
32
+ `.${CLASS_TOP} .${CLASS_VALUE}`,
33
+ `.${CLASS_LEAF_FRONT} .${CLASS_VALUE}`,
34
+ `.${CLASS_LEAF_BACK} .${CLASS_VALUE}`,
35
+ `.${CLASS_BOTTOM} .${CLASS_VALUE}`,
36
+ ].join(", ");
@@ -0,0 +1,3 @@
1
+ export type { CountdownLabels, CountdownUnit } from "./Countdown";
2
+ export { Countdown, countdownUnits } from "./Countdown";
3
+ export { CountdownTag } from "./CountdownTag";
@@ -0,0 +1,17 @@
1
+ @use "../../../styles/tools/convert";
2
+
3
+ $unit-width: convert.to-rem(67.5px);
4
+ $unit-height: convert.to-rem(82px);
5
+ $unit-half-height: convert.to-rem(41px);
6
+ $unit-radius: convert.to-rem(10px);
7
+ $tag-gap: convert.to-rem(4px);
8
+ $tag-min-height: convert.to-rem(40px);
9
+ $tag-padding-y: convert.to-rem(9px);
10
+ $tag-padding-x: convert.to-rem(15px);
11
+ $tag-part-gap: convert.to-rem(3px);
12
+ $tag-value-font-size: convert.to-rem(16px);
13
+ $tag-value-line-height: convert.to-rem(18px);
14
+ $tag-value-letter-spacing: convert.to-rem(-0.2px);
15
+ $tag-label-font-size: convert.to-rem(12px);
16
+ $tag-label-line-height: convert.to-rem(14px);
17
+ $tag-separator-offset-y: convert.to-rem(-1px);