haya-select 1.0.19 → 1.0.21
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/package.json +1 -1
- package/src/select/index.jsx +161 -86
- package/src/select/style.scss +6 -1
package/package.json
CHANGED
package/src/select/index.jsx
CHANGED
|
@@ -10,7 +10,7 @@ import Option from "./option"
|
|
|
10
10
|
import OptionGroup from "./option-group"
|
|
11
11
|
import PropTypes from "prop-types"
|
|
12
12
|
import React from "react"
|
|
13
|
-
import {anythingDifferent} from "set-state-compare"
|
|
13
|
+
import {anythingDifferent, Shape} from "set-state-compare"
|
|
14
14
|
|
|
15
15
|
const nameForComponentWithMultiple = (component) => {
|
|
16
16
|
let name = nameForComponent(component)
|
|
@@ -51,24 +51,27 @@ export default class HayaSelect extends React.PureComponent {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
p = fetchingObject(() => this.props)
|
|
54
|
-
s = fetchingObject(() => this.
|
|
54
|
+
s = fetchingObject(() => this.shape)
|
|
55
55
|
t = fetchingObject(this)
|
|
56
56
|
|
|
57
57
|
endOfSelectRef = React.createRef()
|
|
58
58
|
optionsContainerRef = React.createRef()
|
|
59
|
+
currentSelectedRef = React.createRef()
|
|
59
60
|
searchTextInputRef = React.createRef()
|
|
60
61
|
|
|
61
|
-
|
|
62
|
+
shape = new Shape(this, {
|
|
62
63
|
currentOptions: this.defaultCurrentOptions(),
|
|
63
64
|
loadedOptions: this.defaultLoadedOptions(),
|
|
64
65
|
opened: false,
|
|
65
66
|
optionsLeft: undefined,
|
|
67
|
+
optionsPlacement: undefined,
|
|
66
68
|
optionsTop: undefined,
|
|
69
|
+
optionsVisibility: undefined,
|
|
67
70
|
optionsWidth: undefined,
|
|
68
71
|
scrollLeft: undefined,
|
|
69
72
|
scrollTop: undefined,
|
|
70
73
|
toggled: this.defaultToggled()
|
|
71
|
-
}
|
|
74
|
+
})
|
|
72
75
|
|
|
73
76
|
defaultCurrentOptions() {
|
|
74
77
|
const {defaultValue, defaultValues, values} = this.props
|
|
@@ -105,34 +108,40 @@ export default class HayaSelect extends React.PureComponent {
|
|
|
105
108
|
if (((defaultValue || defaultValues || defaultValuesFromOptions) || (attribute && model)) && typeof options == "function") {
|
|
106
109
|
this.loadDefaultValuesFromOptionsCallback()
|
|
107
110
|
}
|
|
111
|
+
|
|
112
|
+
window.addEventListener("resize", this.t.onAnythingResizedDebounced, true)
|
|
113
|
+
window.addEventListener("scroll", this.t.onAnythingScrolledDebounced, true)
|
|
108
114
|
}
|
|
109
115
|
|
|
110
116
|
componentDidUpdate() {
|
|
111
|
-
|
|
112
|
-
const stateUpdate = {}
|
|
117
|
+
const newState = {}
|
|
113
118
|
|
|
114
|
-
|
|
115
|
-
|
|
119
|
+
if ("values" in this.props) {
|
|
120
|
+
const newCurrentOptions = this.defaultCurrentOptions()
|
|
116
121
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
122
|
+
if (anythingDifferent(this.shape.currentOptions, newCurrentOptions)) {
|
|
123
|
+
newState.currentOptions = newCurrentOptions
|
|
120
124
|
}
|
|
125
|
+
}
|
|
121
126
|
|
|
122
|
-
|
|
123
|
-
|
|
127
|
+
if ("toggled" in this.props) {
|
|
128
|
+
const newToggled = this.p.toggled
|
|
124
129
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
130
|
+
if (anythingDifferent(this.shape.toggled, newToggled)) {
|
|
131
|
+
newState.toggled = newToggled
|
|
128
132
|
}
|
|
133
|
+
}
|
|
129
134
|
|
|
130
|
-
|
|
131
|
-
|
|
135
|
+
this.shape.set(newState)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
componentWillUnmount() {
|
|
139
|
+
window.removeEventListener("resize", this.t.onAnythingResizedDebounced)
|
|
140
|
+
window.removeEventListener("scroll", this.t.onAnythingScrolledDebounced)
|
|
132
141
|
}
|
|
133
142
|
|
|
134
143
|
render() {
|
|
135
|
-
const {endOfSelectRef} = this.t
|
|
144
|
+
const {currentSelectedRef, endOfSelectRef} = this.t
|
|
136
145
|
const {
|
|
137
146
|
attribute,
|
|
138
147
|
className,
|
|
@@ -150,7 +159,7 @@ export default class HayaSelect extends React.PureComponent {
|
|
|
150
159
|
toggleOptions,
|
|
151
160
|
...restProps
|
|
152
161
|
} = this.props
|
|
153
|
-
const {currentOptions, opened} = this.s
|
|
162
|
+
const {currentOptions, opened, optionsPlacement} = this.s
|
|
154
163
|
const BodyPortal = config.getBodyPortal()
|
|
155
164
|
|
|
156
165
|
return (
|
|
@@ -168,14 +177,16 @@ export default class HayaSelect extends React.PureComponent {
|
|
|
168
177
|
<div
|
|
169
178
|
className="haya-select-current-selected"
|
|
170
179
|
data-opened={opened}
|
|
171
|
-
|
|
180
|
+
data-options-placement={optionsPlacement}
|
|
181
|
+
onClick={this.t.onSelectClicked}
|
|
182
|
+
ref={currentSelectedRef}
|
|
172
183
|
>
|
|
173
184
|
{opened &&
|
|
174
185
|
<input
|
|
175
186
|
className="haya-select-search-text-input"
|
|
176
|
-
onChange={
|
|
187
|
+
onChange={this.tonSearchTextInputChangedDebounced}
|
|
177
188
|
placeholder={I18n.t("haya_select.search_dot_dot_dot")} type="text"
|
|
178
|
-
ref={
|
|
189
|
+
ref={this.t.searchTextInputRef}
|
|
179
190
|
/>
|
|
180
191
|
}
|
|
181
192
|
{!opened &&
|
|
@@ -235,18 +246,16 @@ export default class HayaSelect extends React.PureComponent {
|
|
|
235
246
|
if (!defaultValues) return
|
|
236
247
|
|
|
237
248
|
const result = await this.props.options({
|
|
238
|
-
searchValue: digg(this
|
|
249
|
+
searchValue: digg(this.t.searchTextInputRef, "current")?.value,
|
|
239
250
|
values: defaultValues
|
|
240
251
|
})
|
|
241
252
|
|
|
242
|
-
this.
|
|
243
|
-
currentOptions: prevState.currentOptions.concat(result)
|
|
244
|
-
}))
|
|
253
|
+
this.shape.set({currentOptions: this.shape.currentOptions.concat(result)})
|
|
245
254
|
}
|
|
246
255
|
|
|
247
256
|
loadOptions = async () => {
|
|
248
257
|
const {options} = this.p
|
|
249
|
-
const searchValue = digg(this
|
|
258
|
+
const searchValue = digg(this.t.searchTextInputRef, "current")?.value
|
|
250
259
|
|
|
251
260
|
if (Array.isArray(options)) {
|
|
252
261
|
return this.loadOptionsFromArray(options, searchValue)
|
|
@@ -254,7 +263,7 @@ export default class HayaSelect extends React.PureComponent {
|
|
|
254
263
|
|
|
255
264
|
const loadedOptions = await options({searchValue})
|
|
256
265
|
|
|
257
|
-
this.
|
|
266
|
+
this.shape.set({loadedOptions})
|
|
258
267
|
}
|
|
259
268
|
|
|
260
269
|
hayaSelectOption({key, loadedOption}) {
|
|
@@ -263,7 +272,7 @@ export default class HayaSelect extends React.PureComponent {
|
|
|
263
272
|
}
|
|
264
273
|
|
|
265
274
|
return <Option
|
|
266
|
-
currentOptions={this.
|
|
275
|
+
currentOptions={this.shape.currentOptions}
|
|
267
276
|
icon={this.iconForOption(loadedOption)}
|
|
268
277
|
key={key}
|
|
269
278
|
option={loadedOption}
|
|
@@ -276,7 +285,7 @@ export default class HayaSelect extends React.PureComponent {
|
|
|
276
285
|
const lowerSearchValue = searchValue?.toLowerCase()
|
|
277
286
|
const loadedOptions = options.filter(({text}) => !lowerSearchValue || text.toLowerCase().includes(lowerSearchValue))
|
|
278
287
|
|
|
279
|
-
this.
|
|
288
|
+
this.shape.set({loadedOptions})
|
|
280
289
|
}
|
|
281
290
|
|
|
282
291
|
onSelectClicked = (e) => {
|
|
@@ -292,39 +301,110 @@ export default class HayaSelect extends React.PureComponent {
|
|
|
292
301
|
}
|
|
293
302
|
}
|
|
294
303
|
|
|
295
|
-
onSearchTextInputChangedDebounced = debounce(
|
|
304
|
+
onSearchTextInputChangedDebounced = debounce(this.t.loadOptions, 200)
|
|
296
305
|
|
|
297
306
|
closeOptions() {
|
|
298
|
-
this.
|
|
307
|
+
this.shape.set({
|
|
299
308
|
loadedOptions: undefined,
|
|
300
309
|
opened: false
|
|
301
310
|
})
|
|
302
311
|
|
|
303
312
|
if (this.props.onOptionsClosed) {
|
|
304
|
-
this.props.onOptionsClosed({options: this.
|
|
313
|
+
this.props.onOptionsClosed({options: this.shape.currentOptions})
|
|
305
314
|
}
|
|
306
315
|
}
|
|
307
316
|
|
|
308
317
|
openOptions() {
|
|
318
|
+
this.setOptionsPositionBelow()
|
|
319
|
+
this.loadOptions()
|
|
320
|
+
this.shape.set(
|
|
321
|
+
{opened: true},
|
|
322
|
+
() => {
|
|
323
|
+
this.focusTextInput()
|
|
324
|
+
this.setOptionsPositionAboveIfOutsideScreen()
|
|
325
|
+
}
|
|
326
|
+
)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
focusTextInput = () => digg(this.t.searchTextInputRef, "current").focus()
|
|
330
|
+
|
|
331
|
+
setOptionsPosition() {
|
|
332
|
+
this.setOptionsPositionBelow()
|
|
333
|
+
this.setOptionsPositionAboveIfOutsideScreen()
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
setOptionsPositionAboveIfOutsideScreen() {
|
|
337
|
+
const {optionsContainerRef} = this.t
|
|
338
|
+
const {optionsTop} = this.s
|
|
339
|
+
const optionsHeight = digg(optionsContainerRef, "current", "offsetHeight")
|
|
340
|
+
const scrollTop = document.documentElement.scrollTop
|
|
341
|
+
const optionsTotalHeight = optionsHeight + optionsTop + scrollTop
|
|
342
|
+
|
|
343
|
+
if (window.innerHeight < optionsTotalHeight) {
|
|
344
|
+
this.setOptionsPositionAbove()
|
|
345
|
+
} else {
|
|
346
|
+
this.shape.set({optionsVisibility: "visible"})
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
setOptionsPositionAbove() {
|
|
351
|
+
const {optionsContainerRef, currentSelectedRef} = this.t
|
|
352
|
+
const optionsHeight = digg(optionsContainerRef, "current", "offsetHeight")
|
|
353
|
+
const position = currentSelectedRef.current.getBoundingClientRect()
|
|
354
|
+
const {left, top, width} = digs(position, "left", "top", "width")
|
|
355
|
+
const optionsTop = top - optionsHeight + 2
|
|
356
|
+
|
|
357
|
+
this.shape.set(
|
|
358
|
+
{
|
|
359
|
+
opened: true,
|
|
360
|
+
optionsLeft: left,
|
|
361
|
+
optionsPlacement: "above",
|
|
362
|
+
optionsTop,
|
|
363
|
+
optionsVisibility: "visible",
|
|
364
|
+
optionsWidth: width,
|
|
365
|
+
scrollLeft: document.documentElement.scrollLeft,
|
|
366
|
+
scrollTop: document.documentElement.scrollTop
|
|
367
|
+
},
|
|
368
|
+
() => digg(this.t.searchTextInputRef, "current").focus()
|
|
369
|
+
)
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
setOptionsPositionBelow() {
|
|
309
373
|
const {endOfSelectRef} = this.t
|
|
310
374
|
const position = endOfSelectRef.current.getBoundingClientRect()
|
|
311
375
|
const {left, top, width} = digs(position, "left", "top", "width")
|
|
312
376
|
|
|
313
|
-
this.
|
|
314
|
-
|
|
315
|
-
this.setState(
|
|
377
|
+
this.shape.set(
|
|
316
378
|
{
|
|
317
379
|
opened: true,
|
|
318
380
|
optionsLeft: left,
|
|
381
|
+
optionsPlacement: "below",
|
|
319
382
|
optionsTop: top,
|
|
383
|
+
optionsVisibility: "hidden",
|
|
320
384
|
optionsWidth: width,
|
|
321
385
|
scrollLeft: document.documentElement.scrollLeft,
|
|
322
386
|
scrollTop: document.documentElement.scrollTop
|
|
323
387
|
},
|
|
324
|
-
() => digg(this
|
|
388
|
+
() => digg(this.t.searchTextInputRef, "current").focus()
|
|
325
389
|
)
|
|
326
390
|
}
|
|
327
391
|
|
|
392
|
+
onAnythingResized = (e) => {
|
|
393
|
+
if (this.s.opened) {
|
|
394
|
+
this.setOptionsPosition()
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
onAnythingResizedDebounced = debounce(this.t.onAnythingResized, 25)
|
|
399
|
+
|
|
400
|
+
onAnythingScrolled = (e) => {
|
|
401
|
+
if (this.s.opened) {
|
|
402
|
+
this.setOptionsPosition()
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
onAnythingScrolledDebounced = debounce(this.t.onAnythingScrolled, 25)
|
|
407
|
+
|
|
328
408
|
onWindowClicked = (e) => {
|
|
329
409
|
const {optionsContainerRef} = this.t
|
|
330
410
|
const {opened} = this.s
|
|
@@ -337,7 +417,7 @@ export default class HayaSelect extends React.PureComponent {
|
|
|
337
417
|
|
|
338
418
|
optionsContainer() {
|
|
339
419
|
const {optionsContainerRef} = this.t
|
|
340
|
-
const {loadedOptions, optionsLeft, optionsTop, optionsWidth, scrollLeft, scrollTop} = this.s
|
|
420
|
+
const {loadedOptions, optionsLeft, optionsTop, optionsVisibility, optionsWidth, scrollLeft, scrollTop} = this.s
|
|
341
421
|
|
|
342
422
|
return (
|
|
343
423
|
<div
|
|
@@ -347,6 +427,7 @@ export default class HayaSelect extends React.PureComponent {
|
|
|
347
427
|
style={{
|
|
348
428
|
left: optionsLeft + scrollLeft,
|
|
349
429
|
top: optionsTop + scrollTop - 1,
|
|
430
|
+
visibility: optionsVisibility,
|
|
350
431
|
width: optionsWidth,
|
|
351
432
|
}}
|
|
352
433
|
>
|
|
@@ -372,63 +453,57 @@ export default class HayaSelect extends React.PureComponent {
|
|
|
372
453
|
|
|
373
454
|
const {onChange, toggleOptions} = this.props
|
|
374
455
|
const {multiple} = this.p
|
|
456
|
+
const {currentOptions, toggled} = this.shape
|
|
457
|
+
const newState = {}
|
|
458
|
+
const existingOption = currentOptions.find((currentOption) => currentOption.value == loadedOption.value)
|
|
459
|
+
const newToggled = {...toggled}
|
|
375
460
|
|
|
376
|
-
|
|
377
|
-
(
|
|
378
|
-
const
|
|
379
|
-
const newState = {}
|
|
380
|
-
const {toggled} = digs(prevState, "toggled")
|
|
381
|
-
const newToggled = {...toggled}
|
|
461
|
+
if (existingOption) {
|
|
462
|
+
if (toggleOptions) {
|
|
463
|
+
const currentIndex = digg(toggled, loadedOption.value)
|
|
382
464
|
|
|
383
|
-
if (
|
|
384
|
-
|
|
385
|
-
const currentIndex = digg(toggled, loadedOption.value)
|
|
465
|
+
if (currentIndex >= (toggleOptions.length - 1)) {
|
|
466
|
+
delete newToggled[loadedOption.value]
|
|
386
467
|
|
|
387
|
-
|
|
388
|
-
delete newToggled[loadedOption.value]
|
|
389
|
-
|
|
390
|
-
newState.currentOptions = prevState.currentOptions.filter((currentOption) => currentOption.value != loadedOption.value)
|
|
391
|
-
} else {
|
|
392
|
-
newToggled[loadedOption.value] = toggled[loadedOption.value] + 1
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
newState.toggled = newToggled
|
|
396
|
-
} else {
|
|
397
|
-
newState.currentOptions = prevState.currentOptions.filter((currentOption) => currentOption.value != loadedOption.value)
|
|
398
|
-
}
|
|
468
|
+
newState.currentOptions = currentOptions.filter((currentOption) => currentOption.value != loadedOption.value)
|
|
399
469
|
} else {
|
|
400
|
-
|
|
401
|
-
newToggled[loadedOption.value] = 0
|
|
402
|
-
newState.toggled = newToggled
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
if (multiple || toggleOptions) {
|
|
406
|
-
newState.currentOptions = prevState.currentOptions.concat([loadedOption])
|
|
407
|
-
} else {
|
|
408
|
-
newState.currentOptions = [loadedOption]
|
|
409
|
-
}
|
|
470
|
+
newToggled[loadedOption.value] = toggled[loadedOption.value] + 1
|
|
410
471
|
}
|
|
411
472
|
|
|
412
|
-
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
473
|
+
newState.toggled = newToggled
|
|
474
|
+
} else {
|
|
475
|
+
newState.currentOptions = currentOptions.filter((currentOption) => currentOption.value != loadedOption.value)
|
|
476
|
+
}
|
|
477
|
+
} else {
|
|
478
|
+
if (toggleOptions) {
|
|
479
|
+
newToggled[loadedOption.value] = 0
|
|
480
|
+
newState.toggled = newToggled
|
|
481
|
+
}
|
|
417
482
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
483
|
+
if (multiple || toggleOptions) {
|
|
484
|
+
newState.currentOptions = currentOptions.concat([loadedOption])
|
|
485
|
+
} else {
|
|
486
|
+
newState.currentOptions = [loadedOption]
|
|
487
|
+
}
|
|
488
|
+
}
|
|
421
489
|
|
|
422
|
-
|
|
423
|
-
}
|
|
490
|
+
this.shape.set(newState)
|
|
424
491
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
492
|
+
if (onChange) {
|
|
493
|
+
const toggles = {}
|
|
494
|
+
|
|
495
|
+
for(const toggleKey in this.shape.toggled) {
|
|
496
|
+
const toggleValue = digg(this.s.toggled, toggleKey)
|
|
497
|
+
const toggleOption = digg(this.p.toggleOptions, toggleValue)
|
|
498
|
+
|
|
499
|
+
toggles[toggleKey] = toggleOption
|
|
430
500
|
}
|
|
431
|
-
|
|
501
|
+
|
|
502
|
+
onChange({
|
|
503
|
+
options: this.shape.currentOptions,
|
|
504
|
+
toggles
|
|
505
|
+
})
|
|
506
|
+
}
|
|
432
507
|
|
|
433
508
|
if (!multiple) this.closeOptions()
|
|
434
509
|
}
|
package/src/select/style.scss
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
flex-wrap: wrap;
|
|
5
5
|
background-color: #fff;
|
|
6
6
|
border: 1px solid #999;
|
|
7
|
+
color: #000;
|
|
7
8
|
cursor: pointer;
|
|
8
9
|
padding-top: 5px;
|
|
9
10
|
padding-bottom: 5px;
|
|
@@ -13,7 +14,11 @@
|
|
|
13
14
|
border-radius: 4px;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
&[data-opened="true"] {
|
|
17
|
+
&[data-opened="true"][data-options-placement="above"] {
|
|
18
|
+
border-radius: 0 0 4px 4px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
&[data-opened="true"][data-options-placement="below"] {
|
|
17
22
|
border-radius: 4px 4px 0 0;
|
|
18
23
|
}
|
|
19
24
|
|