settings-menu-select.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  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-5'
  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. }
  28. export default function SettingsMenuSelect<T extends PossibleValue = string>({
  29. label,
  30. name,
  31. options,
  32. optgroup,
  33. loading,
  34. onChange,
  35. value,
  36. disabled = false,
  37. }: SettingsMenuSelectProps<T>) {
  38. const handleChange: ChangeEventHandler<HTMLSelectElement> = useCallback(
  39. event => {
  40. const selectedValue = event.target.value
  41. let onChangeValue: PossibleValue = selectedValue
  42. if (typeof value === 'boolean') {
  43. onChangeValue = selectedValue === 'true'
  44. } else if (typeof value === 'number') {
  45. onChangeValue = parseInt(selectedValue, 10)
  46. }
  47. onChange(onChangeValue as T)
  48. },
  49. [onChange, value]
  50. )
  51. const { settingToFocus } = useEditorLeftMenuContext()
  52. const selectRef = useRef<HTMLSelectElement | null>(null)
  53. useEffect(() => {
  54. if (settingToFocus === name && selectRef.current) {
  55. selectRef.current.scrollIntoView({
  56. block: 'center',
  57. behavior: 'smooth',
  58. })
  59. selectRef.current.focus()
  60. }
  61. // clear the focus setting
  62. window.dispatchEvent(
  63. new CustomEvent('ui.focus-setting', { detail: undefined })
  64. )
  65. }, [name, settingToFocus])
  66. return (
  67. <OLFormGroup
  68. controlId={`settings-menu-${name}`}
  69. className="left-menu-setting"
  70. >
  71. <OLFormLabel>{label}</OLFormLabel>
  72. {loading ? (
  73. <p className="mb-0">
  74. <Spinner
  75. animation="border"
  76. aria-hidden="true"
  77. size="sm"
  78. role="status"
  79. />
  80. </p>
  81. ) : (
  82. <OLFormSelect
  83. size="sm"
  84. onChange={handleChange}
  85. value={value?.toString()}
  86. disabled={disabled}
  87. ref={selectRef}
  88. >
  89. {options.map(option => (
  90. <option
  91. key={`${name}-${option.value}`}
  92. value={option.value.toString()}
  93. aria-hidden={option.ariaHidden}
  94. disabled={option.disabled}
  95. >
  96. {option.label}
  97. </option>
  98. ))}
  99. {optgroup ? (
  100. <optgroup label={optgroup.label}>
  101. {optgroup.options.map(option => (
  102. <option
  103. value={option.value.toString()}
  104. key={option.value.toString()}
  105. >
  106. {option.label}
  107. </option>
  108. ))}
  109. </optgroup>
  110. ) : null}
  111. </OLFormSelect>
  112. )}
  113. </OLFormGroup>
  114. )
  115. }