@govtechsg/sgds-web-component 0.0.8 → 0.0.10
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.
- package/.github/workflows/publish-latest.yml +22 -0
- package/.github/workflows/publish-pr.yml +28 -0
- package/.husky/commit-msg +4 -0
- package/.husky/prepare-commit-msg +8 -0
- package/.storybook/main.js +16 -0
- package/.storybook/preview-head.html +11 -0
- package/.storybook/preview.js +9 -0
- package/.vscode/settings.json +7 -0
- package/CONTRIBUTING.md +56 -0
- package/LICENSE +20 -0
- package/amplify.yml +22 -0
- package/commitlint.config.js +1 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/button-element.scss.html +112 -0
- package/coverage/lcov-report/button-element.ts.html +145 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +116 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov.info +32 -0
- package/index.html +430 -0
- package/{Button → lib/Button}/index.d.ts +0 -0
- package/{Button → lib/Button}/index.js +304 -39
- package/lib/Button/index.js.map +1 -0
- package/{Button → lib/Button}/package.json +0 -0
- package/lib/Button/sgds-button.d.ts +48 -0
- package/lib/Card/index.d.ts +1 -0
- package/lib/Card/index.js +6150 -0
- package/lib/Card/index.js.map +1 -0
- package/lib/Card/package.json +7 -0
- package/lib/Card/sgds-action-card.d.ts +20 -0
- package/lib/Checkbox/index.d.ts +1 -0
- package/lib/Checkbox/index.js +6366 -0
- package/lib/Checkbox/index.js.map +1 -0
- package/lib/Checkbox/package.json +7 -0
- package/lib/Checkbox/sgds-checkbox.d.ts +36 -0
- package/lib/Dropdown/index.d.ts +3 -0
- package/{Mainnav → lib/Dropdown}/index.js +2785 -9262
- package/lib/Dropdown/index.js.map +1 -0
- package/lib/Dropdown/package.json +7 -0
- package/lib/Dropdown/sgds-dropdown-item.d.ts +7 -0
- package/lib/Dropdown/sgds-dropdown.d.ts +7 -0
- package/{Footer → lib/Footer}/index.d.ts +0 -0
- package/{Footer → lib/Footer}/index.js +111 -95
- package/lib/Footer/index.js.map +1 -0
- package/{Footer → lib/Footer}/package.json +0 -0
- package/{Footer → lib/Footer}/sgds-footer.d.ts +2 -2
- package/lib/Input/index.d.ts +1 -0
- package/lib/Input/index.js +6656 -0
- package/lib/Input/index.js.map +1 -0
- package/lib/Input/package.json +7 -0
- package/lib/Input/sgds-input.d.ts +42 -0
- package/{Mainnav → lib/Mainnav}/index.d.ts +1 -0
- package/{index.js → lib/Mainnav/index.js} +3870 -23414
- package/lib/Mainnav/index.js.map +1 -0
- package/{Mainnav → lib/Mainnav}/package.json +0 -0
- package/lib/Mainnav/sgds-mainnav-dropdown.d.ts +5 -0
- package/lib/Mainnav/sgds-mainnav-item.d.ts +4 -0
- package/{Mainnav → lib/Mainnav}/sgds-mainnav.d.ts +2 -2
- package/{Masthead → lib/Masthead}/index.d.ts +0 -0
- package/{Masthead → lib/Masthead}/index.js +140 -114
- package/lib/Masthead/index.js.map +1 -0
- package/{Masthead → lib/Masthead}/package.json +0 -0
- package/{Masthead → lib/Masthead}/sgds-masthead.d.ts +1 -1
- package/lib/Modal/index.d.ts +1 -0
- package/lib/Modal/index.js +6432 -0
- package/lib/Modal/index.js.map +1 -0
- package/lib/Modal/package.json +7 -0
- package/lib/Modal/sgds-modal.d.ts +28 -0
- package/lib/QuantityToggle/index.d.ts +1 -0
- package/lib/QuantityToggle/index.js +7049 -0
- package/lib/QuantityToggle/index.js.map +1 -0
- package/lib/QuantityToggle/package.json +7 -0
- package/lib/QuantityToggle/sgds-quantitytoggle.d.ts +30 -0
- package/lib/Radio/index.d.ts +2 -0
- package/lib/Radio/index.js +12607 -0
- package/lib/Radio/index.js.map +1 -0
- package/lib/Radio/package.json +7 -0
- package/lib/Radio/sgds-radio.d.ts +31 -0
- package/lib/Radio/sgds-radiogroup.d.ts +41 -0
- package/{Sidenav → lib/Sidenav}/index.d.ts +0 -0
- package/{Sidenav → lib/Sidenav}/index.js +2266 -2171
- package/lib/Sidenav/index.js.map +1 -0
- package/{Sidenav → lib/Sidenav}/package.json +0 -0
- package/{Sidenav → lib/Sidenav}/sgds-sidenav-item.d.ts +2 -1
- package/lib/Sidenav/sgds-sidenav-link.d.ts +4 -0
- package/{Sidenav → lib/Sidenav}/sgds-sidenav.d.ts +1 -1
- package/lib/Tab/index.d.ts +3 -0
- package/lib/Tab/index.js +13557 -0
- package/lib/Tab/index.js.map +1 -0
- package/lib/Tab/package.json +7 -0
- package/lib/Tab/sgds-tab.d.ts +26 -0
- package/lib/Tab/sgds-tabgroup.d.ts +47 -0
- package/lib/Tab/sgds-tabpanel.d.ts +25 -0
- package/lib/Textarea/index.d.ts +1 -0
- package/lib/Textarea/index.js +6696 -0
- package/lib/Textarea/index.js.map +1 -0
- package/lib/Textarea/package.json +7 -0
- package/lib/Textarea/sgds-textarea.d.ts +53 -0
- package/lib/index.d.ts +16 -0
- package/lib/index.js +134580 -0
- package/lib/index.js.map +1 -0
- package/lib/umd/index.js +134587 -0
- package/lib/umd/index.js.map +1 -0
- package/lib/utils/animate.d.ts +10 -0
- package/lib/utils/animation-registry.d.ts +18 -0
- package/{utils → lib/utils}/breakpoints.d.ts +0 -0
- package/lib/utils/card-element.d.ts +11 -0
- package/lib/utils/defaultvalue.d.ts +2 -0
- package/lib/utils/dropdown-element.d.ts +37 -0
- package/lib/utils/event.d.ts +2 -0
- package/lib/utils/form.d.ts +38 -0
- package/{utils → lib/utils}/generateId.d.ts +0 -0
- package/lib/utils/link-element.d.ts +7 -0
- package/lib/utils/mergeDeep.d.ts +2 -0
- package/lib/utils/modal.d.ts +12 -0
- package/lib/utils/object.d.ts +2 -0
- package/lib/utils/offset.d.ts +4 -0
- package/lib/utils/scroll.d.ts +13 -0
- package/{utils → lib/utils}/sgds-element.d.ts +0 -0
- package/lib/utils/slot.d.ts +22 -0
- package/lib/utils/tabbable.d.ts +8 -0
- package/lib/utils/watch.d.ts +14 -0
- package/mocks/dropdown.d.ts +4 -0
- package/mocks/dropdown.ts +27 -0
- package/mocks/link.d.ts +3 -0
- package/mocks/link.ts +6 -0
- package/package.json +65 -10
- package/rollup.config.js +73 -0
- package/rollup.test.config.js +42 -0
- package/scripts/buildUtils.js +30 -0
- package/scripts/frankBuild.js +49 -0
- package/src/Button/index.ts +1 -0
- package/src/Button/sgds-button.scss +28 -0
- package/src/Button/sgds-button.ts +153 -0
- package/src/Card/index.ts +1 -0
- package/src/Card/sgds-action-card.scss +27 -0
- package/src/Card/sgds-action-card.ts +115 -0
- package/src/Checkbox/index.ts +1 -0
- package/src/Checkbox/sgds-checkbox.scss +4 -0
- package/src/Checkbox/sgds-checkbox.ts +149 -0
- package/src/Dropdown/index.ts +3 -0
- package/src/Dropdown/sgds-dropdown-item.ts +39 -0
- package/src/Dropdown/sgds-dropdown.scss +5 -0
- package/src/Dropdown/sgds-dropdown.ts +54 -0
- package/src/Footer/index.ts +3 -0
- package/src/Footer/sgds-footer.scss +5 -0
- package/src/Footer/sgds-footer.ts +121 -0
- package/src/Input/index.ts +1 -0
- package/src/Input/sgds-input.scss +20 -0
- package/src/Input/sgds-input.ts +178 -0
- package/src/Mainnav/index.ts +4 -0
- package/src/Mainnav/sgds-mainnav-dropdown.scss +13 -0
- package/src/Mainnav/sgds-mainnav-dropdown.ts +45 -0
- package/src/Mainnav/sgds-mainnav-item.scss +24 -0
- package/src/Mainnav/sgds-mainnav-item.ts +8 -0
- package/src/Mainnav/sgds-mainnav.scss +39 -0
- package/src/Mainnav/sgds-mainnav.ts +183 -0
- package/src/Masthead/index.ts +1 -0
- package/src/Masthead/sgds-masthead.scss +217 -0
- package/src/Masthead/sgds-masthead.ts +189 -0
- package/src/Modal/index.ts +1 -0
- package/src/Modal/sgds-modal.scss +128 -0
- package/src/Modal/sgds-modal.ts +309 -0
- package/src/QuantityToggle/index.ts +1 -0
- package/src/QuantityToggle/sgds-quantitytoggle.scss +10 -0
- package/src/QuantityToggle/sgds-quantitytoggle.ts +130 -0
- package/src/Radio/index.ts +2 -0
- package/src/Radio/sgds-radio.scss +5 -0
- package/src/Radio/sgds-radio.ts +120 -0
- package/src/Radio/sgds-radiogroup.scss +22 -0
- package/src/Radio/sgds-radiogroup.ts +221 -0
- package/src/Sidenav/index.ts +4 -0
- package/src/Sidenav/sgds-sidenav-item.scss +73 -0
- package/src/Sidenav/sgds-sidenav-item.ts +145 -0
- package/src/Sidenav/sgds-sidenav-link.scss +25 -0
- package/src/Sidenav/sgds-sidenav-link.ts +8 -0
- package/src/Sidenav/sgds-sidenav.scss +6 -0
- package/src/Sidenav/sgds-sidenav.ts +33 -0
- package/src/Tab/index.ts +3 -0
- package/src/Tab/sgds-tab.scss +84 -0
- package/src/Tab/sgds-tab.ts +87 -0
- package/src/Tab/sgds-tabgroup.scss +198 -0
- package/src/Tab/sgds-tabgroup.ts +295 -0
- package/src/Tab/sgds-tabpanel.scss +12 -0
- package/src/Tab/sgds-tabpanel.ts +55 -0
- package/src/Textarea/index.ts +1 -0
- package/src/Textarea/sgds-textarea.scss +23 -0
- package/src/Textarea/sgds-textarea.ts +201 -0
- package/src/index.ts +16 -0
- package/src/utils/animate.ts +69 -0
- package/src/utils/animation-registry.ts +71 -0
- package/src/utils/base.scss +14 -0
- package/src/utils/breakpoints.ts +5 -0
- package/src/utils/card-element.ts +42 -0
- package/src/utils/components.style.scss +531 -0
- package/src/utils/defaultvalue.ts +51 -0
- package/src/utils/dropdown-element.ts +244 -0
- package/src/utils/event.ts +13 -0
- package/src/utils/form.ts +183 -0
- package/src/utils/generateId.ts +4 -0
- package/src/utils/link-element.ts +34 -0
- package/src/utils/mergeDeep.ts +22 -0
- package/src/utils/modal.ts +64 -0
- package/src/utils/object.ts +2 -0
- package/src/utils/offset.ts +6 -0
- package/src/utils/scroll.ts +57 -0
- package/src/utils/sgds-element.ts +18 -0
- package/src/utils/slot.ts +102 -0
- package/src/utils/tabbable.ts +81 -0
- package/src/utils/watch.ts +62 -0
- package/stories/ActionCard.stories.mdx +199 -0
- package/stories/Button.stories.mdx +194 -0
- package/stories/Checkbox.stories.mdx +196 -0
- package/stories/Dropdown.stories.mdx +152 -0
- package/stories/Footer.stories.mdx +261 -0
- package/stories/Input.stories.mdx +236 -0
- package/stories/MainNav.stories.mdx +169 -0
- package/stories/Masthead.stories.mdx +112 -0
- package/stories/Modal.stories.mdx +103 -0
- package/stories/QuantityToggle.stories.mdx +97 -0
- package/stories/Radio.stories.mdx +262 -0
- package/stories/Sample.stories.js +29 -0
- package/stories/Sample.stories.mdx +33 -0
- package/stories/SideNav.stories.mdx +245 -0
- package/stories/common.js +185 -0
- package/stories/textarea.stories.mdx +253 -0
- package/test/button.element.test.ts +185 -0
- package/test/checkbox.test.ts +240 -0
- package/test/dropdown.test.ts +637 -0
- package/test/footer.test.ts +181 -0
- package/test/generateId.test.ts +18 -0
- package/test/input.element.test.ts +316 -0
- package/test/link-element.test.ts +38 -0
- package/test/mainnav.test.ts +313 -0
- package/test/masthead.test.ts +116 -0
- package/test/modal.test.ts +149 -0
- package/test/quantitytoggle.test.ts +76 -0
- package/test/radio.test.ts +310 -0
- package/test/selectable-card.test.ts +159 -0
- package/test/sidenav.test.ts +390 -0
- package/test/tab.test.ts +76 -0
- package/test/textarea.test.ts +126 -0
- package/tsconfig.json +26 -0
- package/tsconfig.test.json +24 -0
- package/typings/scss.d.ts +5 -0
- package/web-dev-server.config.mjs +7 -0
- package/web-test-runner.config.mjs +47 -0
- package/Button/index.js.map +0 -1
- package/Button/sgds-button.d.ts +0 -23
- package/Footer/index.js.map +0 -1
- package/Mainnav/index.js.map +0 -1
- package/Mainnav/sgds-mainnav-item.d.ts +0 -7
- package/Masthead/index.js.map +0 -1
- package/Sidenav/index.js.map +0 -1
- package/Sidenav/sgds-sidenav-link.d.ts +0 -7
- package/index.d.ts +0 -5
- package/index.js.map +0 -1
- package/umd/index.js +0 -52097
- package/umd/index.js.map +0 -1
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { customElement, property,query, state } from "lit/decorators.js";
|
|
2
|
+
import { html } from 'lit/static-html.js';
|
|
3
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
4
|
+
import {classMap} from 'lit/directives/class-map.js';
|
|
5
|
+
import { live } from 'lit/directives/live.js';
|
|
6
|
+
import styles from "./sgds-textarea.scss";
|
|
7
|
+
import SgdsElement from "../utils/sgds-element";
|
|
8
|
+
import { defaultValue } from "../utils/defaultvalue";
|
|
9
|
+
import { FormSubmitController } from "../utils/form";
|
|
10
|
+
import genId from "../utils/generateId";
|
|
11
|
+
import { watch } from "../utils/watch";
|
|
12
|
+
|
|
13
|
+
@customElement("sgds-textarea")
|
|
14
|
+
export class SgdsTextArea extends SgdsElement {
|
|
15
|
+
static styles = styles;
|
|
16
|
+
|
|
17
|
+
@query('textarea.form-control') textarea: HTMLTextAreaElement;
|
|
18
|
+
@state() private hasFocus = false;
|
|
19
|
+
|
|
20
|
+
private readonly formSubmitController = new FormSubmitController(this);
|
|
21
|
+
private resizeObserver: ResizeObserver;
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@property({ type: String, reflect: true }) label = "label";
|
|
25
|
+
@property({ type: String, reflect: true }) textareaId = genId("textarea","input");
|
|
26
|
+
@property({ type: String, reflect: true }) name;
|
|
27
|
+
@property({ type: String, reflect: true }) textareaClasses?;
|
|
28
|
+
@property({ type: String, reflect: true }) value = '';
|
|
29
|
+
@property({ type: String, reflect: true}) minlength;
|
|
30
|
+
@property({ type: String, reflect: true}) maxlength;
|
|
31
|
+
@property({ type: Boolean, reflect: true}) spellcheck = false;
|
|
32
|
+
/** The number of rows to display by default. */
|
|
33
|
+
@property({ type: Number }) rows = 4;
|
|
34
|
+
@property({ type: String, reflect: true }) placeholder = "Placeholder";
|
|
35
|
+
@property({ type: String, reflect: true }) invalidFeedback = "default feedback";
|
|
36
|
+
@property({ type: Boolean, reflect: true }) autofocus = false;
|
|
37
|
+
@property({ type: Boolean, reflect: true }) disabled = false;
|
|
38
|
+
@property({ type: Boolean, reflect: true }) required = false;
|
|
39
|
+
/** Makes the input readonly. */
|
|
40
|
+
@property({ type: Boolean, reflect: true }) readonly = false;
|
|
41
|
+
@property({ type: Boolean, reflect: true }) invalid = false;
|
|
42
|
+
@property({ type: Boolean, reflect: true }) valid = false;
|
|
43
|
+
/** Controls how the textarea can be resized. */
|
|
44
|
+
@property() resize: 'none' | 'vertical' | 'auto' = 'vertical';
|
|
45
|
+
/** The textarea's inputmode attribute. */
|
|
46
|
+
@property() inputmode: 'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url';
|
|
47
|
+
/* The textarea's autocorrect attribute. */
|
|
48
|
+
@property() autocorrect: string;
|
|
49
|
+
/** Gets or sets the default value used to reset this element. The initial value corresponds to the one originally specified in the HTML that created this element. */
|
|
50
|
+
@defaultValue()
|
|
51
|
+
defaultValue = '';
|
|
52
|
+
|
|
53
|
+
connectedCallback() {
|
|
54
|
+
super.connectedCallback();
|
|
55
|
+
this.resizeObserver = new ResizeObserver(() => this.setTextareaHeight());
|
|
56
|
+
|
|
57
|
+
this.updateComplete.then(() => {
|
|
58
|
+
this.setTextareaHeight();
|
|
59
|
+
this.resizeObserver.observe(this.textarea);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
disconnectedCallback() {
|
|
64
|
+
super.disconnectedCallback();
|
|
65
|
+
this.resizeObserver.unobserve(this.textarea);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Sets focus on the textarea. */
|
|
69
|
+
focus(options?: FocusOptions) {
|
|
70
|
+
this.textarea.focus(options);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Checks for validity and shows the browser's validation message if the control is invalid. */
|
|
74
|
+
reportValidity() {
|
|
75
|
+
return this.textarea.reportValidity();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
handleInvalid() {
|
|
79
|
+
this.invalid = true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
handleChange(event: string){
|
|
83
|
+
this.value = this.textarea.value;
|
|
84
|
+
this.emit(event);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
handleFocus() {
|
|
88
|
+
this.hasFocus = true;
|
|
89
|
+
this.emit('sgds-focus');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
handleBlur() {
|
|
93
|
+
this.hasFocus = false;
|
|
94
|
+
this.emit('sgds-blur');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Selects all the text in the textarea. */
|
|
98
|
+
select() {
|
|
99
|
+
this.textarea.select();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
handleKeyDown(event: KeyboardEvent) {
|
|
103
|
+
const hasModifier = event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
|
|
104
|
+
|
|
105
|
+
// Pressing enter when focused on an input should submit the form like a native input, but we wait a tick before
|
|
106
|
+
// submitting to allow users to cancel the keydown event if they need to
|
|
107
|
+
if (event.key === 'Enter' && !hasModifier) {
|
|
108
|
+
setTimeout(() => {
|
|
109
|
+
if (!event.defaultPrevented) {
|
|
110
|
+
this.formSubmitController.submit();
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@watch('rows', { waitUntilFirstUpdate: true })
|
|
117
|
+
handleRowsChange() {
|
|
118
|
+
this.setTextareaHeight();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
setTextareaHeight() {
|
|
122
|
+
if (this.resize === 'auto') {
|
|
123
|
+
this.textarea.style.height = 'auto';
|
|
124
|
+
this.textarea.style.height = `${this.textarea.scrollHeight}px`;
|
|
125
|
+
} else {
|
|
126
|
+
(this.textarea.style.height as string | undefined) = undefined;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@watch('disabled', { waitUntilFirstUpdate: true })
|
|
131
|
+
handleDisabledChange() {
|
|
132
|
+
// Disabled form controls are always valid, so we need to recheck validity when the state changes
|
|
133
|
+
this.textarea.disabled = this.disabled;
|
|
134
|
+
this.invalid = !this.textarea.checkValidity();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@watch('value', { waitUntilFirstUpdate: true })
|
|
138
|
+
handleValueChange() {
|
|
139
|
+
this.invalid = !this.textarea.checkValidity();
|
|
140
|
+
this.valid = this.textarea.checkValidity()
|
|
141
|
+
this.updateComplete.then(() => this.setTextareaHeight());
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
render() {
|
|
145
|
+
|
|
146
|
+
// if maxlength is defined
|
|
147
|
+
const wordCount = html`
|
|
148
|
+
<div class="form-text">${this.value.length}/${this.maxlength}</div>
|
|
149
|
+
`
|
|
150
|
+
|
|
151
|
+
return html`
|
|
152
|
+
|
|
153
|
+
<div class="text-area-label-wrapper d-flex justify-content-between">
|
|
154
|
+
<label for=${ifDefined(this.textareaId)} class="form-label">${this.label}</label>
|
|
155
|
+
${parseInt(this.maxlength) > 0 ? wordCount : undefined}
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<textarea
|
|
159
|
+
class="${classMap(
|
|
160
|
+
{
|
|
161
|
+
'form-control': true,
|
|
162
|
+
'is-invalid' : this.invalid,
|
|
163
|
+
'is-valid' : this.valid,
|
|
164
|
+
'textarea-resize-none': this.resize === 'none',
|
|
165
|
+
'textarea-resize-vertical': this.resize === 'vertical',
|
|
166
|
+
'textarea-resize-auto': this.resize === 'auto',
|
|
167
|
+
[`${this.textareaClasses}`]: this.textareaClasses,
|
|
168
|
+
})}"
|
|
169
|
+
id=${ifDefined(this.textareaId)}
|
|
170
|
+
name=${ifDefined(this.name)}
|
|
171
|
+
rows=${ifDefined(this.rows)}
|
|
172
|
+
placeholder=${ifDefined(this.placeholder)}
|
|
173
|
+
minlength=${ifDefined(this.minlength)}
|
|
174
|
+
maxlength=${ifDefined(this.maxlength)}
|
|
175
|
+
.value=${live(this.value)}
|
|
176
|
+
aria-invalid=${this.invalid ? 'true' : 'false'}
|
|
177
|
+
spellcheck=${ifDefined(this.spellcheck)}
|
|
178
|
+
?disabled=${this.disabled}
|
|
179
|
+
?readonly=${this.readonly}
|
|
180
|
+
?required=${this.required}
|
|
181
|
+
?autofocus=${this.autofocus}
|
|
182
|
+
autocorrect=${ifDefined(this.autocorrect)}
|
|
183
|
+
inputmode=${ifDefined(this.inputmode)}
|
|
184
|
+
@keyup=${this.handleValueChange}
|
|
185
|
+
@input=${()=> this.handleChange('sgds-input')}
|
|
186
|
+
@change=${()=> this.handleChange('sgds-change')}
|
|
187
|
+
@invalid=${this.handleInvalid}
|
|
188
|
+
@focus=${this.handleFocus}
|
|
189
|
+
@blur=${this.handleBlur}
|
|
190
|
+
>
|
|
191
|
+
</textarea>
|
|
192
|
+
|
|
193
|
+
<div id="${this.textareaId}-invalid" class="invalid-feedback">${this.invalidFeedback}</div>
|
|
194
|
+
|
|
195
|
+
`;
|
|
196
|
+
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export default SgdsTextArea;
|
|
201
|
+
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import "./Sidenav";
|
|
2
|
+
import "./Radio";
|
|
3
|
+
import "./Masthead";
|
|
4
|
+
import "./Button";
|
|
5
|
+
import "./Footer";
|
|
6
|
+
import "./Mainnav";
|
|
7
|
+
import "./Input";
|
|
8
|
+
import "./Checkbox";
|
|
9
|
+
import "./Dropdown";
|
|
10
|
+
import "./Input";
|
|
11
|
+
import "./Textarea";
|
|
12
|
+
import "./Modal";
|
|
13
|
+
import './QuantityToggle';
|
|
14
|
+
import "./Textarea";
|
|
15
|
+
import "./Tab";
|
|
16
|
+
import "./Card";
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Animates an element using keyframes. Returns a promise that resolves after the animation completes or gets canceled.
|
|
3
|
+
//
|
|
4
|
+
export function animateTo(el: HTMLElement, keyframes: Keyframe[], options?: KeyframeAnimationOptions) {
|
|
5
|
+
return new Promise(resolve => {
|
|
6
|
+
if (options?.duration === Infinity) {
|
|
7
|
+
throw new Error('Promise-based animations must be finite.');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const animation = el.animate(keyframes, {
|
|
11
|
+
...options,
|
|
12
|
+
duration: prefersReducedMotion() ? 0 : options!.duration
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
animation.addEventListener('cancel', resolve, { once: true });
|
|
16
|
+
animation.addEventListener('finish', resolve, { once: true });
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
//
|
|
21
|
+
// Parses a CSS duration and returns the number of milliseconds.
|
|
22
|
+
//
|
|
23
|
+
export function parseDuration(delay: number | string) {
|
|
24
|
+
delay = delay.toString().toLowerCase();
|
|
25
|
+
|
|
26
|
+
if (delay.indexOf('ms') > -1) {
|
|
27
|
+
return parseFloat(delay);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (delay.indexOf('s') > -1) {
|
|
31
|
+
return parseFloat(delay) * 1000;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return parseFloat(delay);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
//
|
|
38
|
+
// Tells if the user has enabled the "reduced motion" setting in their browser or OS.
|
|
39
|
+
//
|
|
40
|
+
export function prefersReducedMotion() {
|
|
41
|
+
const query = window.matchMedia('(prefers-reduced-motion: reduce)');
|
|
42
|
+
return query.matches;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
//
|
|
46
|
+
// Stops all active animations on the target element. Returns a promise that resolves after all animations are canceled.
|
|
47
|
+
//
|
|
48
|
+
export function stopAnimations(el: HTMLElement) {
|
|
49
|
+
return Promise.all(
|
|
50
|
+
el.getAnimations().map(animation => {
|
|
51
|
+
return new Promise(resolve => {
|
|
52
|
+
const handleAnimationEvent = requestAnimationFrame(resolve);
|
|
53
|
+
|
|
54
|
+
animation.addEventListener('cancel', () => handleAnimationEvent, { once: true });
|
|
55
|
+
animation.addEventListener('finish', () => handleAnimationEvent, { once: true });
|
|
56
|
+
animation.cancel();
|
|
57
|
+
});
|
|
58
|
+
})
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// We can't animate `height: auto`, but we can calculate the height and shim keyframes by replacing it with the
|
|
63
|
+
// element's scrollHeight before the animation.
|
|
64
|
+
export function shimKeyframesHeightAuto(keyframes: Keyframe[], calculatedHeight: number) {
|
|
65
|
+
return keyframes.map(keyframe => ({
|
|
66
|
+
...keyframe,
|
|
67
|
+
height: keyframe.height === 'auto' ? `${calculatedHeight}px` : keyframe.height
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export interface ElementAnimation {
|
|
2
|
+
keyframes: Keyframe[];
|
|
3
|
+
rtlKeyframes?: Keyframe[];
|
|
4
|
+
options?: KeyframeAnimationOptions;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface ElementAnimationMap {
|
|
8
|
+
[animationName: string]: ElementAnimation;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface GetAnimationOptions {
|
|
12
|
+
/**
|
|
13
|
+
* The component's directionality. When set to "rtl", `rtlKeyframes` will be preferred over `keyframes` where
|
|
14
|
+
* available using getAnimation().
|
|
15
|
+
*/
|
|
16
|
+
dir: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const defaultAnimationRegistry = new Map<string, ElementAnimation>();
|
|
20
|
+
const customAnimationRegistry = new WeakMap<Element, ElementAnimationMap>();
|
|
21
|
+
|
|
22
|
+
function ensureAnimation(animation: ElementAnimation | null) {
|
|
23
|
+
return animation ?? { keyframes: [], options: { duration: 0 } };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//
|
|
27
|
+
// Given an ElementAnimation, this function returns a new ElementAnimation where the keyframes property reflects either
|
|
28
|
+
// keyframes or rtlKeyframes depending on the specified directionality.
|
|
29
|
+
//
|
|
30
|
+
function getLogicalAnimation(animation: ElementAnimation) {
|
|
31
|
+
return animation;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
//
|
|
35
|
+
// Sets a default animation. Components should use the `name.animation` for primary animations and `name.part.animation`
|
|
36
|
+
// for secondary animations, e.g. `dialog.show` and `dialog.overlay.show`. For modifiers, use `drawer.showTop`.
|
|
37
|
+
//
|
|
38
|
+
export function setDefaultAnimation(animationName: string, animation: ElementAnimation | null) {
|
|
39
|
+
defaultAnimationRegistry.set(animationName, ensureAnimation(animation));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//
|
|
43
|
+
// Sets a custom animation for the specified element.
|
|
44
|
+
//
|
|
45
|
+
export function setAnimation(el: Element, animationName: string, animation: ElementAnimation | null) {
|
|
46
|
+
customAnimationRegistry.set(el, { ...customAnimationRegistry.get(el), [animationName]: ensureAnimation(animation) });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
//
|
|
50
|
+
// Gets an element's animation. Falls back to the default if no animation is found.
|
|
51
|
+
//
|
|
52
|
+
export function getAnimation(el: Element, animationName: string) {
|
|
53
|
+
const customAnimation = customAnimationRegistry.get(el);
|
|
54
|
+
|
|
55
|
+
// Check for a custom animation
|
|
56
|
+
if (customAnimation?.[animationName]) {
|
|
57
|
+
return getLogicalAnimation(customAnimation[animationName]);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Check for a default animation
|
|
61
|
+
const defaultAnimation = defaultAnimationRegistry.get(animationName);
|
|
62
|
+
if (defaultAnimation) {
|
|
63
|
+
return getLogicalAnimation(defaultAnimation);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Fall back to an empty animation
|
|
67
|
+
return {
|
|
68
|
+
keyframes: [],
|
|
69
|
+
options: { duration: 0 }
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
// @charset "utf-8";
|
|
3
|
+
|
|
4
|
+
@import "~@govtechsg/sgds/sass/functions";
|
|
5
|
+
@import "~@govtechsg/sgds/sass/variables";
|
|
6
|
+
@import "~@govtechsg/sgds/sass/mixins";
|
|
7
|
+
|
|
8
|
+
@import "~@govtechsg/sgds/sass/utilities";
|
|
9
|
+
|
|
10
|
+
@import "~@govtechsg/sgds/sass/root";
|
|
11
|
+
@import "~@govtechsg/sgds/sass/reboot";
|
|
12
|
+
@import "~@govtechsg/sgds/sass/type";
|
|
13
|
+
@import "~@govtechsg/sgds/sass/helpers";
|
|
14
|
+
@import "~@govtechsg/sgds/sass/utilities/api";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { property, state } from "lit/decorators.js";
|
|
2
|
+
import genId from "./generateId";
|
|
3
|
+
import SgdsElement from "./sgds-element";
|
|
4
|
+
|
|
5
|
+
export type Variant =
|
|
6
|
+
| "primary"
|
|
7
|
+
| "secondary"
|
|
8
|
+
| "success"
|
|
9
|
+
| "danger"
|
|
10
|
+
| "warning"
|
|
11
|
+
| "info"
|
|
12
|
+
| "dark"
|
|
13
|
+
| "light"
|
|
14
|
+
| string;
|
|
15
|
+
|
|
16
|
+
export type Color =
|
|
17
|
+
| "primary"
|
|
18
|
+
| "secondary"
|
|
19
|
+
| "success"
|
|
20
|
+
| "danger"
|
|
21
|
+
| "warning"
|
|
22
|
+
| "info"
|
|
23
|
+
| "dark"
|
|
24
|
+
| "light"
|
|
25
|
+
| "white"
|
|
26
|
+
| "muted";
|
|
27
|
+
|
|
28
|
+
export class CardElement extends SgdsElement {
|
|
29
|
+
@property({ type: String })
|
|
30
|
+
|
|
31
|
+
/** The border's variant. */
|
|
32
|
+
@property() borderColor?: Variant;
|
|
33
|
+
|
|
34
|
+
/** The bg's variant. */
|
|
35
|
+
@property() bgColor?: Variant;
|
|
36
|
+
|
|
37
|
+
/** The text's variant. */
|
|
38
|
+
@property() textColor?: Color;
|
|
39
|
+
|
|
40
|
+
@property({ type: Boolean, reflect: true })
|
|
41
|
+
disabled = false;
|
|
42
|
+
}
|