settings-menu-select.tsx 3.4 KB

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