@operato/form 2.0.0-alpha.127 → 2.0.0-alpha.133
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 +23 -0
- package/dist/src/components/ox-search-form.js +22 -1
- package/dist/src/components/ox-search-form.js.map +1 -1
- package/dist/src/filters/filter-styles.js +21 -7
- package/dist/src/filters/filter-styles.js.map +1 -1
- package/dist/src/filters/ox-filter-personalizer.d.ts +8 -0
- package/dist/src/filters/ox-filter-personalizer.js +181 -0
- package/dist/src/filters/ox-filter-personalizer.js.map +1 -0
- package/dist/src/filters/ox-filters-form-base.d.ts +7 -2
- package/dist/src/filters/ox-filters-form-base.js +77 -26
- package/dist/src/filters/ox-filters-form-base.js.map +1 -1
- package/dist/src/types.d.ts +4 -0
- package/dist/src/types.js.map +1 -1
- package/dist/stories/ox-filters-form-base-page-preference.stories.d.ts +31 -0
- package/dist/stories/ox-filters-form-base-page-preference.stories.js +173 -0
- package/dist/stories/ox-filters-form-base-page-preference.stories.js.map +1 -0
- package/dist/stories/ox-search-form-change-handler.stories.js +5 -1
- package/dist/stories/ox-search-form-change-handler.stories.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -2
- package/src/components/ox-search-form.ts +28 -1
- package/src/filters/filter-styles.ts +21 -7
- package/src/filters/ox-filter-personalizer.ts +195 -0
- package/src/filters/ox-filters-form-base.ts +104 -50
- package/src/types.ts +5 -0
- package/stories/ox-filters-form-base-page-preference.stories.ts +192 -0
- package/stories/ox-search-form-change-handler.stories.ts +5 -1
|
@@ -80,15 +80,29 @@ export const FilterStyles = css`
|
|
|
80
80
|
--ox-filters-input-font: normal 16px var(--theme-font);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
input
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
ox-input-barcode {
|
|
84
|
+
max-width: unset;
|
|
85
|
+
flex: 1;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
input
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
ox-input-search {
|
|
89
|
+
max-width: unset;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
ox-select {
|
|
93
|
+
max-width: unset;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
ox-checkbox {
|
|
97
|
+
max-width: unset;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
input[type='number'] {
|
|
101
|
+
max-width: unset;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
input {
|
|
105
|
+
flex: 1;
|
|
92
106
|
}
|
|
93
107
|
}
|
|
94
108
|
`
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import '@material/web/button/outlined-button.js'
|
|
2
|
+
|
|
3
|
+
import { css, html, LitElement } from 'lit'
|
|
4
|
+
import { customElement, property, state } from 'lit/decorators.js'
|
|
5
|
+
|
|
6
|
+
import { i18next } from '@operato/i18n'
|
|
7
|
+
import { OxPopupList } from '@operato/popup'
|
|
8
|
+
|
|
9
|
+
import { FilterConfig, PersonalFilterPreference } from '../types.js'
|
|
10
|
+
import { OxCheckbox } from '@operato/input'
|
|
11
|
+
|
|
12
|
+
import { OxFiltersFormBase } from './ox-filters-form-base'
|
|
13
|
+
|
|
14
|
+
@customElement('ox-filter-personalizer')
|
|
15
|
+
export class OxFilterPersonalizer extends LitElement {
|
|
16
|
+
static styles = [
|
|
17
|
+
css`
|
|
18
|
+
md-icon {
|
|
19
|
+
--md-icon-size: 16px;
|
|
20
|
+
width: 36px;
|
|
21
|
+
height: 36px;
|
|
22
|
+
color: var(--md-sys-color-secondary-container);
|
|
23
|
+
cursor: pointer;
|
|
24
|
+
|
|
25
|
+
display: flex;
|
|
26
|
+
place-content: center;
|
|
27
|
+
place-items: center;
|
|
28
|
+
position: relative;
|
|
29
|
+
|
|
30
|
+
&:hover {
|
|
31
|
+
color: var(--md-sys-color-primary-container);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
md-ripple {
|
|
36
|
+
border-radius: 50%;
|
|
37
|
+
inset: unset;
|
|
38
|
+
height: 36px;
|
|
39
|
+
width: 36px;
|
|
40
|
+
}
|
|
41
|
+
`
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
@property({ type: Boolean, attribute: true }) debug: boolean = false
|
|
45
|
+
|
|
46
|
+
@state() private preference?: PersonalFilterPreference
|
|
47
|
+
|
|
48
|
+
render() {
|
|
49
|
+
return html`
|
|
50
|
+
<md-icon
|
|
51
|
+
@click=${async (e: MouseEvent) => {
|
|
52
|
+
const form = this.closest('ox-filters-form-base') as OxFiltersFormBase
|
|
53
|
+
const { filters, personalFilters = [] } = form
|
|
54
|
+
const queryFilters = await form.getQueryFilters()
|
|
55
|
+
|
|
56
|
+
this.preference = {
|
|
57
|
+
filters: (
|
|
58
|
+
personalFilters.map(filter => {
|
|
59
|
+
const originFilter = filters.find(f => f.name == filter.name)
|
|
60
|
+
|
|
61
|
+
if (!originFilter) {
|
|
62
|
+
/* 원래 filters 설정에 있는 것들 만을 유지한다. */
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
name: filter.name,
|
|
68
|
+
hidden: filter.hidden,
|
|
69
|
+
/* 만약, filters에 기본값이 이미 설정되어 있다면, 그대로 유지한다. */
|
|
70
|
+
value: originFilter!.value ?? queryFilters.find(f => f.name == filter.name)?.value
|
|
71
|
+
}
|
|
72
|
+
}) || []
|
|
73
|
+
).filter(Boolean) as Partial<FilterConfig>[]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const template = html`
|
|
77
|
+
<div class="personalizer-header" slot="header">
|
|
78
|
+
<md-icon
|
|
79
|
+
style="margin-left: auto;"
|
|
80
|
+
@click=${async (e: MouseEvent) => {
|
|
81
|
+
if (form.personalConfigProvider) {
|
|
82
|
+
form.personalConfig = await form.personalConfigProvider.save({
|
|
83
|
+
...form.personalConfig,
|
|
84
|
+
...this.preference
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
popup.close()
|
|
88
|
+
}}
|
|
89
|
+
title=${String(i18next.t('button.save'))}
|
|
90
|
+
>keep</md-icon
|
|
91
|
+
><md-icon
|
|
92
|
+
@click=${async (e: MouseEvent) => {
|
|
93
|
+
if (form.personalConfigProvider) {
|
|
94
|
+
form.personalConfig = await form.personalConfigProvider.save({
|
|
95
|
+
...form.personalConfig,
|
|
96
|
+
filters: []
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
this.preference = {}
|
|
100
|
+
}
|
|
101
|
+
popup.close()
|
|
102
|
+
}}
|
|
103
|
+
title=${String(i18next.t('button.delete'))}
|
|
104
|
+
>keep_off</md-icon
|
|
105
|
+
><md-icon @click=${async (e: MouseEvent) => popup.close()} title=${String(i18next.t('button.close'))}
|
|
106
|
+
>close</md-icon
|
|
107
|
+
>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
${this.preference?.filters!.map(
|
|
111
|
+
filter => html`
|
|
112
|
+
<ox-checkbox label="checkbox" ?checked=${!filter.hidden} value=${filter.name} option
|
|
113
|
+
>${filter.name}<span style="position: absolute; right: 10px; cursor: move;" handle
|
|
114
|
+
>☰</span
|
|
115
|
+
></ox-checkbox
|
|
116
|
+
>
|
|
117
|
+
`
|
|
118
|
+
)}
|
|
119
|
+
`
|
|
120
|
+
|
|
121
|
+
const popup = OxPopupList.open({
|
|
122
|
+
template,
|
|
123
|
+
multiple: true,
|
|
124
|
+
sortable: true,
|
|
125
|
+
debug: this.debug,
|
|
126
|
+
attrSelected: 'checked',
|
|
127
|
+
top: e.pageY,
|
|
128
|
+
left: e.pageX,
|
|
129
|
+
styles: css`
|
|
130
|
+
:host {
|
|
131
|
+
width: 240px;
|
|
132
|
+
max-height: 300px;
|
|
133
|
+
overflow: auto;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
::slotted(.personalizer-header) {
|
|
137
|
+
--md-icon-size: 1.4em;
|
|
138
|
+
|
|
139
|
+
display: flex;
|
|
140
|
+
flex-direction: row;
|
|
141
|
+
align-items: center;
|
|
142
|
+
text-transform: capitalize;
|
|
143
|
+
box-shadow: 0 3px 3px rgba(0, 0, 0, 0.3);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
::slotted([option]) {
|
|
147
|
+
position: relative;
|
|
148
|
+
user-select: none;
|
|
149
|
+
}
|
|
150
|
+
`
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
popup.onselect = (e: Event) => {
|
|
154
|
+
const selected = (e as CustomEvent).detail
|
|
155
|
+
|
|
156
|
+
const pconfig: PersonalFilterPreference = { ...form.personalConfig }
|
|
157
|
+
const pfilters = this.preference?.filters || form.filters
|
|
158
|
+
|
|
159
|
+
pconfig.filters = pfilters.map(filter => {
|
|
160
|
+
return {
|
|
161
|
+
name: filter.name,
|
|
162
|
+
hidden: selected.indexOf(filter.name) == -1,
|
|
163
|
+
value: filter.value
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
form.personalConfig = this.preference = pconfig
|
|
168
|
+
|
|
169
|
+
form.applyUpdatedConfiguration()
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
popup.addEventListener('sorted', (e: Event) => {
|
|
173
|
+
const sorted = (e as CustomEvent).detail as HTMLElement[]
|
|
174
|
+
|
|
175
|
+
const pconfig: PersonalFilterPreference = { ...form.personalConfig }
|
|
176
|
+
|
|
177
|
+
pconfig.filters = sorted.map(element => {
|
|
178
|
+
const name = (element as OxCheckbox).value
|
|
179
|
+
return {
|
|
180
|
+
name,
|
|
181
|
+
hidden: !element.hasAttribute('checked'),
|
|
182
|
+
value: filters.find(filter => filter.name == name)?.value
|
|
183
|
+
}
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
form.personalConfig = this.preference = pconfig
|
|
187
|
+
|
|
188
|
+
form.applyUpdatedConfiguration()
|
|
189
|
+
})
|
|
190
|
+
}}
|
|
191
|
+
>settings<md-ripple></md-ripple
|
|
192
|
+
></md-icon>
|
|
193
|
+
`
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -5,13 +5,14 @@ import '@operato/input/ox-input-search.js'
|
|
|
5
5
|
|
|
6
6
|
import json5 from 'json5'
|
|
7
7
|
import { css, html, LitElement, nothing, PropertyValues, TemplateResult } from 'lit'
|
|
8
|
-
import { customElement, property, queryAsync } from 'lit/decorators.js'
|
|
8
|
+
import { customElement, property, queryAsync, state } from 'lit/decorators.js'
|
|
9
9
|
|
|
10
10
|
import { getDefaultValue } from '@operato/time-calculator'
|
|
11
|
+
import { PagePreferenceProvider } from '@operato/p13n'
|
|
11
12
|
|
|
12
13
|
import { FilterStyles } from './filter-styles.js'
|
|
13
14
|
import { getFilterRenderer } from './registry.js'
|
|
14
|
-
import { FilterConfig, FilterOperator, FilterSelectRenderer, FilterValue } from '../types.js'
|
|
15
|
+
import { FilterConfig, FilterOperator, FilterSelectRenderer, FilterValue, PersonalFilterPreference } from '../types.js'
|
|
15
16
|
|
|
16
17
|
export type QueryFilterRangeValue = [from: number, to: number]
|
|
17
18
|
|
|
@@ -43,8 +44,9 @@ export class OxFiltersFormBase extends LitElement {
|
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
@media only screen and (max-width: 460px) {
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
form {
|
|
48
|
+
flex-direction: column;
|
|
49
|
+
flex-flow: column;
|
|
48
50
|
}
|
|
49
51
|
}
|
|
50
52
|
`
|
|
@@ -54,6 +56,10 @@ export class OxFiltersFormBase extends LitElement {
|
|
|
54
56
|
@property({ type: Array }) filters: FilterConfig[] = []
|
|
55
57
|
@property({ type: Boolean, attribute: 'url-params-sensitive' }) urlParamsSensitive?: boolean
|
|
56
58
|
@property({ type: Boolean, attribute: 'autofocus' }) autofocus: boolean = true
|
|
59
|
+
@property({ type: Object }) personalConfigProvider?: PagePreferenceProvider
|
|
60
|
+
|
|
61
|
+
@state() personalConfig?: PersonalFilterPreference
|
|
62
|
+
@state() personalFilters?: FilterConfig[]
|
|
57
63
|
|
|
58
64
|
@queryAsync('form') form!: HTMLFormElement
|
|
59
65
|
|
|
@@ -73,7 +79,7 @@ export class OxFiltersFormBase extends LitElement {
|
|
|
73
79
|
operator,
|
|
74
80
|
value: defaultValue,
|
|
75
81
|
onchange
|
|
76
|
-
} = this.
|
|
82
|
+
} = (this.personalFilters || []).find(filter => filter.name == name) || {}
|
|
77
83
|
|
|
78
84
|
if (this.autoUpdateTargetsOnChange[name]) {
|
|
79
85
|
/*
|
|
@@ -112,7 +118,7 @@ export class OxFiltersFormBase extends LitElement {
|
|
|
112
118
|
return
|
|
113
119
|
}
|
|
114
120
|
if (operator == 'between') {
|
|
115
|
-
return (defaultValue as Array<any>).map(v => getDefaultValue(v, this))
|
|
121
|
+
return defaultValue instanceof Array && (defaultValue as Array<any>).map(v => getDefaultValue(v, this))
|
|
116
122
|
} else {
|
|
117
123
|
return getDefaultValue(defaultValue, this)
|
|
118
124
|
}
|
|
@@ -125,35 +131,39 @@ export class OxFiltersFormBase extends LitElement {
|
|
|
125
131
|
e.preventDefault()
|
|
126
132
|
}}
|
|
127
133
|
>
|
|
128
|
-
${this.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
134
|
+
${(this.personalFilters || [])
|
|
135
|
+
.filter(filter => !filter.hidden)
|
|
136
|
+
.map((filter: FilterConfig) => {
|
|
137
|
+
const { name, label, type, operator, value: defaultValue } = filter
|
|
138
|
+
const idx = operator === 'between' ? 1 : 0
|
|
139
|
+
const renderer = getFilterRenderer(
|
|
140
|
+
operator === 'like' || operator === 'i_like' || operator === 'i_nlike' || operator === 'nlike'
|
|
141
|
+
? 'text'
|
|
142
|
+
: type
|
|
143
|
+
)[idx]
|
|
144
|
+
|
|
145
|
+
const value =
|
|
146
|
+
this.value?.find(filter => filter.name == name)?.value ?? this.buildDefaultValue(operator!, defaultValue)
|
|
147
|
+
|
|
148
|
+
if (!renderer) {
|
|
149
|
+
return nothing
|
|
150
|
+
}
|
|
143
151
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
152
|
+
return type === 'search'
|
|
153
|
+
? html`<ox-input-search name="search" .value=${value} ?autofocus=${this.autofocus}></ox-input-search>`
|
|
154
|
+
: type === 'boolean' || type === 'checkbox'
|
|
155
|
+
? renderer(filter, value, this)
|
|
156
|
+
: type !== 'select'
|
|
157
|
+
? html`
|
|
158
|
+
<label filter-title ?between=${operator == 'between'}
|
|
159
|
+
><span>${label}</span> ${renderer(filter, value, this)}
|
|
160
|
+
</label>
|
|
161
|
+
`
|
|
162
|
+
: this.renderSelect(filter, value, renderer)
|
|
163
|
+
})}
|
|
156
164
|
</form>
|
|
165
|
+
|
|
166
|
+
<slot name="setting"></slot>
|
|
157
167
|
`
|
|
158
168
|
}
|
|
159
169
|
|
|
@@ -203,10 +213,6 @@ export class OxFiltersFormBase extends LitElement {
|
|
|
203
213
|
`
|
|
204
214
|
}
|
|
205
215
|
|
|
206
|
-
getFieldByName(name: string) {
|
|
207
|
-
return this.renderRoot.querySelector(`[name=${name}]`)
|
|
208
|
-
}
|
|
209
|
-
|
|
210
216
|
async updated(changes: PropertyValues<this>) {
|
|
211
217
|
if (changes.has('urlParamsSensitive')) {
|
|
212
218
|
if (this.urlParamsSensitive) {
|
|
@@ -269,29 +275,77 @@ export class OxFiltersFormBase extends LitElement {
|
|
|
269
275
|
}
|
|
270
276
|
}
|
|
271
277
|
|
|
272
|
-
if (changes.has('
|
|
273
|
-
this.
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
278
|
+
if (changes.has('personalConfigProvider') && this.personalConfigProvider) {
|
|
279
|
+
this.personalConfig = await this.personalConfigProvider.load()
|
|
280
|
+
} else if (changes.has('filters') || changes.has('personalConfig')) {
|
|
281
|
+
this.applyUpdatedConfiguration()
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async applyUpdatedConfiguration() {
|
|
286
|
+
if (!this.personalConfig) {
|
|
287
|
+
this.personalFilters = [...this.filters]
|
|
288
|
+
} else {
|
|
289
|
+
const { filters: personalFilters } = this.personalConfig
|
|
290
|
+
|
|
291
|
+
if (personalFilters) {
|
|
292
|
+
const xfilters = this.filters.map((filter: Partial<FilterConfig>) => {
|
|
293
|
+
const personalFilter = personalFilters.find(pFilter => pFilter.name == filter.name)
|
|
294
|
+
return personalFilter ? { ...filter, ...personalFilter } : filter
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
function reorderList(a: Partial<FilterConfig>[], b: Partial<FilterConfig>[]): Partial<FilterConfig>[] {
|
|
298
|
+
// 결과 배열 초기화, a 배열 길이만큼 undefined로 채움
|
|
299
|
+
const result = new Array(a.length)
|
|
300
|
+
|
|
301
|
+
// b 배열에 없는 아이템은 원래 위치로 채움
|
|
302
|
+
a.forEach((item, index) => {
|
|
303
|
+
if (!item.name || !b.find(bi => bi.name == item.name)) {
|
|
304
|
+
result[index] = item
|
|
281
305
|
}
|
|
282
306
|
})
|
|
283
|
-
})
|
|
284
307
|
|
|
285
|
-
|
|
286
|
-
|
|
308
|
+
b.forEach(item => {
|
|
309
|
+
const ai = a.find(ai => ai.name == item.name)
|
|
310
|
+
if (ai) {
|
|
311
|
+
result[result.findIndex(slot => slot === undefined)] = ai
|
|
312
|
+
}
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
return result
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// 배열 재정렬 실행
|
|
319
|
+
this.personalFilters = reorderList(xfilters as any, personalFilters as any) as FilterConfig[]
|
|
287
320
|
}
|
|
288
321
|
}
|
|
322
|
+
|
|
323
|
+
this.autoUpdateTargetsOnChange = {}
|
|
324
|
+
|
|
325
|
+
this.personalFilters
|
|
326
|
+
?.filter(filter => filter.boundTo && filter.boundTo.length > 0)
|
|
327
|
+
.map(({ name, boundTo }) => {
|
|
328
|
+
boundTo!.forEach(to => {
|
|
329
|
+
const origin = this.autoUpdateTargetsOnChange[to] || []
|
|
330
|
+
if (name && !origin.includes(name)) {
|
|
331
|
+
this.autoUpdateTargetsOnChange[to] = [...origin, name]
|
|
332
|
+
}
|
|
333
|
+
})
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
if (!this.urlParamsSensitive) {
|
|
337
|
+
this.value = await this.getQueryFilters()
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
getFieldByName(name: string) {
|
|
342
|
+
return this.renderRoot.querySelector(`[name=${name}]`)
|
|
289
343
|
}
|
|
290
344
|
|
|
291
345
|
async getQueryFilters(): Promise<FilterValue[]> {
|
|
292
346
|
const formData = new FormData(await this.form)
|
|
293
347
|
|
|
294
|
-
var filters = this.
|
|
348
|
+
var filters = (this.personalFilters || [])
|
|
295
349
|
.map((filter: FilterConfig) => {
|
|
296
350
|
const { name, type, operator } = filter
|
|
297
351
|
|
package/src/types.ts
CHANGED
|
@@ -63,6 +63,7 @@ export type FilterConfig = {
|
|
|
63
63
|
| { name: string; [params: string]: any }[]
|
|
64
64
|
| undefined
|
|
65
65
|
boundTo?: string[]
|
|
66
|
+
hidden?: boolean
|
|
66
67
|
onchange?: FilterChangedCallback
|
|
67
68
|
}
|
|
68
69
|
|
|
@@ -97,3 +98,7 @@ export type SearchFormField = {
|
|
|
97
98
|
handlers?: { [event: string]: EventListener }
|
|
98
99
|
onchange?: SearchFilterChangedCallback
|
|
99
100
|
}
|
|
101
|
+
|
|
102
|
+
export type PersonalFilterPreference = {
|
|
103
|
+
filters?: Partial<FilterConfig>[]
|
|
104
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import '../src/filters/ox-filters-form-base.js'
|
|
2
|
+
import '../src/filters/ox-filter-personalizer.js'
|
|
3
|
+
|
|
4
|
+
import { html, TemplateResult } from 'lit'
|
|
5
|
+
import { sleep } from '@operato/utils'
|
|
6
|
+
import { CommonHeaderStyles } from '@operato/styles'
|
|
7
|
+
|
|
8
|
+
import { OxFiltersFormBase, FilterConfig, FilterValue } from '../src/index.js'
|
|
9
|
+
import { PersonalFilterPreference } from '../src/types.js'
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
title: 'ox-filters-form-base page-preference',
|
|
13
|
+
component: 'ox-filters-form-base',
|
|
14
|
+
argTypes: {
|
|
15
|
+
value: { control: 'object' },
|
|
16
|
+
filters: { control: 'object' },
|
|
17
|
+
urlParamsSensitive: { control: 'boolean' }
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface Story<T> {
|
|
22
|
+
(args: T): TemplateResult
|
|
23
|
+
args?: Partial<T>
|
|
24
|
+
argTypes?: Record<string, unknown>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface ArgTypes {
|
|
28
|
+
value?: FilterValue[]
|
|
29
|
+
filters?: FilterConfig[]
|
|
30
|
+
urlParamsSensitive: boolean
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const Template: Story<ArgTypes> = ({ value, filters, urlParamsSensitive }: ArgTypes) => html`
|
|
34
|
+
<link href="/themes/app-theme.css" rel="stylesheet" />
|
|
35
|
+
<link href="/themes/form-theme.css" rel="stylesheet" />
|
|
36
|
+
<link href="/themes/input-theme.css" rel="stylesheet" />
|
|
37
|
+
|
|
38
|
+
<link
|
|
39
|
+
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL@20..48,100..700,0..1"
|
|
40
|
+
rel="stylesheet"
|
|
41
|
+
/>
|
|
42
|
+
<link
|
|
43
|
+
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL@20..48,100..700,0..1"
|
|
44
|
+
rel="stylesheet"
|
|
45
|
+
/>
|
|
46
|
+
<link
|
|
47
|
+
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp:opsz,wght,FILL@20..48,100..700,0..1"
|
|
48
|
+
rel="stylesheet"
|
|
49
|
+
/>
|
|
50
|
+
|
|
51
|
+
<style>
|
|
52
|
+
body {
|
|
53
|
+
height: 500px;
|
|
54
|
+
}
|
|
55
|
+
</style>
|
|
56
|
+
|
|
57
|
+
<style>
|
|
58
|
+
ox-filters-form-base {
|
|
59
|
+
--input-gap-vertical: 8px;
|
|
60
|
+
--input-gap-horizontal: 16px;
|
|
61
|
+
|
|
62
|
+
--ox-filters-input-placeholder-color: var(--primary-color);
|
|
63
|
+
|
|
64
|
+
--ox-filters-input-border: 1px solid rgba(0, 0, 0, 0.2);
|
|
65
|
+
--ox-filters-input-focus-border: 1px solid var(--primary-color);
|
|
66
|
+
--ox-filters-input-font: normal 14px var(--theme-font);
|
|
67
|
+
--ox-filters-input-color: var(--primary-text-color);
|
|
68
|
+
--ox-filters-input-focus-color: var(--primary-color);
|
|
69
|
+
--ox-filters-label-font: normal 14px var(--theme-font);
|
|
70
|
+
--ox-filters-label-color: var(--primary-text-color);
|
|
71
|
+
|
|
72
|
+
--ox-filters-form-gap: var(--input-gap-vertical, 8px) var(--input-gap-horizontal, 16px);
|
|
73
|
+
--ox-filters-input-padding: 6px 2px;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
div.filters {
|
|
77
|
+
position: relative;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
ox-filter-personalizer {
|
|
81
|
+
position: absolute;
|
|
82
|
+
top: 0;
|
|
83
|
+
right: 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
${CommonHeaderStyles.cssText}
|
|
87
|
+
</style>
|
|
88
|
+
|
|
89
|
+
<div class="header">
|
|
90
|
+
<div class="filters">
|
|
91
|
+
<ox-filters-form-base
|
|
92
|
+
.value=${value!}
|
|
93
|
+
.filters=${filters!}
|
|
94
|
+
?url-params-sensitive=${urlParamsSensitive}
|
|
95
|
+
.personalConfigProvider=${{
|
|
96
|
+
async load() {
|
|
97
|
+
await sleep(1000)
|
|
98
|
+
return {
|
|
99
|
+
filters: [
|
|
100
|
+
{ name: 'name', hidden: false, value: 'abc' },
|
|
101
|
+
{ name: 'description', hidden: true }
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
async save(preference: PersonalFilterPreference) {
|
|
106
|
+
await sleep(1000)
|
|
107
|
+
console.log('saving preference', preference)
|
|
108
|
+
return preference
|
|
109
|
+
},
|
|
110
|
+
async reset() {
|
|
111
|
+
await sleep(1000)
|
|
112
|
+
}
|
|
113
|
+
}}
|
|
114
|
+
@filters-change=${(e: CustomEvent) => {
|
|
115
|
+
console.log('filters changed', e.detail)
|
|
116
|
+
}}
|
|
117
|
+
>
|
|
118
|
+
<ox-filter-personalizer slot="setting"></ox-filter-personalizer>
|
|
119
|
+
</ox-filters-form-base>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
`
|
|
123
|
+
|
|
124
|
+
export const Regular = Template.bind({})
|
|
125
|
+
Regular.args = {
|
|
126
|
+
filters: [
|
|
127
|
+
{
|
|
128
|
+
name: 'search',
|
|
129
|
+
type: 'search',
|
|
130
|
+
label: 'search',
|
|
131
|
+
operator: 'eq'
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: 'test',
|
|
135
|
+
type: 'string',
|
|
136
|
+
label: 'name',
|
|
137
|
+
operator: 'eq',
|
|
138
|
+
value: 'No Name'
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'active',
|
|
142
|
+
type: 'boolean',
|
|
143
|
+
label: 'active',
|
|
144
|
+
operator: 'eq',
|
|
145
|
+
value: true,
|
|
146
|
+
onchange: (value, formBase: OxFiltersFormBase) => {
|
|
147
|
+
const sss = formBase.getFieldByName('sss') as HTMLInputElement
|
|
148
|
+
sss.value = value ? 'DEF' : 'ABC'
|
|
149
|
+
|
|
150
|
+
return false
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: 'sss',
|
|
155
|
+
type: 'select',
|
|
156
|
+
label: 'sss',
|
|
157
|
+
operator: 'eq',
|
|
158
|
+
options: () => {
|
|
159
|
+
return [
|
|
160
|
+
{
|
|
161
|
+
display: '',
|
|
162
|
+
value: ''
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
display: 'ABC',
|
|
166
|
+
value: 'ABC'
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
display: 'DEF',
|
|
170
|
+
value: 'DEF'
|
|
171
|
+
}
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: 'range',
|
|
177
|
+
type: 'number',
|
|
178
|
+
label: 'range',
|
|
179
|
+
operator: 'between'
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: 'date-range',
|
|
183
|
+
type: 'date',
|
|
184
|
+
label: 'date',
|
|
185
|
+
operator: 'between',
|
|
186
|
+
value: [
|
|
187
|
+
{ name: 'week', params: { relativeWeeks: -1 } },
|
|
188
|
+
{ name: 'today', params: { relativeDays: +1 } }
|
|
189
|
+
]
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
}
|
|
@@ -214,7 +214,11 @@ RegularContact.args = {
|
|
|
214
214
|
name: 'name',
|
|
215
215
|
type: 'text',
|
|
216
216
|
placeholder: 'type your name.',
|
|
217
|
-
props: { searchOper: 'i_like' }
|
|
217
|
+
props: { searchOper: 'i_like' },
|
|
218
|
+
onchange: value => {
|
|
219
|
+
console.log('changed', value)
|
|
220
|
+
return false
|
|
221
|
+
}
|
|
218
222
|
},
|
|
219
223
|
{
|
|
220
224
|
label: 'email',
|