@operato/form 1.0.0-alpha.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.
Files changed (61) hide show
  1. package/.editorconfig +29 -0
  2. package/.storybook/main.js +3 -0
  3. package/.storybook/server.mjs +8 -0
  4. package/CHANGELOG.md +11 -0
  5. package/LICENSE +21 -0
  6. package/README.md +75 -0
  7. package/demo/index.html +33 -0
  8. package/dist/src/components/ox-custom-input.d.ts +23 -0
  9. package/dist/src/components/ox-custom-input.js +114 -0
  10. package/dist/src/components/ox-custom-input.js.map +1 -0
  11. package/dist/src/components/ox-custom-select.d.ts +21 -0
  12. package/dist/src/components/ox-custom-select.js +100 -0
  13. package/dist/src/components/ox-custom-select.js.map +1 -0
  14. package/dist/src/components/ox-file-uploader.d.ts +13 -0
  15. package/dist/src/components/ox-file-uploader.js +166 -0
  16. package/dist/src/components/ox-file-uploader.js.map +1 -0
  17. package/dist/src/components/ox-form-master.d.ts +1 -0
  18. package/dist/src/components/ox-form-master.js +208 -0
  19. package/dist/src/components/ox-form-master.js.map +1 -0
  20. package/dist/src/components/ox-form-paginator.d.ts +9 -0
  21. package/dist/src/components/ox-form-paginator.js +70 -0
  22. package/dist/src/components/ox-form-paginator.js.map +1 -0
  23. package/dist/src/components/ox-search-form-paginator.d.ts +1 -0
  24. package/dist/src/components/ox-search-form-paginator.js +51 -0
  25. package/dist/src/components/ox-search-form-paginator.js.map +1 -0
  26. package/dist/src/components/ox-search-form.d.ts +56 -0
  27. package/dist/src/components/ox-search-form.js +284 -0
  28. package/dist/src/components/ox-search-form.js.map +1 -0
  29. package/dist/src/index.d.ts +3 -0
  30. package/dist/src/index.js +4 -0
  31. package/dist/src/index.js.map +1 -0
  32. package/dist/src/styles/index.d.ts +3 -0
  33. package/dist/src/styles/index.js +4 -0
  34. package/dist/src/styles/index.js.map +1 -0
  35. package/dist/src/styles/multi-column-form-styles.d.ts +1 -0
  36. package/dist/src/styles/multi-column-form-styles.js +192 -0
  37. package/dist/src/styles/multi-column-form-styles.js.map +1 -0
  38. package/dist/src/styles/search-form-styles.d.ts +1 -0
  39. package/dist/src/styles/search-form-styles.js +156 -0
  40. package/dist/src/styles/search-form-styles.js.map +1 -0
  41. package/dist/src/styles/single-column-form-styles.d.ts +1 -0
  42. package/dist/src/styles/single-column-form-styles.js +114 -0
  43. package/dist/src/styles/single-column-form-styles.js.map +1 -0
  44. package/dist/tsconfig.tsbuildinfo +1 -0
  45. package/package.json +77 -0
  46. package/src/components/ox-custom-input.ts +102 -0
  47. package/src/components/ox-custom-select.ts +90 -0
  48. package/src/components/ox-file-uploader.ts +163 -0
  49. package/src/components/ox-form-master.ts +241 -0
  50. package/src/components/ox-form-paginator.ts +60 -0
  51. package/src/components/ox-search-form-paginator.ts +46 -0
  52. package/src/components/ox-search-form.ts +317 -0
  53. package/src/index.ts +3 -0
  54. package/src/styles/index.ts +3 -0
  55. package/src/styles/multi-column-form-styles.ts +192 -0
  56. package/src/styles/search-form-styles.ts +156 -0
  57. package/src/styles/single-column-form-styles.ts +114 -0
  58. package/src/themes/form-theme.css +75 -0
  59. package/tsconfig.json +23 -0
  60. package/web-dev-server.config.mjs +27 -0
  61. package/web-test-runner.config.mjs +41 -0
