@salesforcedevs/arch-components 1.20.17-alpha8 → 1.25.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 (158) hide show
  1. package/LICENSE +12 -0
  2. package/lwc.config.json +42 -3
  3. package/package.json +36 -46
  4. package/src/assets/css/arch-variables.css +512 -0
  5. package/src/modules/arch/badge/badge.css +22 -0
  6. package/src/modules/arch/badge/badge.html +5 -0
  7. package/src/modules/arch/badge/badge.ts +9 -0
  8. package/src/modules/arch/button/button.css +1 -0
  9. package/src/modules/arch/button/button.html +20 -0
  10. package/src/modules/arch/button/button.ts +67 -0
  11. package/src/modules/arch/buttonLink/buttonLink.css +1 -0
  12. package/src/modules/arch/buttonLink/buttonLink.html +19 -0
  13. package/src/modules/arch/buttonLink/buttonLink.stories.js +34 -0
  14. package/src/modules/arch/buttonLink/buttonLink.ts +8 -0
  15. package/src/modules/arch/buttonStyles/buttonStyles.css +220 -0
  16. package/src/modules/arch/card/card.css +128 -0
  17. package/src/modules/arch/card/card.html +85 -0
  18. package/src/modules/arch/card/card.ts +277 -0
  19. package/src/modules/arch/cardBase/cardBase.css +11 -0
  20. package/src/modules/arch/cardBase/cardBase.html +2 -0
  21. package/src/modules/arch/cardGridA/cardGridA.css +11 -0
  22. package/src/modules/arch/cardGridA/cardGridA.html +21 -0
  23. package/src/modules/arch/cardGridA/cardGridA.stories.js +118 -0
  24. package/src/modules/arch/cardGridA/cardGridA.ts +24 -0
  25. package/src/modules/arch/cardGridC/cardGridC.css +24 -0
  26. package/src/modules/arch/cardGridC/cardGridC.html +22 -0
  27. package/src/modules/arch/cardGridC/cardGridC.stories.js +51 -0
  28. package/src/modules/arch/cardGridC/cardGridC.ts +11 -0
  29. package/src/modules/arch/cardGridD/cardGridD.css +17 -0
  30. package/src/modules/arch/cardGridD/cardGridD.html +20 -0
  31. package/src/modules/arch/cardGridD/cardGridD.stories.js +43 -0
  32. package/src/modules/arch/cardGridD/cardGridD.ts +7 -0
  33. package/src/modules/arch/cardNew/cardNew.css +31 -0
  34. package/src/modules/arch/cardNew/cardNew.html +32 -0
  35. package/src/modules/arch/cardNew/cardNew.ts +66 -0
  36. package/src/modules/arch/children/children.html +2 -0
  37. package/src/modules/arch/children/children.ts +31 -0
  38. package/src/modules/arch/color/color.ts +59 -0
  39. package/src/modules/arch/content/__fixtures__/index.ts +884 -0
  40. package/src/modules/arch/content/content.css +643 -0
  41. package/src/modules/arch/content/content.html +65 -0
  42. package/src/modules/arch/content/content.stories.js +21 -0
  43. package/src/modules/arch/content/content.ts +169 -0
  44. package/src/modules/arch/contentIcon/contentIcon.css +48 -0
  45. package/src/modules/arch/contentIcon/contentIcon.html +15 -0
  46. package/src/modules/arch/contentIcon/contentIcon.stories.js +130 -0
  47. package/src/modules/arch/contentIcon/contentIcon.ts +68 -0
  48. package/src/{common → modules/arch}/context/context.ts +21 -19
  49. package/src/modules/arch/contextAdapter/constants.ts +1 -0
  50. package/src/modules/arch/contextAdapter/contextAdapter.ts +54 -0
  51. package/src/modules/arch/debounce/debounce.ts +32 -0
  52. package/src/modules/arch/dialog/dialog.ts +154 -0
  53. package/src/modules/arch/dialogStyles/dialogStyles.css +90 -0
  54. package/src/modules/arch/effectAdapter/effectAdapter.html +1 -0
  55. package/src/modules/arch/effectAdapter/effectAdapter.ts +28 -0
  56. package/src/modules/arch/explorer/explorer.css +301 -0
  57. package/src/modules/arch/explorer/explorer.html +418 -0
  58. package/src/modules/arch/explorer/explorer.ts +718 -0
  59. package/src/modules/arch/explorer/types.d.ts +60 -0
  60. package/src/modules/arch/fetch/fetch.ts +55 -0
  61. package/src/modules/arch/footerMfe/footerMfe.html +3 -0
  62. package/src/modules/arch/footerMfe/footerMfe.ts +19 -0
  63. package/src/modules/arch/gallery/gallery.css +365 -0
  64. package/src/modules/arch/gallery/gallery.html +71 -0
  65. package/src/modules/arch/gallery/gallery.ts +366 -0
  66. package/src/modules/arch/gallery/types.d.ts +35 -0
  67. package/src/modules/arch/heading/heading.css +1 -0
  68. package/src/modules/arch/heading/heading.html +9 -0
  69. package/src/modules/arch/heading/heading.ts +36 -0
  70. package/src/modules/arch/helpers/helpers.ts +141 -0
  71. package/src/modules/arch/heroA/heroA.css +116 -0
  72. package/src/modules/arch/heroA/heroA.html +28 -0
  73. package/src/modules/arch/heroA/heroA.stories.js +60 -0
  74. package/src/modules/arch/heroA/heroA.ts +53 -0
  75. package/src/modules/arch/heroB/heroB.css +79 -0
  76. package/src/modules/arch/heroB/heroB.html +27 -0
  77. package/src/modules/arch/heroB/heroB.stories.js +55 -0
  78. package/src/modules/arch/heroB/heroB.ts +26 -0
  79. package/src/modules/arch/i18n/i18n.ts +78 -0
  80. package/src/modules/arch/icon/icon.css +28 -0
  81. package/src/modules/arch/icon/icon.html +17 -0
  82. package/src/modules/arch/icon/icon.stories.js +26 -0
  83. package/src/modules/arch/icon/icon.ts +92 -0
  84. package/src/modules/arch/instrumentation/instrumentation.css +1 -0
  85. package/src/modules/arch/instrumentation/instrumentation.html +1 -0
  86. package/src/modules/arch/instrumentation/instrumentation.ts +113 -0
  87. package/src/modules/arch/labels/helpers.ts +25 -0
  88. package/src/modules/arch/labels/pointHelpers.ts +47 -0
  89. package/src/modules/arch/labels/timeHelpers.ts +182 -0
  90. package/src/modules/arch/labels/types.d.ts +5 -0
  91. package/src/modules/arch/logger/logger.ts +33 -0
  92. package/src/modules/arch/menu/menu.ts +260 -0
  93. package/src/modules/arch/overflow/overflow.ts +71 -0
  94. package/src/modules/arch/page/page.css +3 -0
  95. package/src/modules/arch/page/page.html +3 -0
  96. package/src/modules/arch/page/page.stories.js +19 -0
  97. package/src/modules/arch/page/page.ts +3 -0
  98. package/src/modules/arch/pageHeaderA/pageHeaderA.css +82 -0
  99. package/src/modules/arch/pageHeaderA/pageHeaderA.html +24 -0
  100. package/src/modules/arch/pageHeaderA/pageHeaderA.stories.js +25 -0
  101. package/src/modules/arch/pageHeaderA/pageHeaderA.ts +51 -0
  102. package/src/modules/arch/pill/pill.css +70 -0
  103. package/src/modules/arch/pill/pill.html +17 -0
  104. package/src/modules/arch/pill/pill.ts +34 -0
  105. package/src/modules/arch/polling-request.ts +97 -0
  106. package/src/modules/arch/reflectedElement/reflectedElement.html +2 -0
  107. package/src/{common → modules/arch}/reflectedElement/reflectedElement.ts +5 -3
  108. package/src/modules/arch/reset/reset.css +39 -0
  109. package/src/modules/arch/searchList/searchList.css +120 -0
  110. package/src/modules/arch/searchList/searchList.html +46 -0
  111. package/src/modules/arch/searchList/searchList.ts +53 -0
  112. package/src/modules/arch/sectionA/sectionA.css +64 -0
  113. package/src/modules/arch/sectionA/sectionA.html +21 -0
  114. package/src/modules/arch/sectionA/sectionA.stories.js +25 -0
  115. package/src/modules/arch/sectionA/sectionA.ts +27 -0
  116. package/src/modules/arch/select/select.css +40 -0
  117. package/src/modules/arch/select/select.html +24 -0
  118. package/src/modules/arch/select/select.ts +64 -0
  119. package/src/modules/arch/socialShare/socialShare.css +50 -0
  120. package/src/modules/arch/socialShare/socialShare.html +56 -0
  121. package/src/modules/arch/socialShare/socialShare.ts +29 -0
  122. package/src/modules/arch/spinner/spinner.css +195 -0
  123. package/src/modules/arch/spinner/spinner.html +9 -0
  124. package/src/modules/arch/spinner/spinner.ts +15 -0
  125. package/src/modules/arch/styles/styles.css +24 -0
  126. package/src/modules/arch/summary/summary.css +134 -0
  127. package/src/modules/arch/summary/summary.html +71 -0
  128. package/src/modules/arch/summary/summary.stories.js +163 -0
  129. package/src/modules/arch/summary/summary.ts +96 -0
  130. package/src/modules/arch/tab/tab.css +3 -0
  131. package/src/modules/arch/tab/tab.html +5 -0
  132. package/src/modules/arch/tab/tab.ts +46 -0
  133. package/src/modules/arch/tabset/tabset.css +112 -0
  134. package/src/modules/arch/tabset/tabset.html +62 -0
  135. package/src/modules/arch/tabset/tabset.ts +244 -0
  136. package/src/modules/arch/testutils.ts +118 -0
  137. package/src/modules/arch/threeCardGrid/threeCardGrid.css +6 -0
  138. package/src/modules/arch/threeCardGrid/threeCardGrid.html +5 -0
  139. package/src/modules/arch/threeCardGrid/threeCardGrid.ts +3 -0
  140. package/src/modules/arch/track/track.ts +23 -0
  141. package/src/modules/arch/trailhead.ts +120 -0
  142. package/src/modules/arch/types.d.ts +1 -0
  143. package/src/modules/arch/useEffectAttr.ts +16 -0
  144. package/src/modules/arch/utils/utils.ts +20 -0
  145. package/src/modules/arch/withState.ts +21 -0
  146. package/src/modules/arch/xsfMfeEvents/xsfMfeEvents.html +1 -0
  147. package/src/modules/arch/xsfMfeEvents/xsfMfeEvents.ts +47 -0
  148. package/src/common/effectAdapter/__tests__/effectAdapter.test.ts +0 -12
  149. package/src/common/effectAdapter/effectAdapter.ts +0 -18
  150. package/src/common/reflectedElement/__tests__/modules/test/select/select.html +0 -3
  151. package/src/common/reflectedElement/__tests__/modules/test/select/select.ts +0 -7
  152. package/src/common/reflectedElement/__tests__/modules/test/selectTransform/selectTransform.html +0 -3
  153. package/src/common/reflectedElement/__tests__/modules/test/selectTransform/selectTransform.ts +0 -18
  154. package/src/common/reflectedElement/__tests__/reflectedElement.test.ts +0 -75
  155. package/src/common/slot/__tests__/slot.test.ts +0 -96
  156. package/src/common/slot/slot.ts +0 -20
  157. /package/src/{common → modules/arch}/context/context.html +0 -0
  158. /package/src/{common/effectAdapter/effectAdapter.html → modules/arch/contextAdapter/contextAdapter.html} +0 -0
