@cfpb/cfpb-design-system 4.2.4 → 4.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.
- package/CHANGELOG.md +166 -1
- package/dist/components/cfpb-expandables/index.js +1 -1
- package/dist/components/cfpb-expandables/index.js.map +3 -3
- package/dist/components/cfpb-forms/index.js +1 -1
- package/dist/components/cfpb-forms/index.js.map +2 -2
- package/dist/elements/cfpb-button/index.js +4 -4
- package/dist/elements/cfpb-button/index.js.map +3 -3
- package/dist/elements/cfpb-checkbox-icon/index.js +29 -0
- package/dist/elements/{cfpb-checkbox → cfpb-checkbox-icon}/index.js.map +4 -4
- package/dist/elements/cfpb-expandable/index.css +2 -0
- package/dist/elements/cfpb-expandable/index.css.map +7 -0
- package/dist/elements/cfpb-expandable/index.js +33 -0
- package/dist/elements/cfpb-expandable/index.js.map +7 -0
- package/dist/elements/cfpb-file-upload/index.js +4 -4
- package/dist/elements/cfpb-file-upload/index.js.map +3 -3
- package/dist/elements/cfpb-form-alert/index.js +32 -0
- package/dist/elements/cfpb-form-alert/index.js.map +7 -0
- package/dist/elements/cfpb-form-choice/index.js +12 -3
- package/dist/elements/cfpb-form-choice/index.js.map +4 -4
- package/dist/elements/cfpb-form-search/index.js +41 -0
- package/dist/elements/cfpb-form-search/index.js.map +7 -0
- package/dist/elements/cfpb-form-search-input/index.js +41 -0
- package/dist/elements/cfpb-form-search-input/index.js.map +7 -0
- package/dist/elements/cfpb-icon-text/index.js +3 -3
- package/dist/elements/cfpb-icon-text/index.js.map +3 -3
- package/dist/elements/cfpb-label/index.js +3 -3
- package/dist/elements/cfpb-label/index.js.map +2 -2
- package/dist/elements/cfpb-list/index.js +39 -0
- package/dist/elements/cfpb-list/index.js.map +7 -0
- package/dist/elements/cfpb-list-item/index.js +39 -0
- package/dist/elements/cfpb-list-item/index.js.map +7 -0
- package/dist/elements/cfpb-multiselect/index.js +13 -4
- package/dist/elements/cfpb-multiselect/index.js.map +4 -4
- package/dist/elements/cfpb-pagination/index.js +3 -3
- package/dist/elements/cfpb-pagination/index.js.map +2 -2
- package/dist/elements/cfpb-select/index.css +2 -0
- package/dist/elements/cfpb-select/index.css.map +7 -0
- package/dist/elements/cfpb-select/index.js +42 -0
- package/dist/elements/cfpb-select/index.js.map +7 -0
- package/dist/elements/cfpb-select-list/index.js +39 -0
- package/dist/elements/cfpb-select-list/index.js.map +7 -0
- package/dist/elements/cfpb-tag-filter/index.js +3 -3
- package/dist/elements/cfpb-tag-filter/index.js.map +3 -3
- package/dist/elements/cfpb-tag-group/index.js +3 -3
- package/dist/elements/cfpb-tag-group/index.js.map +4 -4
- package/dist/elements/cfpb-tag-topic/index.js +4 -4
- package/dist/elements/cfpb-tag-topic/index.js.map +1 -1
- package/dist/elements/index.css +2 -0
- package/dist/elements/index.css.map +7 -0
- package/dist/elements/index.js +7 -6
- package/dist/elements/index.js.map +4 -4
- package/dist/index.js +7 -6
- package/dist/index.js.map +4 -4
- package/dist/utilities/index.js +1 -1
- package/dist/utilities/index.js.map +3 -3
- package/package.json +1 -1
- package/src/components/cfpb-expandables/expandable.js +3 -0
- package/src/components/cfpb-forms/multiselect.js +1 -1
- package/src/elements/abstracts/custom-props.css +123 -0
- package/src/elements/abstracts/grid-mixins.scss +83 -0
- package/src/elements/abstracts/heading-mixins.scss +346 -0
- package/src/elements/abstracts/index.scss +7 -0
- package/src/elements/abstracts/media-queries.scss +35 -0
- package/src/elements/abstracts/sizing-vars.scss +65 -0
- package/src/elements/abstracts/vars-breakpoints.scss +16 -0
- package/src/elements/abstracts/vars.css +79 -0
- package/src/elements/base/base.scss +375 -0
- package/src/elements/base/font.scss +27 -0
- package/src/elements/base/index.scss +3 -0
- package/src/elements/base/normalize.scss +290 -0
- package/src/elements/cfpb-button/cfpb-button-group.scss +10 -0
- package/src/elements/cfpb-button/cfpb-button-link.scss +96 -0
- package/src/elements/cfpb-button/cfpb-button.component.scss +11 -4
- package/src/elements/cfpb-button/cfpb-button.scss +222 -0
- package/src/elements/cfpb-button/index.js +28 -29
- package/src/elements/cfpb-button/vars.css +30 -0
- package/src/elements/cfpb-checkbox-icon/cfpb-checkbox-icon.component.scss +88 -0
- package/src/elements/cfpb-checkbox-icon/index.js +104 -0
- package/src/elements/cfpb-expandable/cfpb-expandable.component.scss +218 -0
- package/src/elements/cfpb-expandable/index.js +127 -0
- package/src/elements/cfpb-file-upload/cfpb-file-upload.component.scss +2 -2
- package/src/elements/cfpb-file-upload/index.js +16 -18
- package/src/elements/cfpb-form-alert/cfpb-form-alert.component.scss +36 -0
- package/src/elements/cfpb-form-alert/index.js +55 -0
- package/src/elements/cfpb-form-choice/cfpb-form-choice.component.scss +42 -81
- package/src/elements/cfpb-form-choice/index.js +58 -18
- package/src/elements/cfpb-form-search/cfpb-form-search.component.scss +54 -0
- package/src/elements/cfpb-form-search/index.js +194 -0
- package/src/elements/cfpb-form-search-input/cfpb-form-search-input.component.scss +217 -0
- package/src/elements/cfpb-form-search-input/index.js +136 -0
- package/src/elements/cfpb-icon-text/cfpb-icon-text.component.scss +32 -39
- package/src/elements/cfpb-icon-text/index.js +32 -104
- package/src/elements/cfpb-label/cfpb-label.component.scss +2 -2
- package/src/elements/cfpb-label/index.js +6 -9
- package/src/elements/cfpb-list/cfpb-list.component.scss +23 -0
- package/src/elements/cfpb-list/index.js +357 -0
- package/src/elements/cfpb-list/index.spec.js +169 -0
- package/src/elements/cfpb-list-item/cfpb-list-item.component.scss +69 -0
- package/src/elements/cfpb-list-item/index.js +215 -0
- package/src/elements/cfpb-pagination/cfpb-pagination.component.scss +2 -7
- package/src/elements/cfpb-pagination/index.js +6 -8
- package/src/elements/cfpb-select/cfpb-select.component.scss +241 -0
- package/src/elements/cfpb-select/index.js +381 -0
- package/src/elements/cfpb-tag-filter/cfpb-tag-filter.component.scss +6 -3
- package/src/elements/cfpb-tag-filter/index.js +15 -7
- package/src/elements/cfpb-tag-group/cfpb-tag-group.component.scss +2 -2
- package/src/elements/cfpb-tag-group/index.js +53 -6
- package/src/elements/cfpb-tag-topic/index.js +5 -7
- package/src/elements/cfpb-utilities/parse-child-data.js +50 -0
- package/src/elements/cfpb-utilities/parse-child-data.spec.js +56 -0
- package/src/elements/cfpb-utilities/search-service.js +46 -0
- package/src/elements/cfpb-utilities/search-service.spec.js +138 -0
- package/src/elements/cfpb-utilities/transition/transition.scss +98 -0
- package/src/elements/index.js +7 -1
- package/src/index.scss +11 -0
- package/src/tokens/abstracts/custom-props.json +1642 -0
- package/src/tokens/abstracts/vars.json +1319 -0
- package/src/tokens/cfpb-button/vars.json +436 -0
- package/src/utilities/transition/max-height-transition.js +74 -0
- package/dist/elements/cfpb-checkbox/index.js +0 -29
- package/src/elements/cfpb-multiselect/cfpb-multiselect.component.scss +0 -225
- package/src/elements/cfpb-multiselect/index.js +0 -444
- package/src/elements/cfpb-multiselect/multiselect-model.js +0 -288
- package/src/elements/cfpb-multiselect/multiselect-model.spec.js +0 -236
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { html, LitElement, css, unsafeCSS } from 'lit';
|
|
2
2
|
import { classMap } from 'lit/directives/class-map.js';
|
|
3
|
+
import { ref, createRef } from 'lit/directives/ref.js';
|
|
3
4
|
import styles from './cfpb-form-choice.component.scss';
|
|
5
|
+
import { CfpbCheckboxIcon } from '../cfpb-checkbox-icon';
|
|
4
6
|
|
|
5
7
|
// The validation states are error, warning, or success.
|
|
6
8
|
const VALID_VALIDATION = ['error', 'warning', 'success'];
|
|
@@ -17,29 +19,27 @@ export class CfpbFormChoice extends LitElement {
|
|
|
17
19
|
${unsafeCSS(styles)}
|
|
18
20
|
`;
|
|
19
21
|
|
|
22
|
+
#checkboxIcon = createRef();
|
|
23
|
+
|
|
20
24
|
/**
|
|
21
25
|
* @property {boolean} checked - Whether the choice is checked or not.
|
|
22
26
|
* @property {boolean} disabled - Whether the choice is disabled or not.
|
|
23
27
|
* @property {boolean} large - Whether the choice has a large target area.
|
|
24
28
|
* @property {string} validation - Validation style: error, warning, success.
|
|
25
29
|
* @property {string} type - Choice type: checkbox or radio.
|
|
26
|
-
* @property {string} inlist - Whether the choice appears in a <li> list.
|
|
27
30
|
* @property {string} name - The name within a form.
|
|
28
31
|
* @property {string} value - The value to submit within a form.
|
|
29
32
|
* @returns {object} The map of properties.
|
|
30
33
|
*/
|
|
31
|
-
static
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
value: { type: String },
|
|
41
|
-
};
|
|
42
|
-
}
|
|
34
|
+
static properties = {
|
|
35
|
+
checked: { type: Boolean, reflect: true },
|
|
36
|
+
disabled: { type: Boolean },
|
|
37
|
+
large: { type: Boolean },
|
|
38
|
+
validation: { type: String },
|
|
39
|
+
type: { type: String },
|
|
40
|
+
name: { type: String },
|
|
41
|
+
value: { type: String },
|
|
42
|
+
};
|
|
43
43
|
|
|
44
44
|
constructor() {
|
|
45
45
|
super();
|
|
@@ -48,12 +48,12 @@ export class CfpbFormChoice extends LitElement {
|
|
|
48
48
|
this.large = false;
|
|
49
49
|
this.validation = '';
|
|
50
50
|
this.type = 'checkbox';
|
|
51
|
-
this.inlist = false;
|
|
52
51
|
this.name = '';
|
|
53
52
|
this.value = '';
|
|
54
53
|
}
|
|
55
54
|
|
|
56
|
-
#onChange() {
|
|
55
|
+
#onChange(evt) {
|
|
56
|
+
this.checked = evt.target.checked;
|
|
57
57
|
this.dispatchEvent(
|
|
58
58
|
new Event('change', {
|
|
59
59
|
bubbles: true,
|
|
@@ -71,6 +71,26 @@ export class CfpbFormChoice extends LitElement {
|
|
|
71
71
|
);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
#onFocus() {
|
|
75
|
+
if (this.#checkboxIcon.value) {
|
|
76
|
+
this.#checkboxIcon.value.focus();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
#onBlur() {
|
|
81
|
+
if (this.#checkboxIcon.value) {
|
|
82
|
+
this.#checkboxIcon.value.blur();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#onMouseOver() {
|
|
87
|
+
if (this.#checkboxIcon.value) this.#checkboxIcon.value.mouseover();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
#onMouseLeave() {
|
|
91
|
+
if (this.#checkboxIcon.value) this.#checkboxIcon.value.mouseleave();
|
|
92
|
+
}
|
|
93
|
+
|
|
74
94
|
focus() {
|
|
75
95
|
this.shadowRoot.querySelector('input').focus();
|
|
76
96
|
}
|
|
@@ -98,7 +118,6 @@ export class CfpbFormChoice extends LitElement {
|
|
|
98
118
|
'm-form-field': true,
|
|
99
119
|
[`m-form-field--${this.type}`]: true,
|
|
100
120
|
'm-form-field--lg-target': this.large,
|
|
101
|
-
'm-form-field--in-list': this.inlist,
|
|
102
121
|
};
|
|
103
122
|
|
|
104
123
|
if (this.#validValidation)
|
|
@@ -107,22 +126,41 @@ export class CfpbFormChoice extends LitElement {
|
|
|
107
126
|
return classes;
|
|
108
127
|
}
|
|
109
128
|
|
|
129
|
+
#renderCheckbox() {
|
|
130
|
+
return html`
|
|
131
|
+
<cfpb-checkbox-icon
|
|
132
|
+
.checked=${this.checked}
|
|
133
|
+
?disabled=${this.disabled}
|
|
134
|
+
validation=${this.#validValidation}
|
|
135
|
+
${ref(this.#checkboxIcon)}
|
|
136
|
+
></cfpb-checkbox-icon>
|
|
137
|
+
`;
|
|
138
|
+
}
|
|
139
|
+
|
|
110
140
|
render() {
|
|
111
141
|
const classes = classMap(this.#baseClass);
|
|
112
142
|
|
|
113
143
|
return html`
|
|
114
|
-
<div
|
|
144
|
+
<div
|
|
145
|
+
class=${classes}
|
|
146
|
+
?large=${this.large}
|
|
147
|
+
@mouseover=${this.#onMouseOver}
|
|
148
|
+
@mouseleave=${this.#onMouseLeave}
|
|
149
|
+
>
|
|
115
150
|
<input
|
|
116
151
|
class="a-${this.type}"
|
|
117
|
-
type
|
|
152
|
+
type=${this.#validType}
|
|
118
153
|
id="choice-input"
|
|
119
154
|
?disabled=${this.disabled}
|
|
120
155
|
.checked=${this.checked}
|
|
121
156
|
@change=${this.#onChange}
|
|
122
157
|
@input=${this.#onInput}
|
|
158
|
+
@focus=${this.#onFocus}
|
|
159
|
+
@blur=${this.#onBlur}
|
|
123
160
|
aria-invalid=${this.#validValidation === 'error' ? 'true' : 'false'}
|
|
124
161
|
/>
|
|
125
162
|
<label class="a-label" for="choice-input">
|
|
163
|
+
${this.type === 'checkbox' ? this.#renderCheckbox() : null}
|
|
126
164
|
<slot></slot>
|
|
127
165
|
</label>
|
|
128
166
|
</div>
|
|
@@ -130,6 +168,8 @@ export class CfpbFormChoice extends LitElement {
|
|
|
130
168
|
}
|
|
131
169
|
|
|
132
170
|
static init() {
|
|
171
|
+
CfpbCheckboxIcon.init();
|
|
172
|
+
|
|
133
173
|
window.customElements.get('cfpb-form-choice') ||
|
|
134
174
|
window.customElements.define('cfpb-form-choice', CfpbFormChoice);
|
|
135
175
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
@use 'sass:math';
|
|
2
|
+
@use '@cfpb/cfpb-design-system/src/elements/abstracts' as *;
|
|
3
|
+
@use '@cfpb/cfpb-design-system/src/elements/cfpb-button/cfpb-button' as *;
|
|
4
|
+
|
|
5
|
+
:host {
|
|
6
|
+
// Hide light DOM content.
|
|
7
|
+
::slotted(ul),
|
|
8
|
+
::slotted(ol) {
|
|
9
|
+
display: none !important;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// This line-height settings is taken from base.scss.
|
|
13
|
+
button {
|
|
14
|
+
line-height: math.div(19px, $base-font-size-px);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.o-form-search {
|
|
18
|
+
position: relative;
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
row-gap: math.div(15px, $base-font-size-px) + rem;
|
|
22
|
+
|
|
23
|
+
// Tablet and above.
|
|
24
|
+
@include respond-to-min($bp-sm-min) {
|
|
25
|
+
flex-direction: row;
|
|
26
|
+
border-left: 0;
|
|
27
|
+
button[type='submit'] {
|
|
28
|
+
border-top-left-radius: 0;
|
|
29
|
+
border-bottom-left-radius: 0;
|
|
30
|
+
flex-basis: 25%;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.container {
|
|
35
|
+
position: relative;
|
|
36
|
+
width: 100%;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.popup {
|
|
40
|
+
display: none;
|
|
41
|
+
position: absolute;
|
|
42
|
+
top: 34px;
|
|
43
|
+
z-index: 100;
|
|
44
|
+
background: white;
|
|
45
|
+
border: 1px solid var(--pacific);
|
|
46
|
+
border-top: 0;
|
|
47
|
+
width: calc(100% - 2px);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.popup.show {
|
|
51
|
+
display: block;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { html, LitElement, css, unsafeCSS } from 'lit';
|
|
2
|
+
import styles from './cfpb-form-search.component.scss';
|
|
3
|
+
import { ref, createRef } from 'lit/directives/ref.js';
|
|
4
|
+
import { CfpbFormSearchInput } from '../cfpb-form-search-input';
|
|
5
|
+
import { SearchService } from '../cfpb-utilities/search-service.js';
|
|
6
|
+
import { CfpbList } from '../cfpb-list';
|
|
7
|
+
import { CfpbFormAlert } from '../cfpb-form-alert';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @element cfpb-form-search
|
|
11
|
+
*/
|
|
12
|
+
export class CfpbFormSearch extends LitElement {
|
|
13
|
+
static styles = css`
|
|
14
|
+
${unsafeCSS(styles)}
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
static formAssociated = true;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @property {boolean} disabled - Whether the choice is disabled or not.
|
|
21
|
+
* @property {string} validation - Validation style: error, warning, success.
|
|
22
|
+
* @property {string} label - The aria-label for the input.
|
|
23
|
+
* @property {string} name - The name within a form.
|
|
24
|
+
* @property {string} value - The value within the input.
|
|
25
|
+
* @property {string} placeholder - The placeholder value.
|
|
26
|
+
* @property {string} maxlength - The maximum characters allowed in the input.
|
|
27
|
+
* @property {string} ariaLabelInput - aria-label for input.
|
|
28
|
+
* @property {string} ariaLabelButton - aria-label for button.
|
|
29
|
+
* @returns {object} The map of properties.
|
|
30
|
+
*/
|
|
31
|
+
static get properties() {
|
|
32
|
+
return {
|
|
33
|
+
disabled: { type: Boolean },
|
|
34
|
+
validation: { type: String },
|
|
35
|
+
label: { type: String },
|
|
36
|
+
name: { type: String },
|
|
37
|
+
title: { type: Boolean, attribute: true },
|
|
38
|
+
value: { type: String },
|
|
39
|
+
maxlength: { type: Number },
|
|
40
|
+
placeholder: { type: String },
|
|
41
|
+
ariaLabelInput: { type: String, attribute: 'aria-label-input' },
|
|
42
|
+
ariaLabelButton: { type: String, attribute: 'aria-label-button' },
|
|
43
|
+
searchList: { type: Array },
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#list = createRef();
|
|
48
|
+
#popup = createRef();
|
|
49
|
+
|
|
50
|
+
#internals;
|
|
51
|
+
#search;
|
|
52
|
+
|
|
53
|
+
constructor() {
|
|
54
|
+
super();
|
|
55
|
+
|
|
56
|
+
this.value = '';
|
|
57
|
+
this.#internals = this.attachInternals();
|
|
58
|
+
this.searchList = [];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
#onSlotChange(evt) {
|
|
62
|
+
const slot = evt.target;
|
|
63
|
+
|
|
64
|
+
const list = slot
|
|
65
|
+
.assignedNodes({ flatten: true })
|
|
66
|
+
.filter(
|
|
67
|
+
(node) =>
|
|
68
|
+
node.nodeType === Node.ELEMENT_NODE &&
|
|
69
|
+
(node.tagName === 'UL' || node.tagName === 'OL'),
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
if (!list || !list[0]) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Extract list items (with their text or link info)
|
|
77
|
+
const items = [...list[0].querySelectorAll('li')].map((li) => {
|
|
78
|
+
const link = li.querySelector('a');
|
|
79
|
+
if (link) {
|
|
80
|
+
return {
|
|
81
|
+
value: link.textContent.trim(),
|
|
82
|
+
href: link.getAttribute('href'),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return { value: li.textContent.trim() };
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
this.searchList = items;
|
|
89
|
+
|
|
90
|
+
this.#search = new SearchService(
|
|
91
|
+
items.map((item) => {
|
|
92
|
+
return item.value;
|
|
93
|
+
}),
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
#onClear() {
|
|
98
|
+
this.#popup.value.classList.remove('show');
|
|
99
|
+
this.#list.value.showAllItems();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
#onInput(evt) {
|
|
103
|
+
if (evt.target.value.length > 1) {
|
|
104
|
+
this.#popup.value.classList.add('show');
|
|
105
|
+
if (this.#search) {
|
|
106
|
+
this.#list.value.filterItems(this.#search.search(evt.target.value));
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
this.#popup.value.classList.remove('show');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
this.value = evt.target.value;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
#onBlur() {
|
|
116
|
+
this.#popup.value.classList.remove('show');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
get isSearchDisabled() {
|
|
120
|
+
return this.disabled || this.isOverMaxLength;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
get isOverMaxLength() {
|
|
124
|
+
const isOverMax = this.value.length > this.maxlength;
|
|
125
|
+
if (isOverMax) this.validation = 'error';
|
|
126
|
+
else this.validation = '';
|
|
127
|
+
return isOverMax;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
#onClickSearch(evt) {
|
|
131
|
+
evt.preventDefault();
|
|
132
|
+
if (this.disabled) return;
|
|
133
|
+
|
|
134
|
+
console.log(this.value);
|
|
135
|
+
|
|
136
|
+
if (this.value !== '') {
|
|
137
|
+
this.#internals.setFormValue(this.value);
|
|
138
|
+
this.#internals.form?.requestSubmit();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
render() {
|
|
143
|
+
return html` <!--Light DOM content-->
|
|
144
|
+
<slot @slotchange=${this.#onSlotChange}></slot>
|
|
145
|
+
|
|
146
|
+
<!--Shadow DOM content-->
|
|
147
|
+
<div class="o-form-search">
|
|
148
|
+
<div class="container">
|
|
149
|
+
<cfpb-form-search-input
|
|
150
|
+
?name=${this.name}
|
|
151
|
+
?value=${this.value}
|
|
152
|
+
?placeholder=${this.placeholder}
|
|
153
|
+
title=${this.title}
|
|
154
|
+
?maxlength=${this.maxlength}
|
|
155
|
+
aria-label=${this.ariaLabelInput}
|
|
156
|
+
?validation=${this.validation}
|
|
157
|
+
@clear=${this.#onClear}
|
|
158
|
+
@input=${this.#onInput}
|
|
159
|
+
@blur=${this.#onBlur}
|
|
160
|
+
></cfpb-form-search-input>
|
|
161
|
+
|
|
162
|
+
<div class="popup" ${ref(this.#popup)}>
|
|
163
|
+
<cfpb-list .childData=${this.searchList} ${ref(this.#list)}>
|
|
164
|
+
</cfpb-list>
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
<button
|
|
169
|
+
class="a-btn"
|
|
170
|
+
type="submit"
|
|
171
|
+
aria-label="Search for term(s)"
|
|
172
|
+
?disabled=${this.isSearchDisabled}
|
|
173
|
+
@click=${this.#onClickSearch}
|
|
174
|
+
>
|
|
175
|
+
Search
|
|
176
|
+
</button>
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
${this.isOverMaxLength
|
|
180
|
+
? html`<cfpb-form-alert validation="error">
|
|
181
|
+
Searches are limited to ${this.maxlength} characters.
|
|
182
|
+
</cfpb-form-alert>`
|
|
183
|
+
: null}`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
static init() {
|
|
187
|
+
CfpbFormSearchInput.init();
|
|
188
|
+
CfpbList.init();
|
|
189
|
+
CfpbFormAlert.init();
|
|
190
|
+
|
|
191
|
+
window.customElements.get('cfpb-form-search') ||
|
|
192
|
+
window.customElements.define('cfpb-form-search', CfpbFormSearch);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
@use 'sass:math';
|
|
2
|
+
@use '@cfpb/cfpb-design-system/src/elements/abstracts' as *;
|
|
3
|
+
@use '@cfpb/cfpb-design-system/src/components/cfpb-forms/text-input' as *;
|
|
4
|
+
@use '@cfpb/cfpb-design-system/src/components/cfpb-icons/icon' as *;
|
|
5
|
+
|
|
6
|
+
:host {
|
|
7
|
+
// Theme
|
|
8
|
+
--input-border-default: var(--gray-60); // $input-border
|
|
9
|
+
--input-border-disabled: var(--gray-60);
|
|
10
|
+
--input-border-success: var(--green);
|
|
11
|
+
--input-border-warning: var(--gold);
|
|
12
|
+
--input-border-error: var(--red);
|
|
13
|
+
|
|
14
|
+
// Private vars
|
|
15
|
+
--input-border: var(--input-border-default);
|
|
16
|
+
|
|
17
|
+
.o-search-input {
|
|
18
|
+
position: relative;
|
|
19
|
+
display: flex;
|
|
20
|
+
width: initial;
|
|
21
|
+
flex: 0 1 100%;
|
|
22
|
+
|
|
23
|
+
// Add a min-width for the default width of the text input + clear button,
|
|
24
|
+
// so that when the clear button appears it doesn't bump the input width.
|
|
25
|
+
min-width: 300px;
|
|
26
|
+
|
|
27
|
+
background: var(--white);
|
|
28
|
+
|
|
29
|
+
// Create a faux input border that includes the input and clear button.
|
|
30
|
+
border: 1px solid var(--input-border);
|
|
31
|
+
outline: 0 solid var(--input-border);
|
|
32
|
+
|
|
33
|
+
label {
|
|
34
|
+
position: absolute;
|
|
35
|
+
left: 10px;
|
|
36
|
+
align-self: center;
|
|
37
|
+
cursor: pointer;
|
|
38
|
+
line-height: math.div(
|
|
39
|
+
14.4px,
|
|
40
|
+
$base-font-size-px
|
|
41
|
+
); // 9px vertically centers icon.
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// This line-height settings is taken from base.scss.
|
|
45
|
+
input {
|
|
46
|
+
line-height: math.div(19px, $base-font-size-px);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
input[type='search'] {
|
|
50
|
+
font-size: 1rem;
|
|
51
|
+
width: 100%;
|
|
52
|
+
white-space: nowrap;
|
|
53
|
+
padding-left: 30px;
|
|
54
|
+
cursor: text;
|
|
55
|
+
|
|
56
|
+
// Remove default rounding in Safari.
|
|
57
|
+
appearance: none;
|
|
58
|
+
|
|
59
|
+
// Remove input border and re-create it around the input and clear X.
|
|
60
|
+
border: none;
|
|
61
|
+
box-shadow: none;
|
|
62
|
+
outline: none;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Style the clear x button, and hide it by default.
|
|
66
|
+
button[type='reset'] {
|
|
67
|
+
display: none;
|
|
68
|
+
align-self: center;
|
|
69
|
+
|
|
70
|
+
color: var(--gray-40);
|
|
71
|
+
font-size: 20px;
|
|
72
|
+
border: 1px solid transparent;
|
|
73
|
+
background-color: transparent;
|
|
74
|
+
outline: 0;
|
|
75
|
+
|
|
76
|
+
// Set the touch target minimum for iOS.
|
|
77
|
+
width: 44px;
|
|
78
|
+
text-align: right;
|
|
79
|
+
|
|
80
|
+
> svg {
|
|
81
|
+
// Set width of SVG width to create a box for the focus rectangle.
|
|
82
|
+
width: 25px;
|
|
83
|
+
|
|
84
|
+
// Prevent targeting of button's internal SVG icon.
|
|
85
|
+
pointer-events: none;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
button[type='reset']:hover {
|
|
90
|
+
color: var(--black);
|
|
91
|
+
cursor: pointer;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Style the clear x focus rectangle.
|
|
95
|
+
button[type='reset']:focus {
|
|
96
|
+
color: var(--black);
|
|
97
|
+
|
|
98
|
+
// Put the focus rectangle on the icon
|
|
99
|
+
// because the button touch target is larger and would be lop-sided if
|
|
100
|
+
// we put the rectangle on the button.
|
|
101
|
+
> svg {
|
|
102
|
+
outline: 1px dotted var(--pacific);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// However, hide it if we haven't entered any text yet.
|
|
107
|
+
input[type='search']:placeholder-shown ~ button[type='reset'] {
|
|
108
|
+
display: none;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Remove the default x mark in Chrome.
|
|
112
|
+
input[type='search']::-webkit-search-decoration,
|
|
113
|
+
input[type='search']::-webkit-search-cancel-button,
|
|
114
|
+
input[type='search']::-webkit-search-results-button,
|
|
115
|
+
input[type='search']::-webkit-search-results-decoration {
|
|
116
|
+
display: none;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
&:hover,
|
|
120
|
+
&:focus-within {
|
|
121
|
+
border: 1px solid var(--pacific);
|
|
122
|
+
box-shadow: 0 0 0 1px var(--pacific);
|
|
123
|
+
outline-color: var(--pacific);
|
|
124
|
+
input[type='search'] {
|
|
125
|
+
outline: none;
|
|
126
|
+
|
|
127
|
+
// Remove the right-hand padding, because the clear button is showing.
|
|
128
|
+
padding-right: 0;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
&:focus-within {
|
|
133
|
+
outline: 1px dotted var(--pacific);
|
|
134
|
+
outline-offset: 2px;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Validation state coloring.
|
|
138
|
+
&--error,
|
|
139
|
+
&--warning,
|
|
140
|
+
&--success {
|
|
141
|
+
outline-width: 1px;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
&--error {
|
|
145
|
+
--input-border: var(--input-border-error);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
&--warning {
|
|
149
|
+
--input-border: var(--input-border-warning);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
&--success {
|
|
153
|
+
--input-border: var(--input-border-success);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Show the clear x button if we're focused within the search input area.
|
|
157
|
+
&:focus-within button[type='reset'],
|
|
158
|
+
&:hover button[type='reset'] {
|
|
159
|
+
display: flex;
|
|
160
|
+
justify-content: flex-end;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// If the value is on a search input by the backend, then the reset button
|
|
165
|
+
// will only reset the input back to initial value at page load. We add a
|
|
166
|
+
// small amount of JS to fully clear the input for this circumstance.
|
|
167
|
+
// However, this doesn't work when JS is disabled,
|
|
168
|
+
// so in that case we hide the reset button.
|
|
169
|
+
.no-js .o-search-input button[type='reset'] {
|
|
170
|
+
display: none !important;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
:host([borderless]) {
|
|
175
|
+
.o-search-input {
|
|
176
|
+
border-color: transparent;
|
|
177
|
+
|
|
178
|
+
&:hover,
|
|
179
|
+
&:focus-within {
|
|
180
|
+
border-color: transparent;
|
|
181
|
+
box-shadow: 0 0 0 1px transparent;
|
|
182
|
+
outline-color: transparent;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
:host([disabled]) {
|
|
188
|
+
--input-border: var(--input-border-disabled);
|
|
189
|
+
|
|
190
|
+
.o-search-input {
|
|
191
|
+
label,
|
|
192
|
+
input[type='search'] {
|
|
193
|
+
color: var(--input-border);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
&:hover {
|
|
197
|
+
border: 1px solid var(--input-border);
|
|
198
|
+
box-shadow: none;
|
|
199
|
+
|
|
200
|
+
label,
|
|
201
|
+
input[type='search'] {
|
|
202
|
+
cursor: not-allowed;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
button[type='reset'] {
|
|
206
|
+
display: none;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Validation state coloring.
|
|
211
|
+
&--error:hover,
|
|
212
|
+
&--warning:hover,
|
|
213
|
+
&--success:hover {
|
|
214
|
+
outline-color: var(--input-border);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|