@@ -0,0 +1,317 @@
1
+ import '@material/mwc-icon'
2
+ import './ox-custom-input.js'
3
+ import './ox-custom-select.js'
4
+
5
+ import { LitElement, PropertyValues, css, html } from 'lit'
6
+ import { buildArgs, client } from '@operato/graphql'
7
+ import { customElement, property, query, queryAll } from 'lit/decorators.js'
8
+
9
+ import { SearchFormStyles } from '../styles/search-form-styles.js'
10
+ import gql from 'graphql-tag'
11
+
12
+ @customElement('ox-search-form')
13
+ export class SearchForm extends LitElement {
14
+ static styles = [
15
+ SearchFormStyles,
16
+ css`
17
+ :host {
18
+ display: flex;
19
+ flex-direction: column;
20
+
21
+ position: relative;
22
+ }
23
+
24
+ ::placeholder {
25
+ text-transform: capitalize;
26
+ color: var(--theme-white-color);
27
+ }
28
+ `
29
+ ]
30
+
31
+ @property({ type: Array }) fields?: {
32
+ type: string
33
+ hidden: boolean
34
+ name: string
35
+ id: string
36
+ label: string
37
+ placeholder: string
38
+ value: any
39
+ field: string
40
+ queryName: string
41
+ props: { [prop: string]: string }
42
+ attrs: string[]
43
+ options: { name: string; value: string }[]
44
+ handlers: { [event: string]: EventListener }
45
+ }[]
46
+ @property({ type: Boolean }) hideSearchButton: boolean = false
47
+
48
+ @query('form') form!: HTMLFormElement
49
+ @queryAll('input, select') formFields!: ((HTMLInputElement | HTMLSelectElement) & { field: string; _value: any })[]
50
+
51
+ /* @deprecated */
52
+ getFields() {
53
+ return Array.from(this.form.querySelectorAll('input, select') || [])
54
+ }
55
+
56
+ render() {
57
+ return html`
58
+ <form
59
+ class="search-form"
60
+ @keypress=${(e: KeyboardEvent) => {
61
+ if (e.keyCode === 13) {
62
+ e.preventDefault()
63
+ this.submit()
64
+ }
65
+ }}
66
+ >
67
+ ${(this.fields || []).map(
68
+ field => html`
69
+ ${field.type === 'select'
70
+ ? html`
71
+ <label ?hidden=${field.hidden}>${field.label || field.name || field.id}</label>
72
+ <select .value=${field.value} .name=${field.name} ?hidden=${field.hidden}>
73
+ ${(field.options || []).map(
74
+ (option: { name: string; value: string }) => html`
75
+ <option ?selected=${option.value == field.value} value=${option.value}>${option.name}</option>
76
+ `
77
+ )}
78
+ </select>
79
+ `
80
+ : field.type === 'checkbox'
81
+ ? html`
82
+ <input
83
+ id="ckbx_${field.name}"
84
+ type="checkbox"
85
+ ?checked="${field.value}"
86
+ .name=${field.name}
87
+ ?hidden=${field.hidden}
88
+ @click=${(e: MouseEvent) => {
89
+ const checkbox = e.currentTarget as HTMLInputElement & { _value: any }
90
+ if (checkbox.hasAttribute('indeterminate')) {
91
+ const values = [undefined, 'on', 'off']
92
+ const currentValue = checkbox._value
93
+ const newValue =
94
+ values.indexOf(currentValue) + 1 > values.length - 1
95
+ ? values[0]
96
+ : values[values.indexOf(currentValue) + 1]
97
+
98
+ if (newValue === 'on') {
99
+ checkbox.checked = true
100
+ } else if (newValue === 'off') {
101
+ checkbox.checked = false
102
+ } else {
103
+ checkbox.indeterminate = true
104
+ }
105
+ checkbox._value = newValue
106
+ }
107
+ }}
108
+ />
109
+ <label for="ckbx_${field.name}" ?hidden=${field.hidden}
110
+ >${field.label || field.name || field.id}</label
111
+ >
112
+ `
113
+ : field.type === 'object'
114
+ ? html`
115
+ <label ?hidden=${field.hidden}>${field.label || field.name || field.id}</label>
116
+ <input
117
+ .type=${field.type}
118
+ .value=${field.value || ''}
119
+ .name=${field.name}
120
+ .field=${field.field}
121
+ .queryName=${field.queryName}
122
+ ?hidden=${field.hidden}
123
+ />
124
+ `
125
+ : html`
126
+ <label ?hidden=${field.hidden}>${field.label || field.name || field.id}</label>
127
+ <input
128
+ .type=${field.type}
129
+ .value=${field.value || ''}
130
+ .name=${field.name}
131
+ ?hidden=${field.hidden}
132
+ .placeholder=${field.placeholder || ''}
133
+ />
134
+ `}
135
+ `
136
+ )}
137
+ ${this.hideSearchButton || false
138
+ ? ``
139
+ : html`<mwc-icon @click=${(e: MouseEvent) => this.submit()} search>search</mwc-icon>`}
140
+ </form>
141
+ `
142
+ }
143
+
144
+ async updated(changes: PropertyValues<this>) {
145
+ if (changes.has('fields')) {
146
+ this._checkInputValidity()
147
+
148
+ var formFields = this.formFields
149
+ this.fields &&
150
+ this.fields.forEach(field => {
151
+ var formField = formFields.find(f => f.name == field.name)
152
+ if (field.props instanceof Object && !Array.isArray(field.props)) {
153
+ for (let prop in field.props) {
154
+ if (field.props[prop]) {
155
+ formField!.setAttribute(prop, field.props[prop])
156
+ }
157
+ }
158
+ }
159
+
160
+ if (field.attrs && Array.isArray(field.attrs)) {
161
+ field.attrs.forEach(attr => {
162
+ if (attr === 'indeterminate') {
163
+ ;(formField as HTMLInputElement).indeterminate = true
164
+ } else {
165
+ formField!.setAttribute(attr, '')
166
+ }
167
+ })
168
+ }
169
+
170
+ if (field.handlers instanceof Object && !Array.isArray(field.handlers)) {
171
+ for (let eventName in field.handlers) {
172
+ formField!.addEventListener(eventName, field.handlers[eventName])
173
+ }
174
+ }
175
+ })
176
+ }
177
+ }
178
+
179
+ _checkInputValidity() {
180
+ const names = this.fields?.map(i => i.name) || []
181
+ const result = names.every(name => {
182
+ return names.indexOf(name) === names.lastIndexOf(name)
183
+ })
184
+ if (!result) {
185
+ throw new Error('Field name is duplicated.')
186
+ }
187
+ }
188
+
189
+ get searchParams() {
190
+ let searchParam = new URLSearchParams()
191
+ const fields = this.formFields
192
+ fields.forEach(field => searchParam.append(field.name, field.value))
193
+
194
+ return decodeURI(searchParam.toString())
195
+ }
196
+
197
+ get queryFilters() {
198
+ return this.formFields
199
+ .filter(
200
+ field =>
201
+ (field.type !== 'checkbox' && field.value && field.value !== '') ||
202
+ (field.type === 'checkbox' && !field.hasAttribute('indeterminate')) ||
203
+ (field.type === 'checkbox' && field.hasAttribute('indeterminate') && field._value)
204
+ )
205
+ .map(field => {
206
+ const operator = field.getAttribute('searchOper') || ''
207
+ let value = operator.indexOf('like') >= 0 ? `%${field.value}%` : field.value
208
+ return {
209
+ name: field.name,
210
+ value:
211
+ field.type === 'text'
212
+ ? value
213
+ : field.type === 'checkbox'
214
+ ? field.hasAttribute('indeterminate')
215
+ ? field._value === 'on'
216
+ ? true
217
+ : false
218
+ : (field as HTMLInputElement).checked
219
+ : field.type === 'number'
220
+ ? parseFloat(value)
221
+ : value,
222
+ operator
223
+ }
224
+ })
225
+ }
226
+
227
+ public async getQueryFilters() {
228
+ return await Promise.all(
229
+ this.formFields
230
+ .filter(
231
+ field =>
232
+ (field.type !== 'checkbox' && field.value && field.value !== '') ||
233
+ (field.type === 'checkbox' && !field.hasAttribute('indeterminate')) ||
234
+ (field.type === 'checkbox' && field.hasAttribute('indeterminate') && field._value)
235
+ )
236
+ .map(async field => {
237
+ const name = field.name
238
+ const operator = (field.type === 'text' && field.field ? 'in' : field.getAttribute('searchOper')) || ''
239
+ const value = operator.indexOf('like') >= 0 ? `%${field.value}%` : field.value
240
+ const filter = {
241
+ name,
242
+ operator,
243
+ value:
244
+ field.type === 'text' && field.getAttribute('type') === 'object'
245
+ ? await this._getResourceIds(field)
246
+ : field.type === 'text'
247
+ ? value
248
+ : field.type === 'checkbox'
249
+ ? field.hasAttribute('indeterminate')
250
+ ? field._value === 'on'
251
+ ? true
252
+ : false
253
+ : (field as HTMLInputElement).checked
254
+ : field.type === 'number'
255
+ ? parseFloat(value)
256
+ : value,
257
+ relation: field.type === 'text' && field.getAttribute('type') === 'object'
258
+ }
259
+ return filter
260
+ })
261
+ )
262
+ }
263
+
264
+ private async _getResourceIds(inputField: HTMLInputElement | HTMLSelectElement) {
265
+ const {
266
+ field = 'name',
267
+ value,
268
+ queryName
269
+ } = inputField as (HTMLInputElement | HTMLSelectElement) & { field: string; queryName: string }
270
+
271
+ const response = await client.query({
272
+ query: gql`
273
+ query {
274
+ fetch: ${queryName}(${buildArgs({
275
+ filters: [
276
+ {
277
+ name: field,
278
+ operator: 'i_like',
279
+ value: `%${value}%`
280
+ }
281
+ ]
282
+ })}) {
283
+ items {
284
+ id
285
+ }
286
+ }
287
+ }
288
+ `
289
+ })
290
+
291
+ if (!response.errors) {
292
+ const items = response.data.fetch.items as { id: string }[]
293
+ return (items && items.map(item => item.id)) || []
294
+ }
295
+ }
296
+
297
+ serialize() {
298
+ let data: { [name: string]: any } = {}
299
+
300
+ this.formFields.forEach(field => {
301
+ const name = field.name
302
+ if (field.type === 'number') {
303
+ data[name] = parseFloat(field.value)
304
+ } else if (field.type === 'checkbox') {
305
+ data[name] = (field as HTMLInputElement).checked
306
+ } else {
307
+ data[name] = field.value
308
+ }
309
+ })
310
+
311
+ return data
312
+ }
313
+
314
+ submit() {
315
+ this.dispatchEvent(new CustomEvent('submit'))
316
+ }
317
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './styles'
2
+ export * from './components/ox-search-form'
3
+ export * from './components/ox-file-uploader'
@@ -0,0 +1,3 @@
1
+ export * from './single-column-form-styles'
2
+ export * from './multi-column-form-styles'
3
+ export * from './search-form-styles'
@@ -0,0 +1,192 @@
1
+ import { css } from 'lit'
2
+
3
+ export const MultiColumnFormStyles = css`
4
+ :host {
5
+ overflow: auto;
6
+ padding: var(--form-container-padding);
7
+ }
8
+
9
+ .multi-column-form {
10
+ display: grid;
11
+ grid-template-columns: repeat(24, 1fr);
12
+ grid-gap: var(--form-grid-gap);
13
+ grid-auto-rows: minmax(24px, auto);
14
+ max-width: var(--form-multi-column-max-width);
15
+ margin: var(--form-margin);
16
+ }
17
+ .multi-column-form fieldset {
18
+ display: contents;
19
+ }
20
+ .multi-column-form legend {
21
+ grid-column: span 24;
22
+ text-transform: capitalize;
23
+
24
+ padding: var(--legend-padding);
25
+ font: var(--legend-font);
26
+ color: var(--legend-text-color);
27
+ border-bottom: var(--legend-border-bottom);
28
+ }
29
+
30
+ .multi-column-form label {
31
+ grid-column: span 3;
32
+ text-align: right;
33
+ text-transform: capitalize;
34
+ align-self: center;
35
+
36
+ color: var(--label-color);
37
+ font: var(--label-font);
38
+ }
39
+
40
+ .multi-column-form input,
41
+ .multi-column-form table,
42
+ .multi-column-form select,
43
+ .multi-column-form textarea,
44
+ .multi-column-form [custom-input] {
45
+ grid-column: span 9;
46
+
47
+ border: var(--input-field-border);
48
+ border-radius: var(--input-field-border-radius);
49
+ padding: var(--input-field-padding);
50
+ font: var(--input-field-font);
51
+ max-width: 85%;
52
+ }
53
+ .multi-column-form select {
54
+ min-height: 25px;
55
+ max-width: calc(85% + 18px);
56
+ }
57
+
58
+ .multi-column-form input[type='checkbox'],
59
+ .multi-column-form input[type='radio'] {
60
+ justify-self: end;
61
+ align-self: center;
62
+ grid-column: span 3 / auto;
63
+ position: relative;
64
+ left: 17px;
65
+ }
66
+
67
+ .multi-column-form input[type='checkbox'] + label,
68
+ .multi-column-form input[type='radio'] + label {
69
+ padding-left: 17px;
70
+ text-align: left;
71
+ align-self: center;
72
+ grid-column: span 9 / auto;
73
+
74
+ font: var(--form-sublabel-font);
75
+ color: var(--form-sublabel-color);
76
+ }
77
+ .multi-column-form [options-wrap] {
78
+ grid-column: span 21 / auto;
79
+ }
80
+
81
+ input:focus {
82
+ outline: none;
83
+ border: 1px solid var(--focus-background-color);
84
+ }
85
+ input[type='checkbox'] {
86
+ margin: 0;
87
+ }
88
+
89
+ @media screen and (max-width: 480px) {
90
+ .multi-column-form {
91
+ max-width: 100%;
92
+ grid-template-columns: repeat(12, 1fr);
93
+ grid-gap: 5px;
94
+ }
95
+ .multi-column-form legend {
96
+ grid-column: span 12;
97
+ }
98
+ .multi-column-form label {
99
+ grid-column: span 4 / auto;
100
+ text-align: left;
101
+ text-overflow: ellipsis;
102
+ white-space: nowrap;
103
+ overflow: hidden;
104
+ }
105
+ .multi-column-form input,
106
+ .multi-column-form table,
107
+ .multi-column-form select,
108
+ .multi-column-form textarea,
109
+ .multi-column-form [custom-input] {
110
+ grid-column: span 8 / auto;
111
+ max-width: initial;
112
+ padding: var(--padding-default);
113
+ font: normal 15px var(--theme-font);
114
+ }
115
+ .multi-column-form input[type='checkbox'],
116
+ .multi-column-form input[type='radio'] {
117
+ justify-self: left;
118
+ align-self: center;
119
+ grid-column: span 1 / auto;
120
+ left: 0;
121
+ zoom: 1.4;
122
+ }
123
+
124
+ .multi-column-form input[type='checkbox'] + label,
125
+ .multi-column-form input[type='radio'] + label {
126
+ grid-column: span 11 / auto;
127
+ align-self: center;
128
+ position: relative;
129
+ left: -25px;
130
+ }
131
+ .multi-column-form [options-wrap] {
132
+ grid-column: span 12 / auto;
133
+ display: grid;
134
+ grid-gap: var(--padding-default) var(--padding-narrow);
135
+ grid-template-columns: repeat(12, 1fr);
136
+ }
137
+ .multi-column-form [options-wrap] input[type='checkbox'] + label,
138
+ .multi-column-form [options-wrap] input[type='radio'] + label {
139
+ grid-column: span 5 / auto;
140
+ align-self: left;
141
+ left: -4%;
142
+ margin-right: 0;
143
+ }
144
+ }
145
+ @media (min-width: 481px) and (max-width: 1024px) {
146
+ .multi-column-form select {
147
+ max-width: calc(85% + 10px);
148
+ }
149
+ }
150
+ @media screen and (min-width: 1201px) and (max-width: 2000px) {
151
+ .multi-column-form {
152
+ grid-template-columns: repeat(36, 1fr);
153
+ }
154
+ .multi-column-form legend {
155
+ grid-column: span 36;
156
+ }
157
+ .multi-column-form input,
158
+ .multi-column-form table,
159
+ .multi-column-form select,
160
+ .multi-column-form textarea,
161
+ .multi-column-form [custom-input] {
162
+ max-width: 90%;
163
+ }
164
+ .multi-column-form select {
165
+ max-width: calc(90% + 18px);
166
+ }
167
+ .multi-column-form [options-wrap] {
168
+ grid-column: span 33 / auto;
169
+ }
170
+ }
171
+ @media screen and (min-width: 2001px) {
172
+ .multi-column-form {
173
+ grid-template-columns: repeat(48, 1fr);
174
+ }
175
+ .multi-column-form legend {
176
+ grid-column: span 48;
177
+ }
178
+ .multi-column-form input,
179
+ .multi-column-form table,
180
+ .multi-column-form select,
181
+ .multi-column-form textarea,
182
+ .multi-column-form [custom-input] {
183
+ max-width: 90%;
184
+ }
185
+ .multi-column-form select {
186
+ max-width: calc(90% + 18px);
187
+ }
188
+ .multi-column-form [options-wrap] {
189
+ grid-column: span 45 / auto;
190
+ }
191
+ }
192
+ `
@@ -0,0 +1,156 @@
1
+ import { css } from 'lit'
2
+
3
+ export const SearchFormStyles = css`
4
+ :host {
5
+ overflow: auto;
6
+ background-color: var(--search-form-background-color);
7
+ }
8
+
9
+ .search-form {
10
+ position: relative;
11
+ display: grid;
12
+ grid-template-columns: repeat(24, 1fr);
13
+ grid-gap: var(--form-grid-gap);
14
+ grid-auto-rows: minmax(24px, auto);
15
+ padding: var(--search-form-box-padding);
16
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
17
+ }
18
+ .search-form label {
19
+ grid-column: span 3;
20
+ text-align: right;
21
+ align-self: center;
22
+ text-transform: capitalize;
23
+
24
+ color: var(--label-color);
25
+ font: var(--label-font);
26
+ }
27
+
28
+ .search-form input,
29
+ .search-form select {
30
+ grid-column: span 9;
31
+
32
+ border: var(--input-field-border);
33
+ border-radius: var(--input-field-border-radius);
34
+ padding: var(--input-field-padding);
35
+ font: var(--input-field-font);
36
+ max-width: 85%;
37
+ color: var(--input-field-color);
38
+ background-color: var(--input-field-background-color);
39
+ }
40
+
41
+ .search-form select {
42
+ min-height: 25px;
43
+ max-width: calc(85% + 18px);
44
+ }
45
+
46
+ .search-form input[type='checkbox'],
47
+ .search-form input[type='radio'] {
48
+ justify-self: end;
49
+ align-self: center;
50
+ grid-column: span 3 / auto;
51
+ position: relative;
52
+ left: 17px;
53
+ }
54
+
55
+ .search-form input[type='checkbox'] + label,
56
+ .search-form input[type='radio'] + label {
57
+ padding-left: 17px;
58
+ text-align: left;
59
+ align-self: center;
60
+ grid-column: span 9 / auto;
61
+
62
+ font: var(--form-sublabel-font);
63
+ color: var(--form-sublabel-color);
64
+ }
65
+
66
+ input:focus {
67
+ outline: none;
68
+ border: 1px solid var(--focus-background-color);
69
+ }
70
+ input[type='checkbox'] {
71
+ margin: 0;
72
+ }
73
+
74
+ [search] {
75
+ position: absolute;
76
+ right: 1%;
77
+ bottom: 15px;
78
+ color: var(--search-form-icon-color);
79
+ }
80
+
81
+ @media screen and (max-width: 460px) {
82
+ .search-form {
83
+ grid-template-columns: repeat(12, 1fr);
84
+ grid-gap: 10px 5px;
85
+ background-color: var(--search-form-narrow-background-color);
86
+
87
+ max-height: 54px;
88
+ overflow-y: auto;
89
+ }
90
+ .search-form label {
91
+ grid-column: span 3;
92
+ padding-right: 5px;
93
+ align-self: center;
94
+ color: var(--search-form-narrow-text-color);
95
+ }
96
+ .search-form input,
97
+ .search-form select {
98
+ grid-column: span 8;
99
+ max-width: 100%;
100
+ color: var(--theme-white-color);
101
+ }
102
+ .search-form input[type='checkbox'],
103
+ .search-form input[type='radio'] {
104
+ justify-self: end;
105
+ align-self: center;
106
+ grid-column: span 3 / auto;
107
+ }
108
+
109
+ .search-form input[type='checkbox'] + label,
110
+ .search-form input[type='radio'] + label {
111
+ grid-column: span 8 / auto;
112
+ align-self: center;
113
+ position: relative;
114
+ left: 5px;
115
+ color: var(--search-form-narrow-text-color);
116
+ }
117
+
118
+ [search] {
119
+ right: 3%;
120
+ color: var(--search-form-narrow-text-color);
121
+ }
122
+ }
123
+ @media (min-width: 461px) and (max-width: 1024px) {
124
+ .search-form select {
125
+ max-width: calc(85% + 10px);
126
+ }
127
+ }
128
+ @media screen and (min-width: 1201px) and (max-width: 2000px) {
129
+ .search-form {
130
+ grid-template-columns: repeat(36, 1fr);
131
+ }
132
+ .search-form input,
133
+ .search-form select {
134
+ max-width: 90%;
135
+ }
136
+ .search-form select {
137
+ max-width: calc(90% + 18px);
138
+ }
139
+ }
140
+
141
+ @media screen and (min-width: 2001px) {
142
+ .search-form {
143
+ grid-template-columns: repeat(48, 1fr);
144
+ }
145
+ .search-form input,
146
+ .search-form select {
147
+ max-width: 90%;
148
+ }
149
+ .search-form select {
150
+ max-width: calc(90% + 18px);
151
+ }
152
+ [search] {
153
+ right: 0.8%;
154
+ }
155
+ }
156
+ `