@muonic/muon 0.0.2-beta.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 (124) hide show
  1. package/.nycrc +17 -0
  2. package/.versionrc +3 -0
  3. package/CHANGELOG.md +389 -0
  4. package/components/card/index.js +1 -0
  5. package/components/card/src/card-component.js +43 -0
  6. package/components/card/src/card-styles.css +25 -0
  7. package/components/card/src/config-tokens.json +11 -0
  8. package/components/card/src/design-tokens.json +34 -0
  9. package/components/card/story.js +52 -0
  10. package/components/cta/index.js +1 -0
  11. package/components/cta/src/config-tokens.json +11 -0
  12. package/components/cta/src/cta-component.js +174 -0
  13. package/components/cta/src/cta-styles.css +105 -0
  14. package/components/cta/src/design-tokens.json +132 -0
  15. package/components/cta/story.js +99 -0
  16. package/components/detail/index.js +1 -0
  17. package/components/detail/src/config-tokens.json +11 -0
  18. package/components/detail/src/design-tokens.json +102 -0
  19. package/components/detail/src/detail-component.js +27 -0
  20. package/components/detail/src/detail-styles.css +83 -0
  21. package/components/detail/story.js +33 -0
  22. package/components/form/index.js +1 -0
  23. package/components/form/src/config-tokens.json +11 -0
  24. package/components/form/src/design-tokens.json +9 -0
  25. package/components/form/src/form-component.js +197 -0
  26. package/components/form/src/form-styles.css +10 -0
  27. package/components/form/story.js +71 -0
  28. package/components/icon/index.js +1 -0
  29. package/components/icon/src/config-tokens.json +31 -0
  30. package/components/icon/src/design-tokens.json +8 -0
  31. package/components/icon/src/icon-component.js +91 -0
  32. package/components/icon/src/icon-styles.css +26 -0
  33. package/components/icon/story.js +26 -0
  34. package/components/image/index.js +1 -0
  35. package/components/image/src/config-tokens.json +26 -0
  36. package/components/image/src/image-component.js +96 -0
  37. package/components/image/src/image-styles.css +71 -0
  38. package/components/image/story.js +31 -0
  39. package/components/inputter/index.js +1 -0
  40. package/components/inputter/src/config-tokens.json +14 -0
  41. package/components/inputter/src/design-tokens.json +308 -0
  42. package/components/inputter/src/inputter-component.js +227 -0
  43. package/components/inputter/src/inputter-styles-detail.css +59 -0
  44. package/components/inputter/src/inputter-styles.css +305 -0
  45. package/components/inputter/src/inputter-styles.slotted.css +64 -0
  46. package/components/inputter/story.js +243 -0
  47. package/css/accessibility.css +3 -0
  48. package/css/default.css +9 -0
  49. package/css/global.css +8 -0
  50. package/directives/image-loader-directive.js +116 -0
  51. package/directives/svg-loader-directive.js +94 -0
  52. package/index.js +52 -0
  53. package/mixins/card-mixin.js +27 -0
  54. package/mixins/detail-mixin.js +128 -0
  55. package/mixins/form-associate-mixin.js +36 -0
  56. package/mixins/form-element-mixin.js +378 -0
  57. package/mixins/image-holder-mixin.js +20 -0
  58. package/mixins/mask-mixin.js +159 -0
  59. package/mixins/validation-mixin.js +272 -0
  60. package/muon-element/index.js +97 -0
  61. package/package.json +72 -0
  62. package/rollup.config.mjs +30 -0
  63. package/scripts/build/storybook/index.mjs +11 -0
  64. package/scripts/build/storybook/run.mjs +47 -0
  65. package/scripts/rollup-plugins.mjs +116 -0
  66. package/scripts/serve/index.mjs +11 -0
  67. package/scripts/serve/run.mjs +27 -0
  68. package/scripts/style-dictionary.mjs +64 -0
  69. package/scripts/utils/config.mjs +30 -0
  70. package/scripts/utils/index.mjs +283 -0
  71. package/storybook/find-stories.js +36 -0
  72. package/storybook/server.config.mjs +19 -0
  73. package/storybook/stories.js +86 -0
  74. package/storybook/tokens/color.js +87 -0
  75. package/storybook/tokens/font.js +52 -0
  76. package/storybook/tokens/spacer.js +48 -0
  77. package/tests/README.md +3 -0
  78. package/tests/components/card/__snapshots__/card.test.snap.js +70 -0
  79. package/tests/components/card/card.test.js +81 -0
  80. package/tests/components/cta/__snapshots__/cta.test.snap.js +246 -0
  81. package/tests/components/cta/cta.test.js +212 -0
  82. package/tests/components/form/__snapshots__/form.test.snap.js +115 -0
  83. package/tests/components/form/form.test.js +336 -0
  84. package/tests/components/icon/__snapshots__/icon.test.snap.js +95 -0
  85. package/tests/components/icon/icon.test.js +197 -0
  86. package/tests/components/image/__snapshots__/image.test.snap.js +205 -0
  87. package/tests/components/image/image.test.js +314 -0
  88. package/tests/components/image/images/15.png +0 -0
  89. package/tests/components/image/images/150.png +0 -0
  90. package/tests/components/inputter/__snapshots__/inputter.test.snap.js +357 -0
  91. package/tests/components/inputter/inputter.test.js +427 -0
  92. package/tests/helpers/index.js +30 -0
  93. package/tests/mixins/__snapshots__/card.test.snap.js +35 -0
  94. package/tests/mixins/__snapshots__/detail.test.snap.js +237 -0
  95. package/tests/mixins/__snapshots__/form-element.test.snap.js +137 -0
  96. package/tests/mixins/__snapshots__/mask.test.snap.js +53 -0
  97. package/tests/mixins/__snapshots__/validation.test.snap.js +297 -0
  98. package/tests/mixins/card.test.js +63 -0
  99. package/tests/mixins/detail.test.js +223 -0
  100. package/tests/mixins/form-element.test.js +473 -0
  101. package/tests/mixins/mask.test.js +261 -0
  102. package/tests/mixins/muon-element.test.js +52 -0
  103. package/tests/mixins/validation.test.js +423 -0
  104. package/tests/runner/commands.mjs +19 -0
  105. package/tests/scripts/utils/card-component.js +26 -0
  106. package/tests/scripts/utils/muon.config.test.json +13 -0
  107. package/tests/scripts/utils/single.component.config.json +5 -0
  108. package/tests/scripts/utils/test-runner.mjs +1 -0
  109. package/tests/scripts/utils/utils-test.mjs +284 -0
  110. package/tests/utils/validation.functions.test.js +199 -0
  111. package/tokens/theme/color.json +482 -0
  112. package/tokens/theme/font.json +61 -0
  113. package/tokens/theme/size.json +27 -0
  114. package/tokens/theme/spacer.json +73 -0
  115. package/tokens/utils/formats/reference.js +17 -0
  116. package/tokens/utils/modular-scale.js +33 -0
  117. package/tokens/utils/templates/font-face.css.template +30 -0
  118. package/tokens/utils/transforms/color.js +27 -0
  119. package/tokens/utils/transforms/string.js +6 -0
  120. package/tokens/utils/validation.json +76 -0
  121. package/utils/scroll/index.js +31 -0
  122. package/utils/validation/index.js +205 -0
  123. package/web-test-runner.browserstack.config.mjs +123 -0
  124. package/web-test-runner.config.mjs +44 -0
