@operato/form 7.1.30 → 7.1.32

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 (42) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/tsconfig.tsbuildinfo +1 -1
  3. package/package.json +7 -7
  4. package/.editorconfig +0 -29
  5. package/.storybook/main.js +0 -3
  6. package/.storybook/preview.js +0 -52
  7. package/.storybook/server.mjs +0 -8
  8. package/demo/index.html +0 -110
  9. package/src/components/ox-custom-input.ts +0 -102
  10. package/src/components/ox-custom-select.ts +0 -91
  11. package/src/components/ox-file-uploader.ts +0 -164
  12. package/src/components/ox-form-master.ts +0 -246
  13. package/src/components/ox-form-paginator.ts +0 -60
  14. package/src/components/ox-search-form-paginator.ts +0 -46
  15. package/src/components/ox-search-form.ts +0 -467
  16. package/src/filters/filter-checkbox.ts +0 -34
  17. package/src/filters/filter-input-barcode.ts +0 -35
  18. package/src/filters/filter-input.ts +0 -30
  19. package/src/filters/filter-range-date.ts +0 -81
  20. package/src/filters/filter-range-number.ts +0 -64
  21. package/src/filters/filter-select.ts +0 -56
  22. package/src/filters/filter-styles.ts +0 -127
  23. package/src/filters/index.ts +0 -10
  24. package/src/filters/ox-filter-personalizer.ts +0 -188
  25. package/src/filters/ox-filters-form-base.ts +0 -437
  26. package/src/filters/registry.ts +0 -54
  27. package/src/index.ts +0 -4
  28. package/src/styles/index.ts +0 -3
  29. package/src/styles/multi-column-form-styles.ts +0 -192
  30. package/src/styles/search-form-styles.ts +0 -160
  31. package/src/styles/single-column-form-styles.ts +0 -114
  32. package/src/types.ts +0 -106
  33. package/stories/ox-filters-form-barcode.stories.ts +0 -140
  34. package/stories/ox-filters-form-base-bounded.stories.ts +0 -208
  35. package/stories/ox-filters-form-base-change-handler.stories.ts +0 -220
  36. package/stories/ox-filters-form-base-page-preference.stories.ts +0 -177
  37. package/stories/ox-filters-form-base.stories.ts +0 -179
  38. package/stories/ox-search-form-change-handler.stories.ts +0 -253
  39. package/stories/ox-search-form.stories.ts +0 -258
  40. package/tsconfig.json +0 -24
  41. package/web-dev-server.config.mjs +0 -27
  42. package/web-test-runner.config.mjs +0 -41