@@ -0,0 +1,25 @@
1
+ import { LabelsDict } from './types';
2
+
3
+ const DEFAULT_LANGUAGE = 'en';
4
+
5
+ export function label(
6
+ lang: string,
7
+ dict: LabelsDict,
8
+ labelKey: string,
9
+ values: (string | number)[] = []
10
+ ): string {
11
+ const [langBase] = lang.split('-');
12
+ const labels = {
13
+ ...dict[DEFAULT_LANGUAGE],
14
+ ...dict[langBase],
15
+ ...dict[lang]
16
+ };
17
+ let str = labels[labelKey];
18
+ if (str === undefined) {
19
+ return labelKey;
20
+ }
21
+ for (const val of values) {
22
+ str = str.replace('%{n}', val.toString());
23
+ }
24
+ return str;
25
+ }
@@ -0,0 +1,47 @@
1
+ import { LabelsDict } from './types';
2
+ import { label } from './helpers';
3
+
4
+ export const labels: LabelsDict = {
5
+ en: {
6
+ zero: '0 Points',
7
+ one: '+1 Point',
8
+ many: '+%{n} Points'
9
+ },
10
+ de: {
11
+ zero: '0 Punkte',
12
+ one: '+1 Punkt',
13
+ many: '+%{n} Punkte'
14
+ },
15
+ es: {
16
+ zero: '0 puntos',
17
+ one: '+1 punto',
18
+ many: '+%{n} puntos'
19
+ },
20
+ fr: {
21
+ zero: '0 point',
22
+ one: '+ 1 point',
23
+ many: '+ %{n} points'
24
+ },
25
+ ja: {
26
+ zero: '0 ポイント',
27
+ one: '+1 ポイント',
28
+ many: '+%{n} ポイント'
29
+ },
30
+ pt: {
31
+ zero: '0 pontos',
32
+ one: '+1 ponto',
33
+ many: '+%{n} pontos'
34
+ }
35
+ };
36
+
37
+ export function pointLabel(lang: string, points: number | undefined) {
38
+ if (points !== undefined) {
39
+ if (points === 0) {
40
+ return label(lang, labels, 'zero', [points]);
41
+ } else if (points === 1) {
42
+ return label(lang, labels, 'one', [points]);
43
+ }
44
+ return label(lang, labels, 'many', [points]);
45
+ }
46
+ return '';
47
+ }
@@ -0,0 +1,182 @@
1
+ import { LabelsDict } from './types';
2
+ import { label } from './helpers';
3
+
4
+ const labels: LabelsDict = {
5
+ en: {
6
+ minute: '~%{n} min',
7
+ minutes: '~%{n} mins',
8
+ hour: '~%{n} hr',
9
+ hours: '~%{n} hrs',
10
+ hourMinute: '~%{n} hr %{n} min',
11
+ hourMinutes: '~%{n} hr %{n} mins',
12
+ hoursMinute: '~%{n} hrs %{n} min',
13
+ hoursMinutes: '~%{n} hrs %{n} mins',
14
+
15
+ minuteLeft: '~%{n} min left',
16
+ minutesLeft: '~%{n} mins left',
17
+ hourLeft: '~%{n} hr left',
18
+ hoursLeft: '~%{n} hrs left',
19
+ hourMinuteLeft: '~%{n} hr %{n} min left',
20
+ hourMinutesLeft: '~%{n} hr %{n} mins left',
21
+ hoursMinuteLeft: '~%{n} hrs %{n} min left',
22
+ hoursMinutesLeft: '~%{n} hrs %{n} mins left'
23
+ },
24
+ es: {
25
+ minute: '~%{n} minuto',
26
+ minutes: '~%{n} minutos',
27
+ hour: '~%{n} hora',
28
+ hours: '~%{n} horas',
29
+ hourMinute: '~%{n} hora %{n} minuto',
30
+ hourMinutes: '~%{n} hora %{n} minutos',
31
+ hoursMinute: '~%{n} horas %{n} minuto',
32
+ hoursMinutes: '~%{n} horas %{n} minutos',
33
+
34
+ minuteLeft: '~%{n} minuto restante',
35
+ minutesLeft: '~%{n} minutos restantes',
36
+ hourLeft: '~%{n} hora restante',
37
+ hoursLeft: '~%{n} horas restantes',
38
+ hourMinuteLeft: '~%{n} hora %{n} minuto restantes',
39
+ hourMinutesLeft: '~%{n} hora %{n} minutos restantes',
40
+ hoursMinuteLeft: '~%{n} horas %{n} minuto restantes',
41
+ hoursMinutesLeft: '~%{n} horas %{n} minutos restantes'
42
+ },
43
+ de: {
44
+ minute: '~%{n} min',
45
+ minutes: '~%{n} min',
46
+ hour: '~%{n} Std',
47
+ hours: '~%{n} Std',
48
+ hourMinute: '~%{n} Std %{n} min',
49
+ hourMinutes: '~%{n} Std %{n} min',
50
+ hoursMinute: '~%{n} Std %{n} min',
51
+ hoursMinutes: '~%{n} Std %{n} min',
52
+
53
+ minuteLeft: '~%{n} min verbleibt',
54
+ minutesLeft: '~%{n} min verbleiben',
55
+ hourLeft: '~%{n} Std verbleibt',
56
+ hoursLeft: '~%{n} Std verbleiben',
57
+ hourMinuteLeft: '~%{n} Std %{n} min verbleiben',
58
+ hourMinutesLeft: '~%{n} Std %{n} min verbleiben',
59
+ hoursMinuteLeft: '~Noch %{n} Stunden %{n} Minute',
60
+ hoursMinutesLeft: '~%{n} Std %{n} min verbleiben'
61
+ },
62
+ fr: {
63
+ minute: '%{n} min',
64
+ minutes: '%{n} min',
65
+ hour: '%{n} h',
66
+ hours: '%{n} h',
67
+ hourMinute: '%{n} h %{n} min',
68
+ hourMinutes: '%{n} h %{n} min',
69
+ hoursMinute: '%{n} h %{n} min',
70
+ hoursMinutes: '%{n} h %{n} min',
71
+
72
+ minuteLeft: 'Il reste %{n} min',
73
+ minutesLeft: 'Il reste %{n} min',
74
+ hourLeft: 'Il reste %{n} h',
75
+ hoursLeft: 'Il reste %{n} h',
76
+ hourMinuteLeft: 'Il reste %{n} h %{n} min',
77
+ hourMinutesLeft: 'Il reste %{n} h %{n} min',
78
+ hoursMinuteLeft: 'Il reste %{n} h %{n} min',
79
+ hoursMinutesLeft: 'Il reste %{n} h %{n} min'
80
+ },
81
+ ja: {
82
+ minute: '%{n} 分',
83
+ minutes: '%{n} 分',
84
+ hour: '%{n} 時間',
85
+ hours: '%{n} 時間',
86
+ hourMinute: '%{n} 時間 %{n} 分',
87
+ hourMinutes: '%{n} 時間 %{n} 分',
88
+ hoursMinute: '%{n} 時間 %{n} 分',
89
+ hoursMinutes: '%{n} 時間 %{n} 分',
90
+
91
+ minuteLeft: '残り %{n} 分',
92
+ minutesLeft: '残り %{n} 分',
93
+ hourLeft: '残り %{n} 時間',
94
+ hoursLeft: '残り %{n} 時間',
95
+ hourMinuteLeft: '残り %{n} 時間 %{n} 分',
96
+ hourMinutesLeft: '残り %{n} 時間 %{n} 分',
97
+ hoursMinuteLeft: '残り %{n} 時間 %{n} 分',
98
+ hoursMinutesLeft: '残り %{n} 時間 %{n} 分'
99
+ },
100
+ pt: {
101
+ minute: '~%{n}min',
102
+ minutes: '~%{n}min',
103
+ hour: '~%{n}h',
104
+ hours: '~%{n}h',
105
+ hourMinute: '~%{hours}h%{minutes}min',
106
+ hourMinutes: '~%{hours}h%{minutes}min',
107
+ hoursMinute: '~%{hours}h%{minutes}min',
108
+ hoursMinutes: '~%{hours}h%{minutes}min',
109
+
110
+ minuteLeft: '~%{n} minuto restantes',
111
+ minutesLeft: '~%{n} minutos restantes',
112
+ hourLeft: '~%{n}h restante',
113
+ hoursLeft: '~%{n}h restantes',
114
+ hourMinuteLeft: '~%{n}h%{n}min restante',
115
+ hourMinutesLeft: '~%{n}h%{n}min restantes',
116
+ hoursMinuteLeft: '~%{n}h%{n}min restante',
117
+ hoursMinutesLeft: '~%{n}h%{n}min restantes'
118
+ }
119
+ };
120
+
121
+ function hoursMinutesLabelArgs(timeInMinutes: number) {
122
+ const hrs = Math.floor(timeInMinutes / 60);
123
+ const mins = timeInMinutes % 60;
124
+
125
+ let key = '';
126
+ const values = [];
127
+
128
+ if (hrs > 0) {
129
+ values.push(hrs);
130
+ }
131
+
132
+ values.push(mins);
133
+
134
+ if (hrs === 0 && mins === 0) {
135
+ key = 'minutes';
136
+ }
137
+ if (hrs === 0 && mins === 1) {
138
+ key = 'minute';
139
+ }
140
+ if (hrs === 0 && mins > 1) {
141
+ key = 'minutes';
142
+ }
143
+ if (hrs === 1 && mins === 0) {
144
+ key = 'hour';
145
+ }
146
+ if (hrs === 1 && mins === 1) {
147
+ key = 'hourMinute';
148
+ }
149
+ if (hrs === 1 && mins > 1) {
150
+ key = 'hourMinutes';
151
+ }
152
+ if (hrs > 1 && mins === 0) {
153
+ key = 'hours';
154
+ }
155
+ if (hrs > 1 && mins === 1) {
156
+ key = 'hoursMinute';
157
+ }
158
+ if (hrs > 1 && mins > 1) {
159
+ key = 'hoursMinutes';
160
+ }
161
+
162
+ return {
163
+ key,
164
+ values
165
+ };
166
+ }
167
+
168
+ export function hoursMinutesLabel(lang: string, timeInMinutes: number) {
169
+ const labelInfo = hoursMinutesLabelArgs(timeInMinutes);
170
+
171
+ return timeInMinutes
172
+ ? label(lang, labels, `${labelInfo.key}`, labelInfo.values)
173
+ : '';
174
+ }
175
+
176
+ export function hoursMinutesLeftLabel(lang: string, timeInMinutes: number) {
177
+ const labelInfo = hoursMinutesLabelArgs(timeInMinutes);
178
+
179
+ return timeInMinutes
180
+ ? label(lang, labels, `${labelInfo.key}Left`, labelInfo.values)
181
+ : '';
182
+ }
@@ -0,0 +1,5 @@
1
+ export type LabelsDict = {
2
+ [key: string]: {
3
+ [key: string]: string;
4
+ };
5
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @file Utility for logging messages to the console
3
+ * @example
4
+ * logger.warn("oops!");
5
+ */
6
+
7
+ type LoggerOptions = {
8
+ prefix: string;
9
+ };
10
+
11
+ export class Logger {
12
+ constructor(opts: Partial<LoggerOptions> = {}) {
13
+ this.options = { ...this.defaultOptions, ...opts };
14
+ }
15
+
16
+ private defaultOptions = {
17
+ prefix: '[th-components]: '
18
+ } as LoggerOptions;
19
+
20
+ private options: LoggerOptions = this.defaultOptions;
21
+
22
+ warn(msg: string) {
23
+ console.warn(`${this.options.prefix}${msg}`.trim());
24
+ }
25
+
26
+ error(msg: string) {
27
+ if (typeof (window as any).__DEBUG__ !== 'undefined' && (window as any).__DEBUG__) {
28
+ console.error(`${this.options.prefix}${msg}`.trim());
29
+ }
30
+ }
31
+ }
32
+
33
+ export default new Logger();
@@ -0,0 +1,260 @@
1
+ //import { getFocusableElements } from 'shared/a11y';
2
+
3
+ export type MenuState = {
4
+ count: number;
5
+ open: boolean;
6
+ selectedIndex: number;
7
+ setup: boolean;
8
+ };
9
+
10
+ type ShadowRoot = {
11
+ querySelector: (selector: string) => Element | null;
12
+ querySelectorAll: (selector: string) => Element[];
13
+ activeElement: Element | null;
14
+ };
15
+
16
+ type Input = {
17
+ onChange: (state: MenuState) => void;
18
+ shadowRoot: ShadowRoot;
19
+ };
20
+
21
+ export type Selector = "trigger" | "menu" | "item";
22
+
23
+ const SELECTOR_TRIGGER = "trigger";
24
+ const SELECTOR_MENU = "menu";
25
+ const SELECTOR_ITEM = "item";
26
+
27
+ export function getSelector(selector: Selector) {
28
+ return `[data-menu="${selector}"]`;
29
+ }
30
+
31
+ export class Menu {
32
+ private _state: MenuState = {
33
+ count: 0,
34
+ open: false,
35
+ selectedIndex: -1,
36
+ setup: false
37
+ };
38
+ private get state(): MenuState {
39
+ return this._state;
40
+ }
41
+ private set state(value: MenuState) {
42
+ this._state = value;
43
+ this.update();
44
+ this.input.onChange(this._state);
45
+ }
46
+
47
+ constructor(private input: Input) {
48
+ input.onChange(this.state);
49
+ }
50
+
51
+ private setState(
52
+ nextState:
53
+ | Partial<MenuState>
54
+ | ((state: MenuState) => Partial<MenuState>)
55
+ ) {
56
+ const { state } = this;
57
+ this.state = {
58
+ ...state,
59
+ ...(typeof nextState === "function" ? nextState(state) : nextState)
60
+ };
61
+ }
62
+
63
+ renderedCallback() {
64
+ if (!this.state.setup) {
65
+ const $trigger = this.getElement(SELECTOR_TRIGGER)!;
66
+ const $menu = this.getElement(SELECTOR_MENU)!;
67
+ $trigger.addEventListener("click", this.onTriggerClick);
68
+ $trigger.addEventListener("keydown", this.onTriggerKeyDown);
69
+ $menu.addEventListener("blur", this.onMenuBlur);
70
+ $menu.addEventListener("click", this.onMenuClick);
71
+ $menu.addEventListener("keydown", this.onMenuKeyDown);
72
+ this._state = { ...this.state, setup: true };
73
+ }
74
+ // Bypass setState to avoid an infinite loop (setState calls onChange)
75
+ this._state = {
76
+ ...this.state,
77
+ count: this.input.shadowRoot.querySelectorAll(
78
+ getSelector(SELECTOR_ITEM)
79
+ ).length
80
+ };
81
+ this.update();
82
+ }
83
+
84
+ disconnectedCallback() {
85
+ const $trigger = this.getElement(SELECTOR_TRIGGER)!;
86
+ const $menu = this.getElement(SELECTOR_MENU)!;
87
+ $trigger.removeEventListener("click", this.onTriggerClick);
88
+ $trigger.removeEventListener("keydown", this.onTriggerKeyDown);
89
+ $menu.removeEventListener("blur", this.onMenuBlur);
90
+ $menu.removeEventListener("click", this.onMenuClick);
91
+ $menu.removeEventListener("keydown", this.onMenuKeyDown);
92
+ }
93
+
94
+ open() {
95
+ this.setState({
96
+ open: true,
97
+ selectedIndex: 0
98
+ });
99
+ if (this.state.setup) {
100
+ this.getElement(SELECTOR_MENU)!.focus();
101
+ }
102
+ }
103
+
104
+ close() {
105
+ this.setState({ open: false });
106
+ }
107
+
108
+ private getElement(selector: Selector) {
109
+ return this.input.shadowRoot.querySelector(getSelector(selector));
110
+ }
111
+
112
+ private onTriggerClick = () => {
113
+ if (this.state.open) {
114
+ this.close();
115
+ } else {
116
+ this.open();
117
+ }
118
+ };
119
+
120
+ private onTriggerKeyDown = (e: KeyboardEvent) => {
121
+ switch (e.key) {
122
+ case "Enter":
123
+ case "Space":
124
+ case "ArrowDown":
125
+ this.open();
126
+ break;
127
+ default:
128
+ break;
129
+ }
130
+ };
131
+
132
+ private onMenuClick = () => {};
133
+
134
+ private onMenuBlur = () => {
135
+ setTimeout(() => {
136
+ const $active = this.input.shadowRoot.activeElement;
137
+ const $menu = this.getElement(SELECTOR_MENU)!;
138
+ if ($menu === $active || $menu.contains($active)) {
139
+ return;
140
+ }
141
+ this.setState({ open: false });
142
+ });
143
+ };
144
+
145
+ private onMenuKeyDown = (e: KeyboardEvent) => {
146
+ switch (e.key) {
147
+ case "ArrowUp":
148
+ this.setState((state) => {
149
+ let { selectedIndex } = state;
150
+ selectedIndex--;
151
+ if (selectedIndex < 0) {
152
+ selectedIndex = state.count - 1;
153
+ }
154
+ return { selectedIndex };
155
+ });
156
+ break;
157
+ case "ArrowDown":
158
+ this.setState((state) => {
159
+ let { selectedIndex } = state;
160
+ selectedIndex++;
161
+ if (selectedIndex === state.count) {
162
+ selectedIndex = 0;
163
+ }
164
+ return { selectedIndex };
165
+ });
166
+ break;
167
+ case "Enter":
168
+ if (!this.state.open) {
169
+ break;
170
+ }
171
+ this.close();
172
+ this.select();
173
+ break;
174
+ case "Escape":
175
+ case "Esc": {
176
+ this.close();
177
+ /* let [element] = getFocusableElements(
178
+ this.getElement(SELECTOR_TRIGGER)!
179
+ );
180
+ if (element) element.focus(); */
181
+ break;
182
+ }
183
+ default:
184
+ break;
185
+ }
186
+ };
187
+
188
+ private select() {
189
+ const element = this.input.shadowRoot.querySelectorAll(
190
+ getSelector(SELECTOR_ITEM)
191
+ )[this.state.selectedIndex] as HTMLElement | undefined;
192
+ if (element) {
193
+ element.click();
194
+ }
195
+ }
196
+
197
+ private setTriggerAttributes() {
198
+ if (!this.state.setup) {
199
+ return;
200
+ }
201
+ const $trigger = this.getElement(SELECTOR_TRIGGER)!;
202
+ const $menu = this.getElement(SELECTOR_MENU)!;
203
+ $trigger.setAttribute("aria-controls", $menu.getAttribute("id") || "");
204
+ $trigger.setAttribute("aria-expanded", String(this.state.open));
205
+ $trigger.setAttribute("aria-haspopup", "true");
206
+ $trigger.setAttribute("role", "button");
207
+ }
208
+
209
+ private setMenuAttributes() {
210
+ if (!this.state.setup) {
211
+ return;
212
+ }
213
+
214
+ const $menu = this.getElement(SELECTOR_MENU)!;
215
+ const $activeDecendant = this.input.shadowRoot.querySelectorAll(
216
+ getSelector(SELECTOR_ITEM)
217
+ )[this.state.selectedIndex] as HTMLElement | undefined;
218
+ $menu.setAttribute(
219
+ "aria-activedescendant",
220
+ $activeDecendant ? $activeDecendant.getAttribute("id") || "" : ""
221
+ );
222
+ $menu.setAttribute("aria-hidden", String(!this.state.open));
223
+ $menu.style.zIndex = this.state.open ? "100" : "";
224
+ $menu.style.display = this.state.open ? "block" : "none";
225
+ if (this.state.open) {
226
+ $menu.setAttribute("tabindex", "0");
227
+ } else {
228
+ $menu.removeAttribute("tabindex");
229
+ }
230
+ this.input.shadowRoot.querySelectorAll("ui,li").forEach((node) => {
231
+ const element = node as HTMLElement;
232
+ element.setAttribute("role", "presentation");
233
+ });
234
+ this.input.shadowRoot
235
+ .querySelectorAll(getSelector(SELECTOR_ITEM))
236
+ .forEach((node) => {
237
+ const element = node as HTMLElement;
238
+ element.setAttribute("tabindex", "-1");
239
+ });
240
+ }
241
+
242
+ private setMenuItemsAttributes() {
243
+ this.input.shadowRoot
244
+ .querySelectorAll(getSelector(SELECTOR_ITEM))
245
+ .forEach((node, i) => {
246
+ const element = node as HTMLElement;
247
+ element.setAttribute(
248
+ "data-active",
249
+ String(i === this.state.selectedIndex)
250
+ );
251
+ element.setAttribute("role", "menuitem");
252
+ });
253
+ }
254
+
255
+ private update() {
256
+ this.setTriggerAttributes();
257
+ this.setMenuAttributes();
258
+ this.setMenuItemsAttributes();
259
+ }
260
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * @file Utilities for dealing with element overflows (execeeding container width)
3
+ * Based on https://sfdc.co/uOYl6
4
+ */
5
+
6
+ export type CalculateOverflowItem = {
7
+ active: boolean;
8
+ value?: string;
9
+ width: number;
10
+ };
11
+
12
+ export function calculateOverflow(
13
+ items: CalculateOverflowItem[],
14
+ containerWidth: number = 0,
15
+ overflowWidth: number = 0
16
+ ) {
17
+ const visibleItems: CalculateOverflowItem[] = [];
18
+ const overflowItems: CalculateOverflowItem[] = [];
19
+
20
+ const allItemsWidth = items.reduce(
21
+ (totalWidth, item) => totalWidth + item.width,
22
+ 0
23
+ );
24
+
25
+ // If total items width is less than containerwidth or if the containerwidth is
26
+ // less than zero in cases where container is not yet rendered and we subtract the threshold
27
+ // return all items as visibleItems and overflowItems empty
28
+ if (allItemsWidth <= containerWidth || containerWidth <= 0) {
29
+ return { visibleItems: items, overflowItems };
30
+ }
31
+
32
+ // Not all items fit, an overflow is needed
33
+ let totalWidth = overflowWidth;
34
+
35
+ // Find the active item if it exists
36
+ const activeItem = items.find((i) => i.active);
37
+
38
+ // The active item is always shown, so reserve space for it
39
+ if (activeItem) {
40
+ totalWidth += activeItem.width;
41
+ }
42
+
43
+ let activeItemFitsWithoutRearrangement = false;
44
+
45
+ items.forEach((item) => {
46
+ if (item.active) {
47
+ activeItemFitsWithoutRearrangement = overflowItems.length === 0;
48
+ if (activeItemFitsWithoutRearrangement) {
49
+ visibleItems.push(item);
50
+ }
51
+ } else {
52
+ const itemFits = item.width + totalWidth <= containerWidth;
53
+ if (itemFits && overflowItems.length === 0) {
54
+ totalWidth += item.width;
55
+ visibleItems.push(item);
56
+ } else {
57
+ overflowItems.push(item);
58
+ }
59
+ }
60
+ });
61
+
62
+ // Place the active item at the end
63
+ if (activeItem && !activeItemFitsWithoutRearrangement) {
64
+ visibleItems.push(activeItem);
65
+ }
66
+
67
+ return {
68
+ visibleItems,
69
+ overflowItems
70
+ };
71
+ }
@@ -0,0 +1,3 @@
1
+ :host {
2
+ display: block;
3
+ }
@@ -0,0 +1,3 @@
1
+ <template>
2
+ <slot></slot>
3
+ </template>
@@ -0,0 +1,19 @@
1
+ import { html } from "lit-html";
2
+
3
+ export default {
4
+ title: "arch/arch-page",
5
+ component: "sb-arch-page"
6
+ };
7
+
8
+ // prettier-ignore
9
+ const createStyles = () => html `
10
+ <style>
11
+ sb-arch-page {
12
+ display: block;
13
+ }
14
+ </style>`;
15
+
16
+ // prettier-ignore
17
+ export const Base = () => html` ${createStyles()}
18
+ <sb-arch-page> Hello </sb-arch-page>
19
+ `;
@@ -0,0 +1,3 @@
1
+ import { LightningElement } from 'lwc';
2
+
3
+ export default class extends LightningElement {}