settings-menu-select.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
  2. import OLFormGroup from '@/features/ui/components/ol/ol-form-group'
  3. import OLFormLabel from '@/features/ui/components/ol/ol-form-label'
  4. import OLFormSelect from '@/features/ui/components/ol/ol-form-select'
  5. import { ChangeEventHandler, useCallback, useEffect, useRef } from 'react'
  6. import { Spinner } from 'react-bootstrap-5'
  7. import { useEditorLeftMenuContext } from '@/features/editor-left-menu/components/editor-left-menu-context'
  8. type PossibleValue = string | number | boolean
  9. export type Option<T extends PossibleValue = string> = {
  10. value: T
  11. label: string
  12. ariaHidden?: 'true' | 'false'
  13. disabled?: boolean
  14. }
  15. export type Optgroup<T extends PossibleValue = string> = {
  16. label: string
  17. options: Array<Option<T>>
  18. }
  19. type SettingsMenuSelectProps<T extends PossibleValue = string> = {
  20. label: string
  21. name: string
  22. options: Array<Option<T>>
  23. optgroup?: Optgroup<T>
  24. loading?: boolean
  25. onChange: (val: T) => void
  26. value?: T
  27. disabled?: boolean
  28. }
  29. export default function SettingsMenuSelect<T extends PossibleValue = string>({
  30. label,
  31. name,
  32. options,
  33. optgroup,
  34. loading,
  35. onChange,
  36. value,
  37. disabled = false,
  38. }: SettingsMenuSelectProps<T>) {
  39. const handleChange: ChangeEventHandler<HTMLSelectElement> = useCallback(
  40. event => {
  41. const selectedValue = event.target.value
  42. let onChangeValue: PossibleValue = selectedValue
  43. if (typeof value === 'boolean') {
  44. onChangeValue = selectedValue === 'true'
  45. } else if (typeof value === 'number') {
  46. onChangeValue = parseInt(selectedValue, 10)
  47. }
  48. onChange(onChangeValue as T)
  49. },
  50. [onChange, value]
  51. )
  52. const { settingToFocus } = useEditorLeftMenuContext()
  53. const selectRef = useRef<HTMLSelectElement | null>(null)
  54. useEffect(() => {
  55. if (settingToFocus === name && selectRef.current) {
  56. selectRef.current.scrollIntoView({
  57. block: 'center',
  58. behavior: 'smooth',
  59. })
  60. selectRef.current.focus()
  61. }
  62. // clear the focus setting
  63. window.dispatchEvent(
  64. new CustomEvent('ui.focus-setting', { detail: undefined })
  65. )
  66. }, [name, settingToFocus])
  67. return (
  68. <OLFormGroup
  69. controlId={`settings-menu-${name}`}
  70. className="left-menu-setting"
  71. >
  72. <OLFormLabel>{label}</OLFormLabel>
  73. {loading ? (
  74. <BootstrapVersionSwitcher
  75. bs3={
  76. <p className="loading pull-right">
  77. <i className="fa fa-fw fa-spin fa-refresh" />
  78. </p>
  79. }
  80. bs5={
  81. <p className="mb-0">
  82. <Spinner
  83. animation="border"
  84. aria-hidden="true"
  85. size="sm"
  86. role="status"
  87. />
  88. </p>
  89. }
  90. />
  91. ) : (
  92. <OLFormSelect
  93. size="sm"
  94. onChange={handleChange}
  95. value={value?.toString()}
  96. disabled={disabled}
  97. ref={selectRef}
  98. >
  99. {options.map(option => (
  100. <option
  101. key={`${name}-${option.value}`}
  102. value={option.value.toString()}
  103. aria-hidden={option.ariaHidden}
  104. disabled={option.disabled}
  105. >
  106. {option.label}
  107. </option>
  108. ))}
  109. {optgroup ? (
  110. <optgroup label={optgroup.label}>
  111. {optgroup.options.map(option => (
  112. <option
  113. value={option.value.toString()}
  114. key={option.value.toString()}
  115. >
  116. {option.label}
  117. </option>
  118. ))}
  119. </optgroup>
  120. ) : null}
  121. </OLFormSelect>
  122. )}
  123. </OLFormGroup>
  124. )
  125. }