@@ -1,437 +0,0 @@
1
- import '@operato/input/ox-checkbox.js'
2
- import '@operato/input/ox-select.js'
3
- import '@operato/popup/ox-popup-list.js'
4
- import '@operato/input/ox-input-search.js'
5
-
6
- import json5 from 'json5'
7
- import { css, html, LitElement, nothing, PropertyValues, TemplateResult } from 'lit'
8
- import { customElement, property, queryAsync, state } from 'lit/decorators.js'
9
- import { styles as MDTypeScaleStyles } from '@material/web/typography/md-typescale-styles'
10
-
11
- import { getDefaultValue } from '@operato/time-calculator'
12
- import { PagePreferenceProvider } from '@operato/p13n'
13
-
14
- import { FilterStyles } from './filter-styles.js'
15
- import { getFilterRenderer } from './registry.js'
16
- import { FilterConfig, FilterOperator, FilterSelectRenderer, FilterValue, PersonalFilterPreference } from '../types.js'
17
-
18
- export type QueryFilterRangeValue = [from: number, to: number]
19
-
20
- @customElement('ox-filters-form-base')
21
- export class OxFiltersFormBase extends LitElement {
22
- static styles = [
23
- MDTypeScaleStyles,
24
- FilterStyles,
25
- css`
26
- :host {
27
- display: flex;
28
- padding: var(--spacing-small);
29
- }
30
-
31
- form {
32
- flex: 1;
33
-
34
- display: flex;
35
- flex-flow: row wrap;
36
- gap: var(--spacing-medium) var(--spacing-large);
37
- }
38
-
39
- form > * {
40
- display: flex;
41
- align-items: center;
42
- gap: var(--spacing-small, 4px);
43
- }
44
-
45
- label span {
46
- display: block;
47
- }
48
-
49
- @media only screen and (max-width: 460px) {
50
- form {
51
- flex-direction: column;
52
- flex-flow: column;
53
- }
54
- }
55
- `
56
- ]
57
-
58
- @property({ type: Array }) value: FilterValue[] = []
59
- @property({ type: Array }) filters: FilterConfig[] = []
60
- @property({ type: Boolean, attribute: 'url-params-sensitive' }) urlParamsSensitive?: boolean
61
- @property({ type: Boolean, attribute: 'autofocus' }) autofocus: boolean = true
62
- @property({ type: Object }) personalConfigProvider?: PagePreferenceProvider
63
-
64
- @state() personalConfig?: PersonalFilterPreference
65
- @state() personalFilters?: FilterConfig[]
66
-
67
- @queryAsync('form') form!: HTMLFormElement
68
-
69
- private _popstateEventHandler?: EventListener
70
- private _filtersChangeHandler?: EventListener
71
- private autoUpdateTargetsOnChange: { [name: string]: string[] } = {}
72
-
73
- connectedCallback(): void {
74
- super.connectedCallback()
75
-
76
- this.renderRoot.addEventListener('change', async (e: Event) => {
77
- const { target, detail: value } = e as CustomEvent
78
- const name = (target as HTMLInputElement).name
79
- const {
80
- label,
81
- type,
82
- operator,
83
- value: defaultValue,
84
- onchange
85
- } = (this.personalFilters || []).find(filter => filter.name == name) || {}
86
-
87
- if (this.autoUpdateTargetsOnChange[name]) {
88
- /*
89
- 일단은 심플하게, boundTo로 연결된 필터값이 바뀌면, 폼 전체를 update하도록 함.
90
- */
91
- ;(this.autoUpdateTargetsOnChange[name] || []).forEach(name => {
92
- const target = this.renderRoot.querySelector(`[name='${name}']`)
93
- if (target) {
94
- ;(target as HTMLInputElement).value = ''
95
- }
96
- })
97
-
98
- this.requestUpdate()
99
- }
100
-
101
- const keepGoing = onchange ? await onchange.call(null, value ?? (target as HTMLInputElement).value, this) : true
102
-
103
- // filters-form과 동일하게 value 세팅 X (셋팅후 다시 셋팅하여 랜더링을 다시하면서 값이 깨짐)
104
- // this.value = await this.getQueryFilters()
105
-
106
- keepGoing &&
107
- this.dispatchEvent(
108
- new CustomEvent('filters-change', {
109
- bubbles: true,
110
- composed: true,
111
- detail: {
112
- filters: this.value,
113
- from: 'filters-form'
114
- }
115
- })
116
- )
117
- })
118
- }
119
-
120
- buildDefaultValue(operator: FilterOperator, defaultValue: any) {
121
- if (defaultValue === undefined) {
122
- return
123
- }
124
- if (operator == 'between') {
125
- return defaultValue instanceof Array && (defaultValue as Array<any>).map(v => getDefaultValue(v, this))
126
- } else {
127
- return getDefaultValue(defaultValue, this)
128
- }
129
- }
130
-
131
- render(): TemplateResult {
132
- return html`
133
- <form
134
- class="md-typescale-body-medium-prominent"
135
- @submit=${(e: Event) => {
136
- e.preventDefault()
137
- }}
138
- >
139
- ${(this.personalFilters || [])
140
- .filter(filter => !filter.hidden)
141
- .map((filter: FilterConfig) => {
142
- const { name, label, type, operator, readonly, value: defaultValue } = filter
143
- const idx = operator === 'between' ? 1 : 0
144
- const renderer = getFilterRenderer(
145
- operator === 'like' || operator === 'i_like' || operator === 'i_nlike' || operator === 'nlike'
146
- ? 'text'
147
- : type
148
- )[idx]
149
-
150
- const value =
151
- this.value?.find(filter => filter.name == name)?.value ?? this.buildDefaultValue(operator!, defaultValue)
152
-
153
- if (!renderer) {
154
- return nothing
155
- }
156
-
157
- return type === 'search'
158
- ? html`<ox-input-search
159
- name="search"
160
- .value=${value}
161
- ?autofocus=${this.autofocus}
162
- ?readonly=${readonly}
163
- ></ox-input-search>`
164
- : type === 'boolean' || type === 'checkbox'
165
- ? renderer(filter, value, this)
166
- : type !== 'select'
167
- ? html`
168
- <label filter-title ?between=${operator == 'between'}
169
- ><span>${label}</span> ${renderer(filter, value, this)}
170
- </label>
171
- `
172
- : this.renderSelect(filter, value, renderer)
173
- })}
174
- </form>
175
-
176
- <slot name="setting"></slot>
177
- `
178
- }
179
-
180
- renderSelect(filter: FilterConfig, value: any, renderer: FilterSelectRenderer) {
181
- const { name, label, operator, readonly, value: defaultValue } = filter
182
-
183
- return operator === 'in'
184
- ? html`
185
- <ox-select
186
- name=${name}
187
- placeholder=${label}
188
- .value=${value ?? defaultValue}
189
- @change=${(e: CustomEvent) =>
190
- e.target?.dispatchEvent(
191
- new CustomEvent('filter-change', {
192
- detail: {
193
- name,
194
- operator,
195
- value: e.detail
196
- }
197
- })
198
- )}
199
- ?readonly=${readonly}
200
- >
201
- <ox-popup-list multiple attr-selected="checked" with-search>
202
- ${renderer(filter, value, this)}
203
- </ox-popup-list>
204
- </ox-select>
205
- `
206
- : html`
207
- <ox-select
208
- name=${name}
209
- placeholder=${label}
210
- .value=${value ?? defaultValue}
211
- @change=${(e: CustomEvent) =>
212
- e.target?.dispatchEvent(
213
- new CustomEvent('filter-change', {
214
- detail: {
215
- name,
216
- operator,
217
- value: e.detail
218
- }
219
- })
220
- )}
221
- ?readonly=${readonly}
222
- >
223
- <ox-popup-list with-search> ${renderer(filter, value, this)} </ox-popup-list>
224
- </ox-select>
225
- `
226
- }
227
-
228
- async updated(changes: PropertyValues<this>) {
229
- if (changes.has('urlParamsSensitive')) {
230
- if (this.urlParamsSensitive) {
231
- this._popstateEventHandler = () => {
232
- const queryString = window.location.search
233
- const urlParams = new URLSearchParams(queryString)
234
- var filters = urlParams.get('filters')
235
-
236
- if (filters) {
237
- try {
238
- this.value = json5.parse(filters)
239
- this.dispatchEvent(
240
- new CustomEvent('filters-change', {
241
- bubbles: true,
242
- composed: true,
243
- detail: {
244
- filters: json5.parse(filters),
245
- from: 'url-parameter'
246
- }
247
- })
248
- )
249
- } catch (e) {
250
- console.error(`invalid filters params on URL query string : ${e}`)
251
- }
252
- }
253
- }
254
-
255
- //@ts-ignore
256
- this._popstateEventHandler() // call for the first time
257
-
258
- window.addEventListener('popstate', this._popstateEventHandler)
259
-
260
- this._filtersChangeHandler = (e: Event) => {
261
- const urlParams = new URLSearchParams(window.location.search)
262
- const { filters, from } = (e as CustomEvent).detail || {}
263
-
264
- if (from === 'url-parameter') {
265
- return
266
- }
267
-
268
- if (filters.length > 0) {
269
- urlParams.set('filters', json5.stringify(filters))
270
- } else {
271
- urlParams.delete('filters')
272
- }
273
- const queryString = String(urlParams)
274
- const url = `${window.location.pathname}${queryString ? `?${queryString}` : ''}`
275
-
276
- history.pushState({}, document.title, url)
277
- }
278
-
279
- this.addEventListener('filters-change', this._filtersChangeHandler)
280
- } else {
281
- if (this._popstateEventHandler) {
282
- window.removeEventListener('popstate', this._popstateEventHandler)
283
- }
284
- if (this._filtersChangeHandler) {
285
- this.removeEventListener('popstate', this._filtersChangeHandler)
286
- }
287
- }
288
- }
289
-
290
- if (changes.has('personalConfigProvider') && this.personalConfigProvider) {
291
- this.personalConfig = await this.personalConfigProvider.load()
292
- } else if (changes.has('filters') || changes.has('personalConfig')) {
293
- this.applyUpdatedConfiguration()
294
- }
295
- }
296
-
297
- async applyUpdatedConfiguration() {
298
- if (!this.filters) {
299
- return
300
- }
301
-
302
- if (!this.personalConfig) {
303
- this.personalFilters = [...this.filters]
304
- } else {
305
- const { filters: personalFilters } = this.personalConfig
306
-
307
- if (personalFilters) {
308
- const xfilters = this.filters.map((filter: Partial<FilterConfig>) => {
309
- const personalFilter = personalFilters.find(pFilter => pFilter.name == filter.name)
310
- return personalFilter ? { ...filter, ...personalFilter } : filter
311
- })
312
-
313
- function reorderList(a: Partial<FilterConfig>[], b: Partial<FilterConfig>[]): Partial<FilterConfig>[] {
314
- // 결과 배열 초기화, a 배열 길이만큼 undefined로 채움
315
- const result = new Array(a.length)
316
-
317
- // b 배열에 없는 아이템은 원래 위치로 채움
318
- a.forEach((item, index) => {
319
- if (!item.name || !b.find(bi => bi.name == item.name)) {
320
- result[index] = item
321
- }
322
- })
323
-
324
- b.forEach(item => {
325
- const ai = a.find(ai => ai.name == item.name)
326
- if (ai) {
327
- result[result.findIndex(slot => slot === undefined)] = ai
328
- }
329
- })
330
-
331
- return result
332
- }
333
-
334
- // 배열 재정렬 실행
335
- this.personalFilters = reorderList(xfilters as any, personalFilters as any) as FilterConfig[]
336
- }
337
- }
338
-
339
- this.autoUpdateTargetsOnChange = {}
340
-
341
- this.personalFilters
342
- ?.filter(filter => filter.boundTo && filter.boundTo.length > 0)
343
- .map(({ name, boundTo }) => {
344
- boundTo!.forEach(to => {
345
- const origin = this.autoUpdateTargetsOnChange[to] || []
346
- if (name && !origin.includes(name)) {
347
- this.autoUpdateTargetsOnChange[to] = [...origin, name]
348
- }
349
- })
350
- })
351
-
352
- if (!this.urlParamsSensitive) {
353
- this.value = await this.getQueryFilters()
354
- }
355
- }
356
-
357
- getFieldByName(name: string) {
358
- return this.renderRoot.querySelector(`[name=${name}]`)
359
- }
360
-
361
- async getQueryFilters(): Promise<FilterValue[]> {
362
- const formData = new FormData(await this.form)
363
-
364
- var filters = (this.personalFilters || [])
365
- .map((filter: FilterConfig) => {
366
- const { name, type, operator } = filter
367
-
368
- var value = formData.getAll(name)
369
- if (value.length == 0) {
370
- return
371
- }
372
-
373
- if (-1 === value.findIndex(v => v !== '')) {
374
- return
375
- }
376
-
377
- const filterValue = value.map(v => {
378
- const value = v.toString()
379
-
380
- /* TODO registry에서 타입별로 parsing 방법을 지정할 수 있도록 해야한다. */
381
- switch (type) {
382
- case 'integer':
383
- case 'float':
384
- case 'number':
385
- case 'progress':
386
- case 'checkbox':
387
- case 'boolean':
388
- return !value ? undefined : JSON.parse(value)
389
- default:
390
- return value
391
- }
392
- })
393
-
394
- return {
395
- name,
396
- operator,
397
- value: filterValue.length === 1 ? filterValue[0] : filterValue
398
- }
399
- })
400
- .filter(result => result !== undefined) as FilterValue[]
401
-
402
- return filters
403
- }
404
-
405
- reset(value?: string[] | { [field: string]: any }) {
406
- this.form
407
- .then((form: HTMLFormElement) => {
408
- if (value) {
409
- if (value instanceof Array) {
410
- value
411
- .filter(item => typeof item == 'string')
412
- .forEach(item => {
413
- const field = this.getFieldByName(item)
414
- if (field) {
415
- ;(field as HTMLInputElement).value = ''
416
- }
417
- })
418
- } else {
419
- const keys = Object.keys(value)
420
- keys
421
- .filter(key => typeof key == 'string')
422
- .forEach(key => {
423
- const field = this.getFieldByName(key)
424
- if (field) {
425
- ;(field as HTMLInputElement).value = value[key]
426
- }
427
- })
428
- }
429
- } else {
430
- form.reset()
431
- }
432
- })
433
- .catch((error: any) => {
434
- console.error('Error resetting the form:', error)
435
- })
436
- }
437
- }
@@ -1,54 +0,0 @@
1
- import { FilterCheckbox } from './filter-checkbox.js'
2
- import { FilterInputBarcode } from './filter-input-barcode.js'
3
- import { FilterInput } from './filter-input.js'
4
- import { FilterRangeDate } from './filter-range-date.js'
5
- import { FilterRangeNumber } from './filter-range-number.js'
6
- import { FilterSelect } from './filter-select.js'
7
- import { FilterSelectRenderer } from '../types.js'
8
-
9
- var RENDERERS: {
10
- [name: string]: FilterSelectRenderer[]
11
- } = {
12
- string: [FilterInput],
13
- text: [FilterInput],
14
- email: [FilterInput],
15
- tel: [FilterInput],
16
- integer: [FilterInput, FilterRangeNumber],
17
- float: [FilterInput, FilterRangeNumber],
18
- number: [FilterInput, FilterRangeNumber],
19
- select: [FilterSelect],
20
- boolean: [FilterCheckbox],
21
- checkbox: [FilterCheckbox],
22
- month: [FilterInput, FilterRangeDate],
23
- week: [FilterInput, FilterRangeDate],
24
- date: [FilterInput, FilterRangeDate],
25
- time: [FilterInput, FilterRangeDate],
26
- datetime: [FilterInput, FilterRangeDate],
27
- color: [FilterInput],
28
- progress: [FilterInput, FilterRangeNumber],
29
- link: [FilterInput],
30
- image: [FilterInput],
31
- json5: [FilterInput],
32
- scanner: [FilterInputBarcode],
33
- barcode: [FilterInputBarcode]
34
- }
35
-
36
- export function registerFilterRenderer(type: string, renderer: FilterSelectRenderer[]) {
37
- RENDERERS[type] = renderer
38
- }
39
-
40
- export function unregisterFilterRenderer(type: string): void {
41
- delete RENDERERS[type]
42
- }
43
-
44
- export function getFilterRenderers(): { [name: string]: FilterSelectRenderer[] } {
45
- return { ...RENDERERS }
46
- }
47
-
48
- export function getFilterRenderer(type: string): FilterSelectRenderer[] {
49
- if (typeof type == 'function') {
50
- return type
51
- }
52
-
53
- return RENDERERS[type || 'text'] || [FilterInput]
54
- }
package/src/index.ts DELETED
@@ -1,4 +0,0 @@
1
- export * from './styles'
2
- export * from './components/ox-search-form'
3
- export * from './components/ox-file-uploader'
4
- export * from './filters'
@@ -1,3 +0,0 @@
1
- export * from './single-column-form-styles'
2
- export * from './multi-column-form-styles'
3
- export * from './search-form-styles'
@@ -1,192 +0,0 @@
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, var(--md-sys-color-on-surface-variant));
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, var(--md-sys-color-on-surface));
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, var(--md-sys-color-secondary));
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, var(--md-sys-color-surface-variant));
84
- }
85
- input[type='checkbox'] {
86
- margin: 0;
87
- }
88
-
89
- @media screen and (max-width: 460px) {
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, var(--spacing-medium));
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(--spacing-small)) var(--spacing-small, var(--spacing-small));
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
- `