@furystack/shades-common-components 10.0.1 → 10.0.2
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/esm/components/button.d.ts.map +1 -1
- package/esm/components/button.js +9 -8
- package/esm/components/button.js.map +1 -1
- package/esm/components/command-palette/command-palette-input.d.ts.map +1 -1
- package/esm/components/command-palette/command-palette-input.js +1 -1
- package/esm/components/command-palette/command-palette-input.js.map +1 -1
- package/esm/components/command-palette/command-palette-suggestion-list.d.ts.map +1 -1
- package/esm/components/command-palette/command-palette-suggestion-list.js +7 -5
- package/esm/components/command-palette/command-palette-suggestion-list.js.map +1 -1
- package/esm/components/command-palette/index.d.ts.map +1 -1
- package/esm/components/command-palette/index.js +10 -10
- package/esm/components/command-palette/index.js.map +1 -1
- package/esm/components/data-grid/footer.d.ts +1 -1
- package/esm/components/data-grid/footer.d.ts.map +1 -1
- package/esm/components/data-grid/footer.js.map +1 -1
- package/esm/components/data-grid/header.d.ts.map +1 -1
- package/esm/components/data-grid/header.js +6 -6
- package/esm/components/data-grid/header.js.map +1 -1
- package/esm/components/form.d.ts.map +1 -1
- package/esm/components/form.js +3 -1
- package/esm/components/form.js.map +1 -1
- package/esm/components/inputs/autocomplete.d.ts.map +1 -1
- package/esm/components/inputs/autocomplete.js.map +1 -1
- package/esm/components/inputs/input.d.ts +5 -0
- package/esm/components/inputs/input.d.ts.map +1 -1
- package/esm/components/inputs/input.js +8 -6
- package/esm/components/inputs/input.js.map +1 -1
- package/esm/components/inputs/text-area.d.ts.map +1 -1
- package/esm/components/inputs/text-area.js +4 -4
- package/esm/components/inputs/text-area.js.map +1 -1
- package/esm/components/loader.d.ts.map +1 -1
- package/esm/components/loader.js +2 -2
- package/esm/components/loader.js.map +1 -1
- package/esm/components/modal.d.ts.map +1 -1
- package/esm/components/modal.js +1 -1
- package/esm/components/modal.js.map +1 -1
- package/esm/components/noty-list.d.ts.map +1 -1
- package/esm/components/noty-list.js +2 -9
- package/esm/components/noty-list.js.map +1 -1
- package/esm/components/skeleton.d.ts.map +1 -1
- package/esm/components/skeleton.js +2 -2
- package/esm/components/skeleton.js.map +1 -1
- package/esm/components/suggest/index.d.ts.map +1 -1
- package/esm/components/suggest/index.js +10 -10
- package/esm/components/suggest/index.js.map +1 -1
- package/esm/components/suggest/suggestion-list.d.ts.map +1 -1
- package/esm/components/suggest/suggestion-list.js +7 -5
- package/esm/components/suggest/suggestion-list.js.map +1 -1
- package/esm/services/collection-service.d.ts.map +1 -1
- package/esm/services/collection-service.js +17 -7
- package/esm/services/collection-service.js.map +1 -1
- package/esm/utils/promisify-animation.spec.js +7 -3
- package/esm/utils/promisify-animation.spec.js.map +1 -1
- package/package.json +7 -7
- package/src/components/button.tsx +9 -8
- package/src/components/command-palette/command-palette-input.tsx +1 -1
- package/src/components/command-palette/command-palette-suggestion-list.tsx +7 -5
- package/src/components/command-palette/index.tsx +10 -10
- package/src/components/data-grid/footer.tsx +5 -5
- package/src/components/data-grid/header.tsx +11 -9
- package/src/components/form.tsx +3 -1
- package/src/components/inputs/autocomplete.tsx +1 -1
- package/src/components/inputs/input.tsx +14 -7
- package/src/components/inputs/text-area.tsx +4 -4
- package/src/components/loader.tsx +2 -2
- package/src/components/modal.tsx +1 -1
- package/src/components/noty-list.tsx +3 -11
- package/src/components/skeleton.tsx +2 -2
- package/src/components/suggest/index.tsx +26 -18
- package/src/components/suggest/suggestion-list.tsx +7 -5
- package/src/services/collection-service.ts +16 -8
- package/src/utils/promisify-animation.spec.ts +7 -3
|
@@ -30,15 +30,15 @@ export const CommandPalette = Shade<CommandPaletteProps>({
|
|
|
30
30
|
useDisposable('clickAwayService', () => new ClickAwayService(element, () => manager.isOpened.setValue(false)))
|
|
31
31
|
|
|
32
32
|
const [isLoadingAtRender] = useObservable('isLoading', manager.isLoading, {
|
|
33
|
-
onChange:
|
|
33
|
+
onChange: (isLoading) => {
|
|
34
34
|
const loader = element.querySelector('.loader-container')
|
|
35
35
|
if (isLoading) {
|
|
36
|
-
promisifyAnimation(loader, [{ opacity: 0 }, { opacity: 1 }], {
|
|
36
|
+
void promisifyAnimation(loader, [{ opacity: 0 }, { opacity: 1 }], {
|
|
37
37
|
duration: 100,
|
|
38
38
|
fill: 'forwards',
|
|
39
39
|
})
|
|
40
40
|
} else {
|
|
41
|
-
promisifyAnimation(loader, [{ opacity: 1 }, { opacity: 0 }], {
|
|
41
|
+
void promisifyAnimation(loader, [{ opacity: 1 }, { opacity: 0 }], {
|
|
42
42
|
duration: 100,
|
|
43
43
|
fill: 'forwards',
|
|
44
44
|
})
|
|
@@ -53,17 +53,17 @@ export const CommandPalette = Shade<CommandPaletteProps>({
|
|
|
53
53
|
const postControls = element.querySelector('.post-controls')
|
|
54
54
|
const inputContainer = element.querySelector('.input-container') as HTMLDivElement
|
|
55
55
|
if (isOpened) {
|
|
56
|
-
promisifyAnimation(suggestions, [{ opacity: 0 }, { opacity: 1 }], {
|
|
56
|
+
void promisifyAnimation(suggestions, [{ opacity: 0 }, { opacity: 1 }], {
|
|
57
57
|
duration: 500,
|
|
58
58
|
fill: 'forwards',
|
|
59
59
|
})
|
|
60
60
|
|
|
61
|
-
promisifyAnimation(postControls, [{ width: '0px' }, { width: '50px' }], {
|
|
61
|
+
void promisifyAnimation(postControls, [{ width: '0px' }, { width: '50px' }], {
|
|
62
62
|
duration: 100,
|
|
63
63
|
fill: 'forwards',
|
|
64
64
|
})
|
|
65
65
|
|
|
66
|
-
promisifyAnimation(
|
|
66
|
+
void promisifyAnimation(
|
|
67
67
|
inputContainer,
|
|
68
68
|
[{ background: 'transparent' }, { background: theme.background.default }],
|
|
69
69
|
{
|
|
@@ -73,18 +73,18 @@ export const CommandPalette = Shade<CommandPaletteProps>({
|
|
|
73
73
|
},
|
|
74
74
|
)
|
|
75
75
|
} else {
|
|
76
|
-
promisifyAnimation(suggestions, [{ opacity: 1 }, { opacity: 0 }], {
|
|
76
|
+
void promisifyAnimation(suggestions, [{ opacity: 1 }, { opacity: 0 }], {
|
|
77
77
|
duration: 500,
|
|
78
78
|
fill: 'forwards',
|
|
79
79
|
})
|
|
80
80
|
|
|
81
|
-
promisifyAnimation(postControls, [{ width: '50px' }, { width: '0px' }], {
|
|
81
|
+
void promisifyAnimation(postControls, [{ width: '50px' }, { width: '0px' }], {
|
|
82
82
|
duration: 500,
|
|
83
83
|
fill: 'forwards',
|
|
84
84
|
delay: 300,
|
|
85
85
|
})
|
|
86
86
|
|
|
87
|
-
promisifyAnimation(
|
|
87
|
+
void promisifyAnimation(
|
|
88
88
|
inputContainer,
|
|
89
89
|
[{ background: theme.background.default }, { background: 'transparent' }],
|
|
90
90
|
{
|
|
@@ -118,7 +118,7 @@ export const CommandPalette = Shade<CommandPaletteProps>({
|
|
|
118
118
|
)
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
manager.getSuggestion({ injector, term: (ev.target as
|
|
121
|
+
void manager.getSuggestion({ injector, term: (ev.target as HTMLInputElement).value })
|
|
122
122
|
}}
|
|
123
123
|
>
|
|
124
124
|
<div
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Shade, createComponent } from '@furystack/shades'
|
|
2
|
-
import { ThemeProviderService } from '../../services/theme-provider-service.js'
|
|
3
|
-
import type { CollectionService } from '../../services/collection-service.js'
|
|
4
1
|
import type { FindOptions } from '@furystack/core'
|
|
2
|
+
import { Shade, createComponent } from '@furystack/shades'
|
|
5
3
|
import type { ObservableValue } from '@furystack/utils'
|
|
4
|
+
import type { CollectionService } from '../../services/collection-service.js'
|
|
5
|
+
import { ThemeProviderService } from '../../services/theme-provider-service.js'
|
|
6
6
|
|
|
7
7
|
export const dataGridItemsPerPage = [10, 20, 25, 50, 100, Infinity]
|
|
8
8
|
|
|
@@ -47,7 +47,7 @@ export const DataGridFooter = Shade<{
|
|
|
47
47
|
<select
|
|
48
48
|
style={{ margin: '0 1em' }}
|
|
49
49
|
onchange={(ev) => {
|
|
50
|
-
const value = parseInt((ev.target as
|
|
50
|
+
const value = parseInt((ev.target as HTMLInputElement).value, 10)
|
|
51
51
|
setCurrentOptions({ ...currentOptions, skip: (currentOptions.top || 0) * value })
|
|
52
52
|
}}
|
|
53
53
|
>
|
|
@@ -64,7 +64,7 @@ export const DataGridFooter = Shade<{
|
|
|
64
64
|
<select
|
|
65
65
|
style={{ margin: '0 1em' }}
|
|
66
66
|
onchange={(ev) => {
|
|
67
|
-
const value = parseInt((ev.currentTarget as
|
|
67
|
+
const value = parseInt((ev.currentTarget as HTMLInputElement).value, 10)
|
|
68
68
|
setCurrentOptions({
|
|
69
69
|
...currentOptions,
|
|
70
70
|
top: value,
|
|
@@ -109,7 +109,7 @@ const SearchForm = Shade<{
|
|
|
109
109
|
},
|
|
110
110
|
})
|
|
111
111
|
|
|
112
|
-
const currentValue = (findOptions.filter?.[props.fieldName] as
|
|
112
|
+
const currentValue = (findOptions.filter?.[props.fieldName] as FilterType<Record<string, string>>)?.$regex || ''
|
|
113
113
|
|
|
114
114
|
return (
|
|
115
115
|
<Form<SearchSubmitType>
|
|
@@ -122,7 +122,9 @@ const SearchForm = Shade<{
|
|
|
122
122
|
justifyContent: 'space-around',
|
|
123
123
|
opacity: '0',
|
|
124
124
|
}}
|
|
125
|
-
validate={(data): data is SearchSubmitType =>
|
|
125
|
+
validate={(data): data is SearchSubmitType =>
|
|
126
|
+
typeof (data as SearchSubmitType).searchValue?.length === 'number'
|
|
127
|
+
}
|
|
126
128
|
onSubmit={({ searchValue }) => {
|
|
127
129
|
props.onSubmit(searchValue)
|
|
128
130
|
}}
|
|
@@ -133,7 +135,7 @@ const SearchForm = Shade<{
|
|
|
133
135
|
autofocus
|
|
134
136
|
labelTitle={`${props.fieldName}`}
|
|
135
137
|
name="searchValue"
|
|
136
|
-
value={currentValue}
|
|
138
|
+
value={typeof currentValue === 'string' ? currentValue : ''}
|
|
137
139
|
labelProps={{
|
|
138
140
|
style: { padding: '0px 2em' },
|
|
139
141
|
}}
|
|
@@ -171,15 +173,15 @@ export const DataGridHeader: <T, Column extends string>(
|
|
|
171
173
|
const searchForm = element.querySelector('.search-form') as HTMLElement
|
|
172
174
|
const headerContent = element.querySelector('.header-content') as HTMLElement
|
|
173
175
|
if (!newValue) {
|
|
174
|
-
collapse(searchForm)
|
|
175
|
-
expand(headerContent)
|
|
176
|
+
void collapse(searchForm)
|
|
177
|
+
void expand(headerContent)
|
|
176
178
|
} else {
|
|
177
179
|
searchForm.style.display = 'flex'
|
|
178
|
-
expand(searchForm).then(async () => {
|
|
180
|
+
void expand(searchForm).then(async () => {
|
|
179
181
|
await sleepAsync(100)
|
|
180
182
|
searchForm.querySelector('input')?.focus()
|
|
181
183
|
})
|
|
182
|
-
collapse(headerContent)
|
|
184
|
+
void collapse(headerContent)
|
|
183
185
|
}
|
|
184
186
|
},
|
|
185
187
|
})
|
|
@@ -188,13 +190,13 @@ export const DataGridHeader: <T, Column extends string>(
|
|
|
188
190
|
|
|
189
191
|
const updateSearchValue = (value?: string) => {
|
|
190
192
|
if (value) {
|
|
191
|
-
const newSettings
|
|
193
|
+
const newSettings = {
|
|
192
194
|
...findOptions,
|
|
193
195
|
filter: {
|
|
194
196
|
...findOptions.filter,
|
|
195
197
|
[props.field]: { $regex: value },
|
|
196
198
|
},
|
|
197
|
-
}
|
|
199
|
+
} as typeof findOptions
|
|
198
200
|
setFindOptions(newSettings)
|
|
199
201
|
} else {
|
|
200
202
|
const { [props.field]: _, ...newFilter } = findOptions.filter || {}
|
package/src/components/form.tsx
CHANGED
|
@@ -74,7 +74,9 @@ export const Form: <T>(props: FormProps<T>, children: ChildrenList) => JSX.Eleme
|
|
|
74
74
|
} else if (props.validate(formData)) {
|
|
75
75
|
formService.validationResult.setValue({ isValid: true })
|
|
76
76
|
formService.validatedFormData.setValue(formData)
|
|
77
|
-
|
|
77
|
+
if (shouldSubmit) {
|
|
78
|
+
props.onSubmit(formData)
|
|
79
|
+
}
|
|
78
80
|
} else {
|
|
79
81
|
formService.validationResult.setValue({ isValid: false, reason: 'validation-failed' })
|
|
80
82
|
}
|
|
@@ -21,7 +21,7 @@ export const Autocomplete = Shade<{
|
|
|
21
21
|
<Input
|
|
22
22
|
{...props.inputProps}
|
|
23
23
|
onchange={(ev) => {
|
|
24
|
-
const { value } = ev.target as
|
|
24
|
+
const { value } = ev.target as HTMLInputElement
|
|
25
25
|
if (props.strict) {
|
|
26
26
|
if (!props.suggestions.includes(value)) {
|
|
27
27
|
;(ev.target as HTMLInputElement).setCustomValidity('Please select a valid entry!')
|
|
@@ -61,6 +61,12 @@ export interface TextInputProps extends PartialElement<HTMLInputElement> {
|
|
|
61
61
|
getEndIcon?: (options: { state: TextInputState; validationResult?: InputValidationResult }) => JSX.Element | string
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
declare global {
|
|
65
|
+
interface ValidityState {
|
|
66
|
+
toJSON: () => Partial<ValidityState>
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
64
70
|
export type TextInputState = {
|
|
65
71
|
value: string
|
|
66
72
|
focused: boolean
|
|
@@ -175,7 +181,7 @@ export const Input = Shade<TextInputProps>({
|
|
|
175
181
|
|
|
176
182
|
newState.value = input?.value || newState.value
|
|
177
183
|
newState.validity = input?.validity || newState.validity
|
|
178
|
-
|
|
184
|
+
newState.validity.toJSON = () => {
|
|
179
185
|
return {
|
|
180
186
|
valid: newState.validity.valid,
|
|
181
187
|
valueMissing: newState.validity.valueMissing,
|
|
@@ -192,9 +198,11 @@ export const Input = Shade<TextInputProps>({
|
|
|
192
198
|
|
|
193
199
|
const validationResult = props.getValidationResult?.({ state: newState })
|
|
194
200
|
|
|
195
|
-
validationResult?.isValid === false || newState.validity?.valid === false
|
|
196
|
-
|
|
197
|
-
|
|
201
|
+
if (validationResult?.isValid === false || newState.validity?.valid === false) {
|
|
202
|
+
element.setAttribute('data-validation-failed', 'true')
|
|
203
|
+
} else {
|
|
204
|
+
element.removeAttribute('data-validation-failed')
|
|
205
|
+
}
|
|
198
206
|
|
|
199
207
|
attachStyles(label, { style: getLabelStyle({ themeProvider, props, state: newState, validationResult }) })
|
|
200
208
|
|
|
@@ -215,7 +223,6 @@ export const Input = Shade<TextInputProps>({
|
|
|
215
223
|
const formService = injector.getInstance(FormService)
|
|
216
224
|
formService.setFieldState(props.name as keyof unknown, validationResult || { isValid: true }, newState.validity)
|
|
217
225
|
}
|
|
218
|
-
return newState
|
|
219
226
|
}
|
|
220
227
|
|
|
221
228
|
const [state, setState] = useObservable<TextInputState>(
|
|
@@ -247,12 +254,12 @@ export const Input = Shade<TextInputProps>({
|
|
|
247
254
|
const el = ev.target as HTMLInputElement
|
|
248
255
|
setState({ ...state, validity: el.validity })
|
|
249
256
|
}}
|
|
250
|
-
onchange={(ev)
|
|
257
|
+
onchange={function (ev) {
|
|
251
258
|
const el = ev.target as HTMLInputElement
|
|
252
259
|
const newValue = el.value
|
|
253
260
|
setState({ ...state, value: newValue, validity: el?.validity })
|
|
254
261
|
props.onTextChange?.(newValue)
|
|
255
|
-
props
|
|
262
|
+
props?.onchange?.call(this, ev)
|
|
256
263
|
}}
|
|
257
264
|
onfocus={(ev) => {
|
|
258
265
|
const el = ev.target as HTMLInputElement
|
|
@@ -50,7 +50,7 @@ export const TextArea = Shade<TextAreaProps>({
|
|
|
50
50
|
}}
|
|
51
51
|
onfocus={() => {
|
|
52
52
|
if (!props.disabled) {
|
|
53
|
-
promisifyAnimation(
|
|
53
|
+
void promisifyAnimation(
|
|
54
54
|
element.querySelector('label'),
|
|
55
55
|
[{ color: themeProvider.getTextColor(theme.background.default) }, { color: palette.primary.main }],
|
|
56
56
|
{
|
|
@@ -59,7 +59,7 @@ export const TextArea = Shade<TextAreaProps>({
|
|
|
59
59
|
fill: 'forwards',
|
|
60
60
|
},
|
|
61
61
|
)
|
|
62
|
-
promisifyAnimation(
|
|
62
|
+
void promisifyAnimation(
|
|
63
63
|
element.querySelector('div[contenteditable="true"]'),
|
|
64
64
|
[
|
|
65
65
|
{ boxShadow: '0px 0px 0px rgba(128,128,128,0.1)' },
|
|
@@ -74,7 +74,7 @@ export const TextArea = Shade<TextAreaProps>({
|
|
|
74
74
|
}}
|
|
75
75
|
onblur={() => {
|
|
76
76
|
if (!props.disabled) {
|
|
77
|
-
promisifyAnimation(
|
|
77
|
+
void promisifyAnimation(
|
|
78
78
|
element.querySelector('label'),
|
|
79
79
|
[{ color: palette.primary.main }, { color: themeProvider.getTextColor(theme.background.default) }],
|
|
80
80
|
{
|
|
@@ -83,7 +83,7 @@ export const TextArea = Shade<TextAreaProps>({
|
|
|
83
83
|
fill: 'forwards',
|
|
84
84
|
},
|
|
85
85
|
)
|
|
86
|
-
promisifyAnimation(
|
|
86
|
+
void promisifyAnimation(
|
|
87
87
|
element.querySelector('div[contenteditable="true"]'),
|
|
88
88
|
[
|
|
89
89
|
{ boxShadow: '0px 3px 0px rgba(128,128,128,0.4)' },
|
|
@@ -38,12 +38,12 @@ export const Loader = Shade<LoaderProps>({
|
|
|
38
38
|
const { borderColor = theme.palette.primary.main } = props
|
|
39
39
|
|
|
40
40
|
setTimeout(() => {
|
|
41
|
-
promisifyAnimation(element, [{ opacity: '0' }, { opacity: '1' }], {
|
|
41
|
+
void promisifyAnimation(element, [{ opacity: '0' }, { opacity: '1' }], {
|
|
42
42
|
duration: 500,
|
|
43
43
|
delay,
|
|
44
44
|
fill: 'forwards',
|
|
45
45
|
})
|
|
46
|
-
promisifyAnimation(
|
|
46
|
+
void promisifyAnimation(
|
|
47
47
|
element.firstElementChild,
|
|
48
48
|
[{ transform: 'rotate(0deg)' }, { transform: 'rotate(180deg)' }, { transform: 'rotate(360deg)' }],
|
|
49
49
|
{
|
package/src/components/modal.tsx
CHANGED
|
@@ -25,7 +25,7 @@ export const NotyComponent = Shade<{ model: NotyModel; onDismiss: () => void }>(
|
|
|
25
25
|
constructed: ({ element }) => {
|
|
26
26
|
setTimeout(() => {
|
|
27
27
|
const height = element.scrollHeight || 80
|
|
28
|
-
promisifyAnimation(
|
|
28
|
+
void promisifyAnimation(
|
|
29
29
|
element,
|
|
30
30
|
[
|
|
31
31
|
{ opacity: '0', height: '0px' },
|
|
@@ -73,21 +73,13 @@ export const NotyComponent = Shade<{ model: NotyModel; onDismiss: () => void }>(
|
|
|
73
73
|
|
|
74
74
|
const timeout = props.model.timeout || getDefaultNotyTimeouts(props.model.type)
|
|
75
75
|
if (timeout) {
|
|
76
|
-
setTimeout(removeSelf, timeout)
|
|
76
|
+
setTimeout(() => void removeSelf(), timeout)
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
element.className = `noty ${props.model.type}`
|
|
80
80
|
element.style.backgroundColor = colors.main
|
|
81
81
|
element.style.color = textColor
|
|
82
82
|
|
|
83
|
-
// attachProps(element, {
|
|
84
|
-
// className: `noty ${props.model.type}`,
|
|
85
|
-
// style: {
|
|
86
|
-
// backgroundColor: colors.main,
|
|
87
|
-
// color: textColor,
|
|
88
|
-
// },
|
|
89
|
-
// })
|
|
90
|
-
|
|
91
83
|
return (
|
|
92
84
|
<>
|
|
93
85
|
<div
|
|
@@ -153,7 +145,7 @@ export const NotyList = Shade({
|
|
|
153
145
|
useDisposable('removeNoty', () =>
|
|
154
146
|
notyService.subscribe('onNotyRemoved', (n) => {
|
|
155
147
|
element.querySelectorAll('shade-noty').forEach((e) => {
|
|
156
|
-
if ((e as JSX.Element).props.model === n) {
|
|
148
|
+
if ((e as JSX.Element<{ model?: NotyModel }>).props.model === n) {
|
|
157
149
|
e.remove()
|
|
158
150
|
}
|
|
159
151
|
})
|
|
@@ -22,13 +22,13 @@ export const Skeleton = Shade<SkeletonProps>({
|
|
|
22
22
|
render: ({ element, props }) => {
|
|
23
23
|
const { delay = 1500 } = props
|
|
24
24
|
setTimeout(() => {
|
|
25
|
-
promisifyAnimation(element, [{ opacity: 0 }, { opacity: 1 }], {
|
|
25
|
+
void promisifyAnimation(element, [{ opacity: 0 }, { opacity: 1 }], {
|
|
26
26
|
fill: 'forwards',
|
|
27
27
|
duration: 300,
|
|
28
28
|
easing: 'ease-out',
|
|
29
29
|
delay,
|
|
30
30
|
}).then(() => {
|
|
31
|
-
promisifyAnimation(
|
|
31
|
+
void promisifyAnimation(
|
|
32
32
|
element,
|
|
33
33
|
[{ backgroundPosition: '0% 50%' }, { backgroundPosition: '100% 50%' }, { backgroundPosition: '0% 50%' }],
|
|
34
34
|
{
|
|
@@ -38,49 +38,57 @@ export const Suggest: <T>(props: SuggestProps<T>, children: ChildrenList) => JSX
|
|
|
38
38
|
const suggestions = element.querySelector('.close-suggestions')
|
|
39
39
|
const postControls = element.querySelector('.post-controls')
|
|
40
40
|
if (isOpened) {
|
|
41
|
-
promisifyAnimation(suggestions, [{ opacity: 0 }, { opacity: 1 }], {
|
|
41
|
+
void promisifyAnimation(suggestions, [{ opacity: 0 }, { opacity: 1 }], {
|
|
42
42
|
duration: 500,
|
|
43
43
|
fill: 'forwards',
|
|
44
44
|
})
|
|
45
45
|
|
|
46
|
-
promisifyAnimation(postControls, [{ width: '0px' }, { width: '50px' }], {
|
|
46
|
+
void promisifyAnimation(postControls, [{ width: '0px' }, { width: '50px' }], {
|
|
47
47
|
duration: 100,
|
|
48
48
|
fill: 'forwards',
|
|
49
49
|
})
|
|
50
50
|
|
|
51
|
-
promisifyAnimation(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
void promisifyAnimation(
|
|
52
|
+
inputContainer,
|
|
53
|
+
[{ background: 'transparent' }, { background: theme.background.default }],
|
|
54
|
+
{
|
|
55
|
+
duration: 500,
|
|
56
|
+
fill: 'forwards',
|
|
57
|
+
easing: 'cubic-bezier(0.050, 0.570, 0.840, 1.005)',
|
|
58
|
+
},
|
|
59
|
+
)
|
|
56
60
|
} else {
|
|
57
|
-
promisifyAnimation(suggestions, [{ opacity: 1 }, { opacity: 0 }], {
|
|
61
|
+
void promisifyAnimation(suggestions, [{ opacity: 1 }, { opacity: 0 }], {
|
|
58
62
|
duration: 500,
|
|
59
63
|
fill: 'forwards',
|
|
60
64
|
})
|
|
61
65
|
|
|
62
|
-
promisifyAnimation(postControls, [{ width: '50px' }, { width: '0px' }], {
|
|
66
|
+
void promisifyAnimation(postControls, [{ width: '50px' }, { width: '0px' }], {
|
|
63
67
|
duration: 500,
|
|
64
68
|
fill: 'forwards',
|
|
65
69
|
delay: 300,
|
|
66
70
|
})
|
|
67
71
|
|
|
68
|
-
promisifyAnimation(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
void promisifyAnimation(
|
|
73
|
+
inputContainer,
|
|
74
|
+
[{ background: theme.background.default }, { background: 'transparent' }],
|
|
75
|
+
{
|
|
76
|
+
duration: 300,
|
|
77
|
+
fill: 'forwards',
|
|
78
|
+
easing: 'cubic-bezier(0.000, 0.245, 0.190, 0.790)',
|
|
79
|
+
},
|
|
80
|
+
)
|
|
73
81
|
}
|
|
74
82
|
})
|
|
75
|
-
manager.isLoading.subscribe(
|
|
83
|
+
manager.isLoading.subscribe((isLoading) => {
|
|
76
84
|
const loader = element.querySelector('shade-loader')
|
|
77
85
|
if (isLoading) {
|
|
78
|
-
promisifyAnimation(loader, [{ opacity: 0 }, { opacity: 1 }], {
|
|
86
|
+
void promisifyAnimation(loader, [{ opacity: 0 }, { opacity: 1 }], {
|
|
79
87
|
duration: 100,
|
|
80
88
|
fill: 'forwards',
|
|
81
89
|
})
|
|
82
90
|
} else {
|
|
83
|
-
promisifyAnimation(loader, [{ opacity: 1 }, { opacity: 0 }], {
|
|
91
|
+
void promisifyAnimation(loader, [{ opacity: 1 }, { opacity: 0 }], {
|
|
84
92
|
duration: 100,
|
|
85
93
|
fill: 'forwards',
|
|
86
94
|
})
|
|
@@ -107,7 +115,7 @@ export const Suggest: <T>(props: SuggestProps<T>, children: ChildrenList) => JSX
|
|
|
107
115
|
)
|
|
108
116
|
}
|
|
109
117
|
|
|
110
|
-
manager.getSuggestion({ injector, term: (ev.target as
|
|
118
|
+
void manager.getSuggestion({ injector, term: (ev.target as HTMLInputElement).value })
|
|
111
119
|
}}
|
|
112
120
|
>
|
|
113
121
|
<div
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ChildrenList } from '@furystack/shades'
|
|
2
2
|
import { Shade, createComponent } from '@furystack/shades'
|
|
3
|
+
import { ThemeProviderService } from '../../services/theme-provider-service.js'
|
|
3
4
|
import { promisifyAnimation } from '../../utils/promisify-animation.js'
|
|
4
5
|
import type { SuggestManager } from './suggest-manager.js'
|
|
5
|
-
import { ThemeProviderService } from '../../services/theme-provider-service.js'
|
|
6
6
|
|
|
7
7
|
export const SuggestionList: <T>(props: { manager: SuggestManager<T> }, children: ChildrenList) => JSX.Element<any> =
|
|
8
8
|
Shade<{ manager: SuggestManager<any> }>({
|
|
@@ -29,14 +29,14 @@ export const SuggestionList: <T>(props: { manager: SuggestManager<T> }, children
|
|
|
29
29
|
})
|
|
30
30
|
|
|
31
31
|
const [isListOpened] = useObservable('isOpened', manager.isOpened, {
|
|
32
|
-
onChange:
|
|
32
|
+
onChange: (isOpened) => {
|
|
33
33
|
const container = element.firstElementChild as HTMLDivElement
|
|
34
34
|
if (isOpened) {
|
|
35
35
|
container.style.zIndex = '1'
|
|
36
36
|
container.style.width = `calc(${Math.round(
|
|
37
37
|
element.parentElement?.getBoundingClientRect().width || 200,
|
|
38
38
|
)}px - 3em)`
|
|
39
|
-
|
|
39
|
+
void promisifyAnimation(
|
|
40
40
|
container,
|
|
41
41
|
[
|
|
42
42
|
{ opacity: 0, transform: 'translate(0, -50px)' },
|
|
@@ -45,7 +45,7 @@ export const SuggestionList: <T>(props: { manager: SuggestManager<T> }, children
|
|
|
45
45
|
{ fill: 'forwards', duration: 500 },
|
|
46
46
|
)
|
|
47
47
|
} else {
|
|
48
|
-
|
|
48
|
+
void promisifyAnimation(
|
|
49
49
|
container,
|
|
50
50
|
[
|
|
51
51
|
{ opacity: 1, transform: 'translate(0, 0)' },
|
|
@@ -80,7 +80,9 @@ export const SuggestionList: <T>(props: { manager: SuggestManager<T> }, children
|
|
|
80
80
|
<div
|
|
81
81
|
className="suggestion-item"
|
|
82
82
|
onclick={() => {
|
|
83
|
-
|
|
83
|
+
if (isListOpened) {
|
|
84
|
+
manager.selectSuggestion(i)
|
|
85
|
+
}
|
|
84
86
|
}}
|
|
85
87
|
style={{
|
|
86
88
|
padding: '1em',
|
|
@@ -44,7 +44,11 @@ export class CollectionService<T> implements Disposable {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
public toggleSelection = (entry: T) => {
|
|
47
|
-
this.isSelected(entry)
|
|
47
|
+
if (this.isSelected(entry)) {
|
|
48
|
+
this.removeFromSelection(entry)
|
|
49
|
+
} else {
|
|
50
|
+
this.addToSelection(entry)
|
|
51
|
+
}
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
public data = new ObservableValue<CollectionData<T>>({ count: 0, entries: [] })
|
|
@@ -68,12 +72,13 @@ export class CollectionService<T> implements Disposable {
|
|
|
68
72
|
switch (ev.key) {
|
|
69
73
|
case ' ':
|
|
70
74
|
ev.preventDefault()
|
|
71
|
-
focusedEntry
|
|
75
|
+
if (focusedEntry) {
|
|
72
76
|
this.selection.setValue(
|
|
73
77
|
selectedEntries.includes(focusedEntry)
|
|
74
78
|
? selectedEntries.filter((e) => e !== focusedEntry)
|
|
75
79
|
: [...selectedEntries, focusedEntry],
|
|
76
80
|
)
|
|
81
|
+
}
|
|
77
82
|
break
|
|
78
83
|
case '*':
|
|
79
84
|
this.selection.setValue(entries.filter((e) => !selectedEntries.includes(e)))
|
|
@@ -85,11 +90,14 @@ export class CollectionService<T> implements Disposable {
|
|
|
85
90
|
this.selection.setValue([])
|
|
86
91
|
break
|
|
87
92
|
case 'Insert':
|
|
88
|
-
focusedEntry
|
|
89
|
-
(this.selection.getValue().includes(focusedEntry)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
if (focusedEntry) {
|
|
94
|
+
if (this.selection.getValue().includes(focusedEntry)) {
|
|
95
|
+
this.selection.setValue([...this.selection.getValue().filter((e) => e !== focusedEntry)])
|
|
96
|
+
} else {
|
|
97
|
+
this.selection.setValue([...this.selection.getValue(), focusedEntry])
|
|
98
|
+
}
|
|
99
|
+
this.focusedEntry.setValue(entries[entries.findIndex((e) => e === this.focusedEntry.getValue()) + 1])
|
|
100
|
+
}
|
|
93
101
|
|
|
94
102
|
break
|
|
95
103
|
case 'ArrowUp':
|
|
@@ -125,7 +133,7 @@ export class CollectionService<T> implements Disposable {
|
|
|
125
133
|
const newFocusedEntry = entries.find(
|
|
126
134
|
(e) =>
|
|
127
135
|
this.options.searchField &&
|
|
128
|
-
(e[this.options.searchField] as
|
|
136
|
+
(e[this.options.searchField] as string)?.toString().startsWith(newSearchExpression),
|
|
129
137
|
)
|
|
130
138
|
this.focusedEntry.setValue(newFocusedEntry)
|
|
131
139
|
this.searchTerm.setValue(newSearchExpression)
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import { sleepAsync } from '@furystack/utils'
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
2
3
|
import { promisifyAnimation } from './promisify-animation.js'
|
|
3
|
-
import { describe, it, expect, vi } from 'vitest'
|
|
4
4
|
|
|
5
5
|
describe('promisifyAnimation', () => {
|
|
6
6
|
it('should trigger the element animation', async () => {
|
|
7
7
|
const el = document.createElement('div')
|
|
8
|
-
const onfinish = vi.fn()
|
|
8
|
+
const onfinish = vi.fn(() => {})
|
|
9
9
|
const oncancel = vi.fn()
|
|
10
10
|
const animate = vi.fn(() => {
|
|
11
11
|
const animation = {
|
|
12
12
|
onfinish,
|
|
13
13
|
oncancel,
|
|
14
14
|
}
|
|
15
|
-
sleepAsync(100)
|
|
15
|
+
sleepAsync(100)
|
|
16
|
+
.then(() => animation.onfinish())
|
|
17
|
+
.catch(() => {
|
|
18
|
+
/** */
|
|
19
|
+
})
|
|
16
20
|
return animation
|
|
17
21
|
})
|
|
18
22
|
Object.assign(el, { animate })
|