@cfpb/cfpb-design-system 4.2.4 → 4.3.1
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 +186 -1
- package/dist/components/cfpb-buttons/index.css +1 -1
- package/dist/components/cfpb-buttons/index.css.map +2 -2
- package/dist/components/cfpb-buttons/index.js +1 -1
- package/dist/components/cfpb-buttons/index.js.map +1 -1
- package/dist/components/cfpb-expandables/index.css +1 -1
- package/dist/components/cfpb-expandables/index.css.map +2 -2
- package/dist/components/cfpb-expandables/index.js +1 -1
- package/dist/components/cfpb-expandables/index.js.map +4 -4
- package/dist/components/cfpb-forms/index.css +1 -1
- package/dist/components/cfpb-forms/index.css.map +2 -2
- package/dist/components/cfpb-forms/index.js +1 -1
- package/dist/components/cfpb-forms/index.js.map +2 -2
- package/dist/components/cfpb-icons/index.css +1 -1
- package/dist/components/cfpb-icons/index.css.map +2 -2
- package/dist/components/cfpb-icons/index.js +1 -1
- package/dist/components/cfpb-icons/index.js.map +1 -1
- package/dist/components/cfpb-layout/index.css +1 -1
- package/dist/components/cfpb-layout/index.css.map +2 -2
- package/dist/components/cfpb-layout/index.js +1 -1
- package/dist/components/cfpb-layout/index.js.map +1 -1
- package/dist/components/cfpb-notifications/index.css +1 -1
- package/dist/components/cfpb-notifications/index.css.map +2 -2
- package/dist/components/cfpb-notifications/index.js +1 -1
- package/dist/components/cfpb-notifications/index.js.map +1 -1
- package/dist/components/cfpb-pagination/index.css +1 -1
- package/dist/components/cfpb-pagination/index.css.map +2 -2
- package/dist/components/cfpb-pagination/index.js +1 -1
- package/dist/components/cfpb-pagination/index.js.map +1 -1
- package/dist/components/cfpb-tables/index.css +1 -1
- package/dist/components/cfpb-tables/index.css.map +2 -2
- package/dist/components/cfpb-tables/index.js +1 -1
- package/dist/components/cfpb-tables/index.js.map +1 -1
- package/dist/components/cfpb-tooltips/index.css +1 -1
- package/dist/components/cfpb-tooltips/index.css.map +2 -2
- package/dist/components/cfpb-tooltips/index.js +1 -1
- package/dist/components/cfpb-tooltips/index.js.map +1 -1
- package/dist/components/cfpb-typography/index.css +1 -1
- package/dist/components/cfpb-typography/index.css.map +2 -2
- package/dist/components/cfpb-typography/index.js +1 -1
- package/dist/components/cfpb-typography/index.js.map +1 -1
- package/dist/elements/abstracts/index.js +2 -0
- package/dist/elements/abstracts/index.js.map +7 -0
- package/dist/elements/base/index.css +3 -0
- package/dist/elements/base/index.css.map +7 -0
- package/dist/elements/base/index.js +2 -0
- package/dist/elements/base/index.js.map +7 -0
- 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 +2 -2
- 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.css +1 -1
- package/dist/index.css.map +3 -3
- package/dist/index.js +7 -6
- package/dist/index.js.map +4 -4
- package/dist/utilities/index.css +1 -1
- package/dist/utilities/index.css.map +2 -2
- package/dist/utilities/index.js +1 -1
- package/dist/utilities/index.js.map +4 -4
- package/package.json +1 -1
- package/src/components/cfpb-buttons/button-group.scss +1 -1
- package/src/components/cfpb-buttons/button-link.scss +10 -54
- package/src/components/cfpb-buttons/button.scss +3 -3
- package/src/components/cfpb-buttons/vars.scss +1 -1
- package/src/components/cfpb-expandables/expandable-group.scss +1 -1
- package/src/components/cfpb-expandables/expandable.js +3 -0
- package/src/components/cfpb-expandables/expandable.scss +1 -1
- package/src/components/cfpb-expandables/summary.scss +1 -1
- package/src/components/cfpb-forms/form-alert.scss +1 -1
- package/src/components/cfpb-forms/form-field.scss +6 -6
- package/src/components/cfpb-forms/form.scss +1 -1
- package/src/components/cfpb-forms/label.scss +2 -2
- package/src/components/cfpb-forms/multiselect.js +1 -1
- package/src/components/cfpb-forms/multiselect.scss +1 -1
- package/src/components/cfpb-forms/range.scss +7 -7
- package/src/components/cfpb-forms/search-input.scss +1 -1
- package/src/components/cfpb-forms/select.scss +1 -1
- package/src/components/cfpb-forms/tag.scss +1 -1
- package/src/components/cfpb-forms/text-input.scss +1 -1
- package/src/components/cfpb-icons/icon.scss +1 -1
- package/src/components/cfpb-layout/card-group.scss +1 -1
- package/src/components/cfpb-layout/card.scss +1 -1
- package/src/components/cfpb-layout/email-signup.scss +1 -1
- package/src/components/cfpb-layout/featured-content-module.scss +1 -1
- package/src/components/cfpb-layout/hero.scss +1 -1
- package/src/components/cfpb-layout/layout.scss +9 -9
- package/src/components/cfpb-layout/well.scss +1 -1
- package/src/components/cfpb-notifications/banner.scss +1 -1
- package/src/components/cfpb-notifications/notification.scss +1 -1
- package/src/components/cfpb-pagination/pagination.scss +1 -1
- package/src/components/cfpb-tables/table.scss +1 -1
- package/src/components/cfpb-tooltips/tooltip.scss +1 -1
- package/src/components/cfpb-typography/date.scss +1 -1
- package/src/components/cfpb-typography/list.scss +1 -1
- package/src/components/cfpb-typography/meta-header.scss +1 -1
- package/src/components/cfpb-typography/mixins.scss +1 -1
- package/src/components/cfpb-typography/pull-quote.scss +1 -1
- package/src/components/cfpb-typography/slug-header.scss +1 -1
- package/src/components/cfpb-typography/tagline.scss +1 -1
- package/src/elements/abstracts/custom-props.css +123 -0
- package/src/{abstracts → elements/abstracts}/grid-mixins.scss +2 -1
- package/src/{abstracts → elements/abstracts}/heading-mixins.scss +1 -0
- package/src/{abstracts → elements/abstracts}/index.scss +1 -0
- package/src/{abstracts → elements/abstracts}/media-queries.scss +1 -1
- package/src/elements/abstracts/sizing-vars.scss +66 -0
- package/src/elements/abstracts/vars.css +79 -0
- package/src/{base → elements/base}/base.scss +14 -14
- package/src/elements/cfpb-button/cfpb-button-group.scss +12 -0
- package/src/elements/cfpb-button/cfpb-button-link.scss +103 -0
- package/src/elements/cfpb-button/cfpb-button.component.scss +11 -4
- package/src/elements/cfpb-button/cfpb-button.scss +218 -0
- package/src/elements/cfpb-button/index.js +44 -30
- 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 +25 -27
- 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 +140 -0
- package/src/elements/cfpb-icon-text/cfpb-icon-text.component.scss +33 -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 +34 -0
- package/src/elements/cfpb-list/index.js +379 -0
- package/src/elements/cfpb-list/index.spec.js +214 -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 +371 -0
- package/src/elements/cfpb-select/multiple-select-event-proxy.js +88 -0
- package/src/elements/cfpb-select/single-select-event-proxy.js +47 -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/cfpb-tag-topic.component.scss +2 -2
- 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.js +2 -2
- package/src/index.scss +14 -2
- 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/breakpoint-state.js +1 -1
- package/src/utilities/transition/max-height-transition.js +74 -0
- package/src/utilities/utilities.scss +1 -1
- package/dist/elements/cfpb-checkbox/index.js +0 -29
- package/src/abstracts/custom-props.scss +0 -175
- package/src/abstracts/vars.scss +0 -184
- 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
- /package/src/{abstracts → elements/abstracts}/index.js +0 -0
- /package/src/{abstracts → elements/abstracts}/vars-breakpoints.js +0 -0
- /package/src/{abstracts → elements/abstracts}/vars-breakpoints.scss +0 -0
- /package/src/{base → elements/base}/font.scss +0 -0
- /package/src/{base → elements/base}/index.js +0 -0
- /package/src/{base → elements/base}/index.scss +0 -0
- /package/src/{base → elements/base}/normalize.scss +0 -0
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { html, LitElement, css, unsafeCSS } from 'lit';
|
|
2
|
+
import { ref, createRef } from 'lit/directives/ref.js';
|
|
3
|
+
import styles from './cfpb-form-search-input.component.scss';
|
|
4
|
+
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
|
|
5
|
+
|
|
6
|
+
import searchIcon from '../../components/cfpb-icons/icons/search.svg';
|
|
7
|
+
import clearIcon from '../../components/cfpb-icons/icons/error.svg';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @element cfpb-form-search-input
|
|
11
|
+
*/
|
|
12
|
+
export class CfpbFormSearchInput extends LitElement {
|
|
13
|
+
static styles = css`
|
|
14
|
+
${unsafeCSS(styles)}
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @property {boolean} disabled - Whether the input is disabled or not.
|
|
19
|
+
* @property {string} validation - Validation style: error, warning, success.
|
|
20
|
+
* @property {string} label - The aria-label for the input.
|
|
21
|
+
* @property {string} name - The name within a form.
|
|
22
|
+
* @property {string} value - The value within the input.
|
|
23
|
+
* @property {number} maxlength - The maximum characters allowed in the input.
|
|
24
|
+
* @property {string} placeholder - The placeholder value.
|
|
25
|
+
* @property {string} ariaLabelInput - aria-label for input.
|
|
26
|
+
* @property {string} ariaLabelButton - aria-label for button.
|
|
27
|
+
* @property {boolean} borderless - Whether the input has a border or not.
|
|
28
|
+
* @returns {object} The map of properties.
|
|
29
|
+
*/
|
|
30
|
+
static properties = {
|
|
31
|
+
disabled: { type: Boolean, reflect: true },
|
|
32
|
+
validation: { type: String, reflect: true },
|
|
33
|
+
label: { type: String },
|
|
34
|
+
name: { type: String },
|
|
35
|
+
title: { type: String, attribute: true },
|
|
36
|
+
value: { type: String },
|
|
37
|
+
maxlength: { type: Number, reflect: true },
|
|
38
|
+
placeholder: { type: String },
|
|
39
|
+
ariaLabelInput: { type: String, attribute: 'aria-label-input' },
|
|
40
|
+
ariaLabelButton: { type: String, attribute: 'aria-label-button' },
|
|
41
|
+
borderless: { type: Boolean, reflect: true },
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
#searchInput;
|
|
45
|
+
|
|
46
|
+
constructor() {
|
|
47
|
+
super();
|
|
48
|
+
|
|
49
|
+
this.label = 'Search';
|
|
50
|
+
this.name = '';
|
|
51
|
+
this.title = 'Search';
|
|
52
|
+
this.value = '';
|
|
53
|
+
this.maxlength = 75;
|
|
54
|
+
this.placeholder = '';
|
|
55
|
+
this.ariaLabelInput = 'Search input';
|
|
56
|
+
this.ariaLabelButton = 'Clear search';
|
|
57
|
+
this.disabled = false;
|
|
58
|
+
this.#searchInput = createRef();
|
|
59
|
+
|
|
60
|
+
// Borderless is only used for CSS.
|
|
61
|
+
this.borderless = false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
#onInput(evt) {
|
|
65
|
+
this.value = evt.target.value;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
#onBlur() {
|
|
69
|
+
this.dispatchEvent(
|
|
70
|
+
new Event('blur', {
|
|
71
|
+
bubbles: true,
|
|
72
|
+
composed: true,
|
|
73
|
+
}),
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
#onClickClear(evt) {
|
|
78
|
+
evt.preventDefault();
|
|
79
|
+
if (this.disabled) return;
|
|
80
|
+
this.value = '';
|
|
81
|
+
this.#searchInput.value?.focus();
|
|
82
|
+
this.dispatchEvent(
|
|
83
|
+
new CustomEvent('clear', { detail: '', bubbles: true, composed: true }),
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
focus() {
|
|
88
|
+
this.#searchInput.value.focus();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
render() {
|
|
92
|
+
return html`
|
|
93
|
+
<div
|
|
94
|
+
class="o-search-input ${this.validation
|
|
95
|
+
? `o-search-input--${this.validation}`
|
|
96
|
+
: ''}"
|
|
97
|
+
>
|
|
98
|
+
<label
|
|
99
|
+
for="search-text"
|
|
100
|
+
class="o-search-input__input-label"
|
|
101
|
+
aria-label=${this.label}
|
|
102
|
+
>
|
|
103
|
+
${unsafeSVG(searchIcon)}
|
|
104
|
+
</label>
|
|
105
|
+
<input
|
|
106
|
+
id="search-text"
|
|
107
|
+
type="search"
|
|
108
|
+
name=${this.name}
|
|
109
|
+
.value=${this.value}
|
|
110
|
+
?disabled=${this.disabled}
|
|
111
|
+
class="a-text-input a-text-input__full"
|
|
112
|
+
placeholder=${this.placeholder}
|
|
113
|
+
title=${this.title}
|
|
114
|
+
autocomplete="off"
|
|
115
|
+
maxlength=${this.maxlength}
|
|
116
|
+
aria-label=${this.ariaLabelInput}
|
|
117
|
+
${ref(this.#searchInput)}
|
|
118
|
+
@input=${this.#onInput}
|
|
119
|
+
@blur=${this.#onBlur}
|
|
120
|
+
/>
|
|
121
|
+
<button
|
|
122
|
+
type="reset"
|
|
123
|
+
aria-label=${this.ariaLabelButton}
|
|
124
|
+
title=${this.ariaLabelButton}
|
|
125
|
+
@click=${this.#onClickClear}
|
|
126
|
+
>
|
|
127
|
+
${unsafeSVG(clearIcon)}
|
|
128
|
+
</button>
|
|
129
|
+
</div>
|
|
130
|
+
`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
static init() {
|
|
134
|
+
window.customElements.get('cfpb-form-search-input') ||
|
|
135
|
+
window.customElements.define(
|
|
136
|
+
'cfpb-form-search-input',
|
|
137
|
+
CfpbFormSearchInput,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -1,60 +1,54 @@
|
|
|
1
1
|
@use 'sass:math';
|
|
2
|
-
@use '@cfpb/cfpb-design-system/src/abstracts' as *;
|
|
2
|
+
@use '@cfpb/cfpb-design-system/src/elements/abstracts' as *;
|
|
3
3
|
|
|
4
4
|
@mixin u-btn-divider() {
|
|
5
5
|
content: '';
|
|
6
6
|
border-left: 1px solid var(--icon-text-divider);
|
|
7
|
-
order: 2;
|
|
8
7
|
place-self: normal;
|
|
9
8
|
}
|
|
10
9
|
|
|
11
10
|
:host {
|
|
12
11
|
// Theme variables.
|
|
13
|
-
--icon-text-divider: var(--pacific-60);
|
|
12
|
+
--icon-text-divider-default: var(--pacific-60);
|
|
13
|
+
--icon-text-divider-disabled: var(--gray-60);
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
// Private variables.
|
|
16
|
+
--icon-text-divider: var(--icon-text-divider-default);
|
|
17
|
+
|
|
18
|
+
.wrapper {
|
|
16
19
|
// This prevents the child button from having an empty gap after the button.
|
|
17
20
|
display: flex;
|
|
18
21
|
width: fit-content;
|
|
19
22
|
align-items: center;
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
// Show SVG.
|
|
33
|
-
& ::slotted(svg) {
|
|
34
|
-
display: initial;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
&.u-has-icon--left {
|
|
39
|
-
& ::slotted(svg) {
|
|
40
|
-
order: 1;
|
|
41
|
-
}
|
|
42
|
-
& ::slotted(span) {
|
|
43
|
-
order: 3;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
&.u-has-icon--right {
|
|
48
|
-
& ::slotted(svg) {
|
|
49
|
-
order: 3;
|
|
50
|
-
}
|
|
51
|
-
& ::slotted(span) {
|
|
52
|
-
order: 1;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
24
|
+
gap: math.div(10px, $btn-font-size) + rem;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.left-divider::before,
|
|
28
|
+
.right-divider::after {
|
|
29
|
+
@include u-btn-divider;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Shine the slot contents through to the wrapper's flexbox items.
|
|
33
|
+
slot {
|
|
34
|
+
display: contents;
|
|
55
35
|
}
|
|
56
36
|
}
|
|
57
37
|
|
|
38
|
+
.left-divider ::slotted(svg:first-of-type) {
|
|
39
|
+
order: -1;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.right-divider ::slotted(svg:last-of-type) {
|
|
43
|
+
order: 1;
|
|
44
|
+
}
|
|
45
|
+
|
|
58
46
|
:host([disabled]) {
|
|
59
|
-
|
|
47
|
+
// !important is set so that disabled divider color overrides all other color.
|
|
48
|
+
--icon-text-divider: var(--icon-text-divider-disabled) !important;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Hide SVG if user calls hideIcon()
|
|
52
|
+
:host([icon-hidden]) ::slotted(svg) {
|
|
53
|
+
display: none !important;
|
|
60
54
|
}
|
|
@@ -2,7 +2,6 @@ import { html, LitElement, css, unsafeCSS } from 'lit';
|
|
|
2
2
|
import styles from './cfpb-icon-text.component.scss';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
5
|
* @element cfpb-icon-text
|
|
7
6
|
* @slot - The main content for the text and icon.
|
|
8
7
|
*/
|
|
@@ -15,132 +14,61 @@ export class CfpbIconText extends LitElement {
|
|
|
15
14
|
* @property {boolean} disabled - Apply disabled styles or not.
|
|
16
15
|
* @returns {object} The map of properties.
|
|
17
16
|
*/
|
|
18
|
-
static
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// DOM references.
|
|
25
|
-
#svgObserver;
|
|
26
|
-
#iconClasses;
|
|
17
|
+
static properties = {
|
|
18
|
+
disabled: { type: Boolean, reflect: true },
|
|
19
|
+
iconHidden: { type: Boolean, reflect: true, attribute: 'icon-hidden' },
|
|
20
|
+
};
|
|
27
21
|
|
|
28
22
|
constructor() {
|
|
29
23
|
super();
|
|
30
|
-
this
|
|
24
|
+
this.disabled = false;
|
|
25
|
+
this.iconHidden = false;
|
|
31
26
|
}
|
|
32
27
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
this.#svgObserver = new MutationObserver(() => {
|
|
37
|
-
this.#processLightDom();
|
|
38
|
-
});
|
|
28
|
+
firstUpdated() {
|
|
29
|
+
const slot = this.shadowRoot.querySelector('slot');
|
|
30
|
+
this.#updateDividers();
|
|
39
31
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
subtree: false,
|
|
43
|
-
});
|
|
32
|
+
// Handle dynamically added/removed nodes.
|
|
33
|
+
slot.addEventListener('slotchange', () => this.#updateDividers());
|
|
44
34
|
}
|
|
45
35
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
this.#svgObserver.disconnect();
|
|
50
|
-
this.#svgObserver = null;
|
|
36
|
+
updated(changedProps) {
|
|
37
|
+
if (changedProps.has('iconHidden')) {
|
|
38
|
+
this.#updateDividers();
|
|
51
39
|
}
|
|
52
40
|
}
|
|
53
41
|
|
|
54
|
-
#
|
|
55
|
-
const
|
|
42
|
+
#updateDividers() {
|
|
43
|
+
const wrapper = this.shadowRoot.querySelector('.wrapper');
|
|
56
44
|
const slot = this.shadowRoot.querySelector('slot');
|
|
57
|
-
const nodes = slot.assignedNodes({ flatten: true })
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
45
|
+
const nodes = slot.assignedNodes({ flatten: true }).filter((node) => {
|
|
46
|
+
return (
|
|
47
|
+
node.nodeType === Node.ELEMENT_NODE ||
|
|
48
|
+
(node.nodeType === Node.TEXT_NODE && node.textContent.trim())
|
|
49
|
+
);
|
|
50
|
+
});
|
|
61
51
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
)
|
|
67
|
-
const span = document.createElement('span');
|
|
68
|
-
span.textContent = node.textContent;
|
|
69
|
-
node.replaceWith(span);
|
|
70
|
-
if (!spanEl) spanEl = span;
|
|
71
|
-
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
72
|
-
const tag = node.tagName.toLowerCase();
|
|
73
|
-
if (tag === 'svg' && !svgEl) {
|
|
74
|
-
svgEl = node;
|
|
75
|
-
} else if (tag === 'span' && !spanEl) {
|
|
76
|
-
spanEl = node;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
52
|
+
const showLeft =
|
|
53
|
+
!this.iconHidden && nodes[0]?.tagName?.toLowerCase() === 'svg';
|
|
54
|
+
const showRight =
|
|
55
|
+
!this.iconHidden &&
|
|
56
|
+
nodes[nodes.length - 1]?.tagName?.toLowerCase() === 'svg';
|
|
80
57
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (
|
|
84
|
-
svgEl.compareDocumentPosition(spanEl) & Node.DOCUMENT_POSITION_FOLLOWING
|
|
85
|
-
) {
|
|
86
|
-
div.classList.add('u-has-icon--left');
|
|
87
|
-
} else {
|
|
88
|
-
div.classList.add('u-has-icon--right');
|
|
89
|
-
}
|
|
90
|
-
}
|
|
58
|
+
wrapper.classList.toggle('left-divider', showLeft);
|
|
59
|
+
wrapper.classList.toggle('right-divider', showRight);
|
|
91
60
|
}
|
|
92
61
|
|
|
93
|
-
firstUpdated() {
|
|
94
|
-
this.#processLightDom();
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Hide any icon in the slot.
|
|
99
|
-
*/
|
|
100
62
|
hideIcon() {
|
|
101
|
-
|
|
102
|
-
const div = this.shadowRoot.querySelector('div');
|
|
103
|
-
if (icon) {
|
|
104
|
-
this.#iconClasses = div.className;
|
|
105
|
-
div.className = '';
|
|
106
|
-
}
|
|
63
|
+
this.iconHidden = true;
|
|
107
64
|
}
|
|
108
65
|
|
|
109
|
-
/**
|
|
110
|
-
* Show any icon in the slot, if it was hidden.
|
|
111
|
-
*/
|
|
112
66
|
showIcon() {
|
|
113
|
-
|
|
114
|
-
const div = this.shadowRoot.querySelector('div');
|
|
115
|
-
if (icon) div.className = this.#iconClasses;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* @returns {boolean} True if it has an icon, false otherwise.
|
|
120
|
-
*/
|
|
121
|
-
hasIcon() {
|
|
122
|
-
const icon = this.#findIconInSlot();
|
|
123
|
-
if (icon) return true;
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Find the icon SVG in the slot.
|
|
129
|
-
* @returns {Node} The icon SVG node.
|
|
130
|
-
*/
|
|
131
|
-
#findIconInSlot() {
|
|
132
|
-
const slot = this.shadowRoot.querySelector('slot');
|
|
133
|
-
const nodes = slot.assignedNodes({ flatten: true });
|
|
134
|
-
|
|
135
|
-
for (const node of nodes) {
|
|
136
|
-
if (node.tagName && node.tagName.toLowerCase() === 'svg') {
|
|
137
|
-
return node;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
67
|
+
this.iconHidden = false;
|
|
140
68
|
}
|
|
141
69
|
|
|
142
70
|
render() {
|
|
143
|
-
return html`<
|
|
71
|
+
return html`<span class="wrapper"><slot></slot></span>`;
|
|
144
72
|
}
|
|
145
73
|
|
|
146
74
|
static init() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
@use 'sass:math';
|
|
2
|
-
@use '@cfpb/cfpb-design-system/src/abstracts' as *;
|
|
2
|
+
@use '@cfpb/cfpb-design-system/src/elements/abstracts' as *;
|
|
3
3
|
|
|
4
4
|
:host {
|
|
5
5
|
.a-label {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
&__helper {
|
|
11
|
-
color:
|
|
11
|
+
color: var(--label-helper);
|
|
12
12
|
font-size: math.div(16px, $base-font-size-px) + rem;
|
|
13
13
|
font-weight: normal;
|
|
14
14
|
|
|
@@ -14,17 +14,14 @@ export class CfpbLabel extends LitElement {
|
|
|
14
14
|
`;
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* @property {string} for - Associate the label with an ID elsewhere.
|
|
18
17
|
* @property {boolean} block - Whether this has block or inline helper text.
|
|
18
|
+
* @property {string} for - Associate the label with an ID elsewhere.
|
|
19
19
|
* @returns {object} The map of properties.
|
|
20
20
|
*/
|
|
21
|
-
static
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
for: { type: String },
|
|
26
|
-
};
|
|
27
|
-
}
|
|
21
|
+
static properties = {
|
|
22
|
+
block: { type: Boolean, reflect: true },
|
|
23
|
+
for: { type: String },
|
|
24
|
+
};
|
|
28
25
|
|
|
29
26
|
constructor() {
|
|
30
27
|
super();
|
|
@@ -48,7 +45,7 @@ export class CfpbLabel extends LitElement {
|
|
|
48
45
|
for=${ifDefined(this.for && this.for.trim() ? this.for : undefined)}
|
|
49
46
|
>
|
|
50
47
|
<slot name="label"></slot>
|
|
51
|
-
<small class
|
|
48
|
+
<small class=${this.#helperClass}>
|
|
52
49
|
<slot name="helper"></slot>
|
|
53
50
|
</small>
|
|
54
51
|
</label>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
@use 'sass:math';
|
|
2
|
+
@use '@cfpb/cfpb-design-system/src/elements/abstracts' as *;
|
|
3
|
+
@use '@cfpb/cfpb-design-system/src/components/cfpb-icons/icon';
|
|
4
|
+
|
|
5
|
+
:host {
|
|
6
|
+
::slotted(cfpb-list-item) {
|
|
7
|
+
position: relative;
|
|
8
|
+
margin-top: 1px;
|
|
9
|
+
|
|
10
|
+
&::before,
|
|
11
|
+
&::after {
|
|
12
|
+
position: absolute;
|
|
13
|
+
content: '';
|
|
14
|
+
display: block;
|
|
15
|
+
width: 100%;
|
|
16
|
+
height: 1px;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
&::before {
|
|
20
|
+
top: -1px;
|
|
21
|
+
border-top: 1px solid var(--gray-20);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&::after {
|
|
25
|
+
bottom: -1px;
|
|
26
|
+
border-bottom: 1px solid var(--gray-20);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
:focus {
|
|
31
|
+
outline: 1px dotted var(--pacific);
|
|
32
|
+
outline-offset: 2px;
|
|
33
|
+
}
|
|
34
|
+
}
|