@@ -0,0 +1,243 @@
1
+ import { Inputter } from '@muonic/muon/components/inputter';
2
+ import setup from '@muonic/muon/storybook/stories';
3
+ import customValidation from '@muon/utils/validation/index.js';
4
+ import { staticHTML, unsafeStatic } from '@muonic/muon';
5
+
6
+ const details = setup('inputter', Inputter);
7
+
8
+ export default {
9
+ ...details.defaultValues,
10
+ parameters: {
11
+ controls: {
12
+ exclude: ['standardTemplate']
13
+ }
14
+ },
15
+ argTypes: {
16
+ ...details.defaultValues.argTypes,
17
+ validation: {
18
+ control: {
19
+ type: 'multi-select',
20
+ options: [...Object.keys(customValidation)]
21
+ }
22
+ }
23
+ }
24
+ };
25
+
26
+ const autoCompleteTemplate = (args) => args.autocomplete ? unsafeStatic(`autocomplete="${args.autocomplete}"`) : '';
27
+
28
+ const placeHolderTemplate = (args) => args.placeholder ? unsafeStatic(`placeholder="${args.placeholder}"`) : '';
29
+
30
+ const disabledTemplate = (args) => args.disabled ? unsafeStatic(`disabled`) : '';
31
+
32
+ const labelTemplate = (args) => staticHTML`
33
+ <label slot="label">${args.label}</label>`;
34
+
35
+ const tipDetailsTemplate = (args) => staticHTML`
36
+ <div slot="tip-details">${args.tip}</div>`;
37
+
38
+ const inputTemplate = (args) => staticHTML`
39
+ <input type="${args.inputtype}" ${placeHolderTemplate(args)} ${autoCompleteTemplate(args)} ${disabledTemplate(args)}>`;
40
+
41
+ const singleTemplate = (args) => staticHTML`
42
+ ${args.label ? labelTemplate(args) : ''}
43
+ ${args.inputtype ? inputTemplate(args) : ''}
44
+ ${args.tip ? tipDetailsTemplate(args) : ''}`;
45
+
46
+ const InputterStandardTemplate = (args) => details.template(args, singleTemplate);
47
+ export const Text = InputterStandardTemplate.bind({});
48
+ Text.args = {
49
+ inputtype: 'text',
50
+ label: 'Text',
51
+ value: '',
52
+ helper: 'Useful information to help populate this field.',
53
+ tip: '',
54
+ validation: ['isRequired'],
55
+ placeholder: 'e.g. Placeholder'
56
+ };
57
+
58
+ export const Email = InputterStandardTemplate.bind({});
59
+ Email.args = {
60
+ inputtype: 'email',
61
+ label: 'Email',
62
+ value: '',
63
+ helper: 'How can we help you?',
64
+ tip: 'By providing clarification on why this information is necessary.',
65
+ validation: ['isRequired', 'isEmail'],
66
+ placeholder: 'e.g. my@email.com',
67
+ autocomplete: 'email'
68
+ };
69
+
70
+ export const Tel = InputterStandardTemplate.bind({});
71
+ Tel.args = {
72
+ inputtype: 'tel',
73
+ label: 'Tel',
74
+ value: '',
75
+ helper: 'How can we help you?',
76
+ tip: 'By providing clarification on why this information is necessary.',
77
+ validation: ['isRequired'],
78
+ placeholder: 'e.g. 07770888444',
79
+ autocomplete: 'tel'
80
+ };
81
+
82
+ export const Search = InputterStandardTemplate.bind({});
83
+ Search.args = {
84
+ inputtype: 'search',
85
+ label: 'Search'
86
+ };
87
+
88
+ export const Password = InputterStandardTemplate.bind({});
89
+ Password.args = {
90
+ inputtype: 'password',
91
+ label: 'Password'
92
+ };
93
+
94
+ export const Disabled = InputterStandardTemplate.bind({});
95
+ Disabled.args = {
96
+ inputtype: 'text',
97
+ label: 'Disabled',
98
+ value: '',
99
+ validation: ['isRequired'],
100
+ placeholder: 'e.g. Placeholder',
101
+ disabled: true
102
+ };
103
+
104
+ export const Date = InputterStandardTemplate.bind({});
105
+ Date.args = {
106
+ inputtype: 'date',
107
+ label: 'Date',
108
+ value: '',
109
+ placeholder: '',
110
+ validation: ['isRequired', 'minDate(\'01/01/2022\')']
111
+ };
112
+
113
+ export const DateMask = InputterStandardTemplate.bind({});
114
+ DateMask.args = {
115
+ inputtype: 'text',
116
+ label: 'Date Mask',
117
+ value: '',
118
+ placeholder: '',
119
+ mask: 'dd/mm/yyyy',
120
+ separator: '/',
121
+ validation: ['isRequired', 'minDate(\'01/01/2022\')']
122
+ };
123
+
124
+ export const Mask = InputterStandardTemplate.bind({});
125
+ Mask.args = {
126
+ inputtype: 'text',
127
+ label: 'Mask',
128
+ value: '',
129
+ placeholder: '',
130
+ mask: '000000',
131
+ validation: ['isRequired']
132
+ };
133
+
134
+ export const Separator = InputterStandardTemplate.bind({});
135
+ Separator.args = {
136
+ inputtype: 'text',
137
+ label: 'Separator',
138
+ value: '',
139
+ placeholder: '',
140
+ mask: ' - - ',
141
+ separator: '-'
142
+ };
143
+
144
+ const innerNumber = (args) => staticHTML`
145
+ ${args.label ? labelTemplate(args) : ''}
146
+ <input type="${args.inputtype}" min="${args.min}" max="${args.max}">
147
+ ${args.tip ? tipDetailsTemplate(args) : ''}
148
+ `;
149
+
150
+ export const Number = (args) => details.template(args, innerNumber);
151
+ Number.args = {
152
+ inputtype: 'number',
153
+ label: 'Number',
154
+ validation: ['isRequired', 'isBetween(5,20)'],
155
+ min: 0,
156
+ max: 10
157
+ };
158
+
159
+ const innerTextarea = (args) => staticHTML`
160
+ <label slot="label">${args.label}</label>
161
+ <textarea placeholder="${args.placeholder}"></textarea>
162
+ `;
163
+
164
+ export const Textarea = (args) => details.template(args, innerTextarea);
165
+ Textarea.args = {
166
+ label: 'Textarea',
167
+ value: '',
168
+ validation: ['isRequired'],
169
+ placeholder: 'e.g. Provide information'
170
+ };
171
+
172
+ const innerMultiple = (args) => staticHTML`
173
+ <input type="${args.inputtype}" name="${args.name}" value="${args.value}" ${unsafeStatic(args.states?.join(' ') ?? '')} id="${args.id}">
174
+ <label for="${args.id}">${unsafeStatic(args.label)}</label>`;
175
+
176
+ const multiTemplate = (args) => staticHTML`
177
+ ${args.options?.map((option, i) => {
178
+ const id = `${args.inputtype}-0${i + 1}`;
179
+ return staticHTML`${innerMultiple({
180
+ ...args,
181
+ value: option.value,
182
+ label: option.label,
183
+ id,
184
+ states: option.states
185
+ })}`;
186
+ })}
187
+ ${args.tip ? tipDetailsTemplate(args) : ''}
188
+ `;
189
+
190
+ const InputterMultipleTemplate = (args) => details.template(args, multiTemplate);
191
+ export const Checkbox = InputterMultipleTemplate.bind({});
192
+ Checkbox.args = {
193
+ inputtype: 'checkbox',
194
+ heading: 'What options do you like?',
195
+ helper: 'How can we help you?',
196
+ tip: 'By providing clarification on why this information is necessary.',
197
+ validation: ['isRequired'],
198
+ name: 'checkboxes',
199
+ options: [
200
+ { label: 'Option A', value: 'a', states: ['checked'] },
201
+ { label: 'Option B', value: 'b' },
202
+ { label: 'Option C', value: 'c' },
203
+ { label: 'Option D', value: 'd', states: ['disabled'] }
204
+ ]
205
+ };
206
+
207
+ export const Radio = InputterMultipleTemplate.bind({});
208
+ Radio.args = {
209
+ inputtype: 'radio',
210
+ heading: 'Which choice would you prefer?',
211
+ helper: 'How can we help you?',
212
+ tip: 'By providing clarification on why this information is necessary.',
213
+ validation: ['isRequired'],
214
+ name: 'radiobuttons',
215
+ options: [
216
+ { label: 'Choice A', value: 'a', states: ['checked'] },
217
+ { label: 'Choice B', value: 'b' },
218
+ { label: 'Choice C', value: 'c' },
219
+ { label: 'Choice D', value: 'd', states: ['disabled'] }
220
+ ]
221
+ };
222
+
223
+ const selectTemplate = (args) => staticHTML`
224
+ <label slot="label">${args.label}</label>
225
+ <select name="${args.name}">
226
+ ${args.options?.map((option) => staticHTML`<option value="${option.value}" ${unsafeStatic(option.states?.join(' ') ?? '')}>${option.label}</option>`)}
227
+ </select>
228
+ `;
229
+
230
+ export const Select = (args) => details.template(args, selectTemplate);
231
+ Select.args = {
232
+ label: 'Select',
233
+ value: '',
234
+ validation: ['isRequired'],
235
+ name: 'select',
236
+ options: [
237
+ { label: 'Please select', value: '' },
238
+ { label: 'Value one', value: 'value-01' },
239
+ { label: 'Value two', value: 'value-02', states: ['selected'] },
240
+ { label: 'Value three', value: 'value-03' },
241
+ { label: 'Value four', value: 'value-04' }
242
+ ]
243
+ };
@@ -0,0 +1,3 @@
1
+ .sr-only {
2
+ display: none;
3
+ }
@@ -0,0 +1,9 @@
1
+ @import "accessibility.css";
2
+
3
+ :host {
4
+ font-family: $THEME_FONT_FAMILY_DEFAULT;
5
+ }
6
+
7
+ :host([hidden]) {
8
+ display: none;
9
+ }
package/css/global.css ADDED
@@ -0,0 +1,8 @@
1
+ body {
2
+ margin: 0;
3
+ font-family: $THEME_FONT_FAMILY_DEFAULT;
4
+ font-size: 16px;
5
+ overflow-x: hidden;
6
+ line-height: 1.5;
7
+ min-height: 100vh;
8
+ }
@@ -0,0 +1,116 @@
1
+ import { AsyncDirective, directive, html, until, styleMap, ifDefined } from '@muonic/muon';
2
+ export class ImageLoaderDirective extends AsyncDirective {
3
+ constructor(partInfo) {
4
+ super(partInfo);
5
+
6
+ this.src = '';
7
+ this.alt = '';
8
+ this.placeholder = '';
9
+ this.loading = '';
10
+ this.image = undefined;
11
+ }
12
+
13
+ async fetchImage() {
14
+ return new Promise((resolve, reject) => {
15
+ this.image = new Image();
16
+
17
+ this.image.src = this.src;
18
+ this.image.alt = this.alt;
19
+ this.image.classList.add('blur-out', 'image-lazy');
20
+ this.image.onload = () => resolve(this.image);
21
+ this.image.onerror = () => reject();
22
+ });
23
+ }
24
+
25
+ observer(parts, attributes) {
26
+ return new Promise((resolve) => {
27
+ const options = {
28
+ threshold: 0.01,
29
+ rootMargin: '150px'
30
+ };
31
+
32
+ const io = new IntersectionObserver((entries) => {
33
+ /* eslint-disable consistent-return */
34
+ return entries.forEach((entry) => {
35
+ if (!this.image && entry.intersectionRatio > 0) {
36
+ return resolve(this.render(attributes[0]));
37
+ }
38
+ });
39
+ }, options);
40
+
41
+ const observe = parts.parentNode;
42
+ io.observe(observe);
43
+ });
44
+ }
45
+
46
+ update(parts, attributes) {
47
+ if (attributes?.[0]?.loading === 'lazy') {
48
+ return html`${until(this.observer(parts, attributes), undefined)}`;
49
+ }
50
+
51
+ return this.render(attributes[0]);
52
+ }
53
+ }
54
+
55
+ export class ImageInlineLoaderDirective extends ImageLoaderDirective {
56
+ render({ src, alt, placeholder, loading = 'lazy' }) {
57
+ const loadingAttribute = window.chrome ? loading : undefined;
58
+
59
+ this.src = src;
60
+ this.alt = alt;
61
+ this.placeholder = placeholder;
62
+ this.loading = loading;
63
+
64
+ if (this.placeholder) {
65
+ this.setValue(html`<img class="image-lazy blur" loading="${ifDefined(loadingAttribute)}" src="${this.placeholder}" alt="" />`);
66
+ }
67
+
68
+ Promise.resolve(this.fetchImage()).then((image) => {
69
+ if (image) {
70
+ dispatchEvent(new CustomEvent('image-loaded', { bubbles: true }));
71
+ this.setValue(image);
72
+ }
73
+ }).catch(() => {
74
+ dispatchEvent(new CustomEvent('image-failed', { bubbles: true }));
75
+ console.error(`Image (${this.src}) failed to load`);
76
+ });
77
+
78
+ return undefined;
79
+ }
80
+ }
81
+
82
+ export class ImageBackgroundLoaderDirective extends ImageLoaderDirective {
83
+ render({ src, alt, placeholder, loading = 'lazy' }) {
84
+ this.src = src;
85
+ this.alt = alt;
86
+ this.placeholder = placeholder;
87
+ this.loading = loading;
88
+
89
+ const styles = {
90
+ '--background-image': `url("${this.placeholder}")`
91
+ };
92
+
93
+ if (this.placeholder) {
94
+ this.setValue(html`<div style=${styleMap(styles)} class="image-holder blur"></div>`);
95
+ }
96
+
97
+ Promise.resolve(this.fetchImage()).then((image) => {
98
+ if (image) {
99
+ const styles = {
100
+ '--background-image': `url("${this.src}")`
101
+ };
102
+
103
+ dispatchEvent(new CustomEvent('image-loaded'));
104
+ this.setValue(html`<div style=${styleMap(styles)} class="image-holder blur-out"></div>`);
105
+ }
106
+ }).catch(() => {
107
+ dispatchEvent(new CustomEvent('image-failed', { bubbles: true }));
108
+ console.error(`Image (${this.src}) failed to load`);
109
+ });
110
+
111
+ return undefined;
112
+ }
113
+ }
114
+
115
+ export const imageInlineLoader = directive(ImageInlineLoaderDirective);
116
+ export const imageBackgroundLoader = directive(ImageBackgroundLoaderDirective);
@@ -0,0 +1,94 @@
1
+ import { AsyncDirective, directive, html, unsafeSVG, until } from '@muonic/muon';
2
+
3
+ export class SVGLoaderDirective extends AsyncDirective {
4
+ constructor(partInfo) {
5
+ super(partInfo);
6
+ this.name = '';
7
+ this.category = '';
8
+ this.path = '';
9
+ }
10
+
11
+ url(path = this.path) {
12
+ return path.replace('(name)', this.name).replace('(category)', this.category);
13
+ }
14
+
15
+ async fetchSVG() {
16
+ const url = this.url();
17
+ let response;
18
+ let cache = undefined;
19
+ let cacheAvailable = false;
20
+
21
+ try {
22
+ cacheAvailable = 'caches' in self;
23
+ cache = cacheAvailable && await caches?.open('muon');
24
+ const cacheData = await cache.match(url);
25
+
26
+ response = cache && cacheData ? cacheData : undefined;
27
+ } catch (error) {
28
+ console.info('cache not available');
29
+ console.info(error);
30
+ }
31
+
32
+ if (!response) {
33
+ response = await window.fetch(url);
34
+
35
+ response = new Response(response.body, response);
36
+ response.headers.append('Cache-Control', 'max-age=100000');
37
+
38
+ if (cache && response.body) {
39
+ cache.put(url, response.clone())
40
+ .catch((error) => {
41
+ console.error(error);
42
+ });
43
+ }
44
+ }
45
+
46
+ return await response.text().then((text) => {
47
+ if (response.status === 200) {
48
+ return html`${unsafeSVG(text)}`;
49
+ }
50
+
51
+ return html``;
52
+ });
53
+ }
54
+
55
+ observer(parts, attributes) {
56
+ return new Promise((resolve) => {
57
+ const options = {
58
+ threshold: 0.01,
59
+ rootMargin: '150px'
60
+ };
61
+
62
+ const io = new IntersectionObserver((entries) => {
63
+ /* eslint-disable consistent-return */
64
+ return entries.forEach((entry) => {
65
+ if (entry.intersectionRatio > 0) {
66
+ return resolve(this.render(attributes));
67
+ }
68
+ });
69
+ }, options);
70
+
71
+ const observe = parts.parentNode;
72
+ io.observe(observe);
73
+ });
74
+ }
75
+
76
+ update(parts, attributes) {
77
+ return html`${until(this.observer(parts, attributes), undefined)}`;
78
+ }
79
+
80
+ render([{ name, category, path }]) {
81
+ this.path = path;
82
+ this.name = name;
83
+ this.category = category;
84
+
85
+ Promise.resolve(this.fetchSVG()).then((svg) => {
86
+ dispatchEvent(new CustomEvent('svg-loaded'));
87
+ this.setValue(svg);
88
+ });
89
+
90
+ return undefined;
91
+ }
92
+ }
93
+
94
+ export const svgLoader = directive(SVGLoaderDirective);
package/index.js ADDED
@@ -0,0 +1,52 @@
1
+ import {
2
+ LitElement,
3
+ html,
4
+ css,
5
+ unsafeCSS,
6
+ noChange,
7
+ adoptStyles,
8
+ supportsAdoptingStyleSheets
9
+ } from 'lit';
10
+ import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
11
+ import { classMap } from 'lit/directives/class-map.js';
12
+ import { styleMap } from 'lit/directives/style-map.js';
13
+ import { Directive, directive } from 'lit/directive.js';
14
+ import { live } from 'lit/directives/live.js';
15
+ import { ifDefined } from 'lit/directives/if-defined.js';
16
+ import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
17
+ import { cache } from 'lit/directives/cache.js';
18
+ import { AsyncDirective } from 'lit/async-directive.js';
19
+ import { ScopedElementsMixin } from '@open-wc/scoped-elements';
20
+ import { dedupeMixin } from '@open-wc/dedupe-mixin';
21
+ import { literal, html as staticHTML, unsafeStatic } from 'lit/static-html.js';
22
+ import { until } from 'lit/directives/until.js';
23
+ import { repeat } from 'lit/directives/repeat.js';
24
+ import { MuonElement } from '@muonic/muon/muon-element';
25
+
26
+ export {
27
+ MuonElement,
28
+ LitElement,
29
+ html,
30
+ css,
31
+ unsafeCSS,
32
+ unsafeHTML,
33
+ unsafeSVG,
34
+ classMap,
35
+ styleMap,
36
+ Directive,
37
+ AsyncDirective,
38
+ directive,
39
+ live,
40
+ ifDefined,
41
+ cache,
42
+ ScopedElementsMixin,
43
+ dedupeMixin,
44
+ literal,
45
+ staticHTML,
46
+ unsafeStatic,
47
+ until,
48
+ noChange,
49
+ repeat,
50
+ adoptStyles,
51
+ supportsAdoptingStyleSheets
52
+ };
@@ -0,0 +1,27 @@
1
+ import { html, dedupeMixin } from '@muonic/muon';
2
+
3
+ export const CardMixin = dedupeMixin((superClass) =>
4
+ class CardMixinClass extends superClass {
5
+
6
+ get _addHeader() {
7
+ return html`
8
+ <div class="header">
9
+ <slot name="header"></slot>
10
+ </div>`;
11
+ }
12
+
13
+ get _addContent() {
14
+ return html`
15
+ <div class="content">
16
+ <slot></slot>
17
+ </div>`;
18
+ }
19
+
20
+ get _addFooter() {
21
+ return html`
22
+ <div class="footer">
23
+ <slot name="footer"></slot>
24
+ </div>`;
25
+ }
26
+ }
27
+ );
@@ -0,0 +1,128 @@
1
+ import { html, classMap, ScopedElementsMixin, dedupeMixin } from '@muonic/muon';
2
+ import { Icon } from '@muon/components/icon';
3
+
4
+ /**.
5
+ * A mixin to hold show / hide content
6
+ *
7
+ * @mixin
8
+ */
9
+
10
+ export const DetailMixin = dedupeMixin((superClass) =>
11
+ class DetailMixinClass extends ScopedElementsMixin(superClass) {
12
+
13
+ static get properties() {
14
+ return {
15
+ open: {
16
+ type: Boolean,
17
+ reflect: true
18
+ },
19
+
20
+ icon: {
21
+ type: String
22
+ },
23
+
24
+ _toggleOpen: {
25
+ type: String,
26
+ state: true
27
+ },
28
+
29
+ _toggleClose: {
30
+ type: String,
31
+ state: true
32
+ },
33
+
34
+ _togglePosition: {
35
+ type: String,
36
+ state: true
37
+ }
38
+ };
39
+ }
40
+
41
+ static get scopedElements() {
42
+ return {
43
+ 'detail-icon': Icon
44
+ };
45
+ }
46
+
47
+ constructor() {
48
+ super();
49
+ this.open = false;
50
+ this._toggleEvent = 'detail-toggle';
51
+ }
52
+
53
+ /**
54
+ * A method to handle 'toggle' event from the html detail element.
55
+ *
56
+ * @param {Event} toggleEvent - Event to handle.
57
+ * @returns {void}
58
+ * @example
59
+ */
60
+ _onToggle(toggleEvent) {
61
+ const isOpen = !!toggleEvent.target.open;
62
+ this.open = isOpen;
63
+ this.dispatchEvent(new CustomEvent(this._toggleEvent, {
64
+ detail: {
65
+ open: isOpen
66
+ }
67
+ }));
68
+ }
69
+
70
+ get standardTemplate() {
71
+ const classes = {
72
+ details: true,
73
+ 'toggle-start': this._togglePosition === 'start',
74
+ 'toggle-end': this._togglePosition === 'end',
75
+ 'has-icon': !!this.icon
76
+ };
77
+ return html`
78
+ <details class=${classMap(classes)} ?open="${this.open}" @toggle="${this._onToggle}">
79
+ ${this._addHeading}
80
+ ${this._addContent}
81
+ </details>
82
+ `;
83
+ }
84
+
85
+ get __addIcon() {
86
+ if (this.icon) {
87
+ return html`
88
+ <detail-icon name="${this.icon}" class="icon"></detail-icon>
89
+ `;
90
+ }
91
+ return undefined;
92
+ }
93
+
94
+ get __addToggle() {
95
+ const toggle = this.open ? this._toggleClose : this._toggleOpen;
96
+ return html`<detail-icon name='${toggle}' class="toggle"></detail-icon>`;
97
+ }
98
+
99
+ /**
100
+ * A method to render the heading part.
101
+ *
102
+ * @returns {object} TemplateResult - rendering template.
103
+ */
104
+ get _addHeading() {
105
+ const isToggleStart = this._togglePosition === 'start';
106
+ return html`
107
+ <summary class="heading">
108
+ ${isToggleStart ? this.__addToggle : this.__addIcon}
109
+ <slot name="heading"></slot>
110
+ ${isToggleStart ? this.__addIcon : this.__addToggle}
111
+ </summary>
112
+ `;
113
+ }
114
+
115
+ /**
116
+ * A method to render the content part when expanded.
117
+ *
118
+ * @returns {object} TemplateResult - rendering template.
119
+ */
120
+ get _addContent() {
121
+ return html`
122
+ <div class="content">
123
+ <slot></slot>
124
+ </div>
125
+ `;
126
+ }
127
+ }
128
+ );