@dr.pogodin/react-utils 1.46.1 → 1.47.0-alpha.1

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.
Files changed (238) hide show
  1. package/babel.config.js +1 -0
  2. package/babel.libweb.config.js +16 -0
  3. package/bin/build.js +24 -15
  4. package/bin/setup.js +11 -8
  5. package/build/development/client/getInj.js.map +1 -1
  6. package/build/development/client/index.js.map +1 -1
  7. package/build/development/client/init.js.map +1 -1
  8. package/build/development/index.js +7 -1
  9. package/build/development/index.js.map +1 -1
  10. package/build/development/server/Cache.js.map +1 -1
  11. package/build/development/server/index.js +13 -8
  12. package/build/development/server/index.js.map +1 -1
  13. package/build/development/server/renderer.js +15 -8
  14. package/build/development/server/renderer.js.map +1 -1
  15. package/build/development/server/server.js +7 -5
  16. package/build/development/server/server.js.map +1 -1
  17. package/build/development/shared/components/Button/index.js +6 -6
  18. package/build/development/shared/components/Button/index.js.map +1 -1
  19. package/build/development/shared/components/Checkbox/index.js +8 -8
  20. package/build/development/shared/components/Checkbox/index.js.map +1 -1
  21. package/build/development/shared/components/GenericLink/index.js +5 -1
  22. package/build/development/shared/components/GenericLink/index.js.map +1 -1
  23. package/build/development/shared/components/Input/index.js +10 -9
  24. package/build/development/shared/components/Input/index.js.map +1 -1
  25. package/build/development/shared/components/Link.js.map +1 -1
  26. package/build/development/shared/components/Modal/index.js +8 -6
  27. package/build/development/shared/components/Modal/index.js.map +1 -1
  28. package/build/development/shared/components/NavLink.js.map +1 -1
  29. package/build/development/shared/components/PageLayout/index.js +6 -6
  30. package/build/development/shared/components/PageLayout/index.js.map +1 -1
  31. package/build/development/shared/components/TextArea/index.js +13 -9
  32. package/build/development/shared/components/TextArea/index.js.map +1 -1
  33. package/build/development/shared/components/Throbber/index.js +6 -6
  34. package/build/development/shared/components/Throbber/index.js.map +1 -1
  35. package/build/development/shared/components/WithTooltip/Tooltip.js +1 -0
  36. package/build/development/shared/components/WithTooltip/Tooltip.js.map +1 -1
  37. package/build/development/shared/components/WithTooltip/index.js +7 -7
  38. package/build/development/shared/components/WithTooltip/index.js.map +1 -1
  39. package/build/development/shared/components/YouTubeVideo/index.js +9 -9
  40. package/build/development/shared/components/YouTubeVideo/index.js.map +1 -1
  41. package/build/development/shared/components/index.js.map +1 -1
  42. package/build/development/shared/components/selectors/CustomDropdown/Options/index.js +1 -1
  43. package/build/development/shared/components/selectors/CustomDropdown/Options/index.js.map +1 -1
  44. package/build/development/shared/components/selectors/CustomDropdown/index.js +11 -11
  45. package/build/development/shared/components/selectors/CustomDropdown/index.js.map +1 -1
  46. package/build/development/shared/components/selectors/NativeDropdown/index.js +12 -12
  47. package/build/development/shared/components/selectors/NativeDropdown/index.js.map +1 -1
  48. package/build/development/shared/components/selectors/Switch/index.js +7 -7
  49. package/build/development/shared/components/selectors/Switch/index.js.map +1 -1
  50. package/build/development/shared/utils/config.js.map +1 -1
  51. package/build/development/shared/utils/globalState.js.map +1 -1
  52. package/build/development/shared/utils/index.js.map +1 -1
  53. package/build/development/shared/utils/jest/E2eSsrEnv.js +14 -10
  54. package/build/development/shared/utils/jest/E2eSsrEnv.js.map +1 -1
  55. package/build/development/shared/utils/splitComponent.js +1 -0
  56. package/build/development/shared/utils/splitComponent.js.map +1 -1
  57. package/build/development/shared/utils/time.js.map +1 -1
  58. package/build/production/client/getInj.js.map +1 -1
  59. package/build/production/client/index.js.map +1 -1
  60. package/build/production/client/init.js.map +1 -1
  61. package/build/production/index.js +4 -1
  62. package/build/production/index.js.map +1 -1
  63. package/build/production/server/Cache.js.map +1 -1
  64. package/build/production/server/index.js +5 -5
  65. package/build/production/server/index.js.map +1 -1
  66. package/build/production/server/renderer.js +7 -7
  67. package/build/production/server/renderer.js.map +1 -1
  68. package/build/production/server/server.js +4 -4
  69. package/build/production/server/server.js.map +1 -1
  70. package/build/production/shared/components/Button/index.js +1 -1
  71. package/build/production/shared/components/Button/index.js.map +1 -1
  72. package/build/production/shared/components/Checkbox/index.js +1 -1
  73. package/build/production/shared/components/Checkbox/index.js.map +1 -1
  74. package/build/production/shared/components/GenericLink/index.js +2 -2
  75. package/build/production/shared/components/GenericLink/index.js.map +1 -1
  76. package/build/production/shared/components/Input/index.js +1 -1
  77. package/build/production/shared/components/Input/index.js.map +1 -1
  78. package/build/production/shared/components/Link.js.map +1 -1
  79. package/build/production/shared/components/Modal/index.js +1 -1
  80. package/build/production/shared/components/Modal/index.js.map +1 -1
  81. package/build/production/shared/components/NavLink.js.map +1 -1
  82. package/build/production/shared/components/PageLayout/index.js +1 -1
  83. package/build/production/shared/components/PageLayout/index.js.map +1 -1
  84. package/build/production/shared/components/TextArea/index.js +1 -1
  85. package/build/production/shared/components/TextArea/index.js.map +1 -1
  86. package/build/production/shared/components/Throbber/index.js +1 -1
  87. package/build/production/shared/components/Throbber/index.js.map +1 -1
  88. package/build/production/shared/components/WithTooltip/Tooltip.js.map +1 -1
  89. package/build/production/shared/components/WithTooltip/index.js +1 -1
  90. package/build/production/shared/components/WithTooltip/index.js.map +1 -1
  91. package/build/production/shared/components/YouTubeVideo/index.js +1 -1
  92. package/build/production/shared/components/YouTubeVideo/index.js.map +1 -1
  93. package/build/production/shared/components/index.js.map +1 -1
  94. package/build/production/shared/components/selectors/CustomDropdown/Options/index.js +1 -1
  95. package/build/production/shared/components/selectors/CustomDropdown/Options/index.js.map +1 -1
  96. package/build/production/shared/components/selectors/CustomDropdown/index.js +1 -1
  97. package/build/production/shared/components/selectors/CustomDropdown/index.js.map +1 -1
  98. package/build/production/shared/components/selectors/NativeDropdown/index.js +1 -1
  99. package/build/production/shared/components/selectors/NativeDropdown/index.js.map +1 -1
  100. package/build/production/shared/components/selectors/Switch/index.js +1 -1
  101. package/build/production/shared/components/selectors/Switch/index.js.map +1 -1
  102. package/build/production/shared/utils/config.js.map +1 -1
  103. package/build/production/shared/utils/globalState.js.map +1 -1
  104. package/build/production/shared/utils/index.js.map +1 -1
  105. package/build/production/shared/utils/jest/E2eSsrEnv.js +8 -7
  106. package/build/production/shared/utils/jest/E2eSsrEnv.js.map +1 -1
  107. package/build/production/shared/utils/splitComponent.js.map +1 -1
  108. package/build/production/shared/utils/time.js.map +1 -1
  109. package/build/web/client/getInj.js +54 -0
  110. package/build/web/client/getInj.js.map +1 -0
  111. package/build/web/client/index.js +38 -0
  112. package/build/web/client/index.js.map +1 -0
  113. package/build/web/client/init.js +49 -0
  114. package/build/web/client/init.js.map +1 -0
  115. package/build/web/index.js +25 -0
  116. package/build/web/index.js.map +1 -0
  117. package/build/web/server/Cache.js +54 -0
  118. package/build/web/server/Cache.js.map +1 -0
  119. package/build/web/server/index.js +211 -0
  120. package/build/web/server/index.js.map +1 -0
  121. package/build/web/server/renderer.js +516 -0
  122. package/build/web/server/renderer.js.map +1 -0
  123. package/build/web/server/server.js +231 -0
  124. package/build/web/server/server.js.map +1 -0
  125. package/build/web/server/utils/errors.js +103 -0
  126. package/build/web/server/utils/errors.js.map +1 -0
  127. package/build/web/server/utils/index.js +4 -0
  128. package/build/web/server/utils/index.js.map +1 -0
  129. package/build/web/shared/components/Button/index.js +90 -0
  130. package/build/web/shared/components/Button/index.js.map +1 -0
  131. package/build/web/shared/components/Button/style.scss +54 -0
  132. package/build/web/shared/components/Checkbox/index.js +35 -0
  133. package/build/web/shared/components/Checkbox/index.js.map +1 -0
  134. package/build/web/shared/components/Checkbox/theme.scss +63 -0
  135. package/build/web/shared/components/GenericLink/index.js +107 -0
  136. package/build/web/shared/components/GenericLink/index.js.map +1 -0
  137. package/build/web/shared/components/GenericLink/style.scss +3 -0
  138. package/build/web/shared/components/Input/index.js +71 -0
  139. package/build/web/shared/components/Input/index.js.map +1 -0
  140. package/build/web/shared/components/Input/theme.scss +52 -0
  141. package/build/web/shared/components/Link.js +20 -0
  142. package/build/web/shared/components/Link.js.map +1 -0
  143. package/build/web/shared/components/Modal/base-theme.scss +38 -0
  144. package/build/web/shared/components/Modal/index.js +128 -0
  145. package/build/web/shared/components/Modal/index.js.map +1 -0
  146. package/build/web/shared/components/Modal/styles.scss +3 -0
  147. package/build/web/shared/components/NavLink.js +13 -0
  148. package/build/web/shared/components/NavLink.js.map +1 -0
  149. package/build/web/shared/components/PageLayout/base-theme.scss +30 -0
  150. package/build/web/shared/components/PageLayout/index.js +38 -0
  151. package/build/web/shared/components/PageLayout/index.js.map +1 -0
  152. package/build/web/shared/components/TextArea/index.js +106 -0
  153. package/build/web/shared/components/TextArea/index.js.map +1 -0
  154. package/build/web/shared/components/TextArea/style.scss +68 -0
  155. package/build/web/shared/components/Throbber/index.js +26 -0
  156. package/build/web/shared/components/Throbber/index.js.map +1 -0
  157. package/build/web/shared/components/Throbber/theme.scss +26 -0
  158. package/build/web/shared/components/WithTooltip/Tooltip.js +226 -0
  159. package/build/web/shared/components/WithTooltip/Tooltip.js.map +1 -0
  160. package/build/web/shared/components/WithTooltip/default-theme.scss +36 -0
  161. package/build/web/shared/components/WithTooltip/index.js +117 -0
  162. package/build/web/shared/components/WithTooltip/index.js.map +1 -0
  163. package/build/web/shared/components/YouTubeVideo/base.scss +15 -0
  164. package/build/web/shared/components/YouTubeVideo/index.js +56 -0
  165. package/build/web/shared/components/YouTubeVideo/index.js.map +1 -0
  166. package/build/web/shared/components/YouTubeVideo/throbber.scss +11 -0
  167. package/build/web/shared/components/index.js +18 -0
  168. package/build/web/shared/components/index.js.map +1 -0
  169. package/build/web/shared/components/selectors/CustomDropdown/Options/index.js +79 -0
  170. package/build/web/shared/components/selectors/CustomDropdown/Options/index.js.map +1 -0
  171. package/build/web/shared/components/selectors/CustomDropdown/Options/style.scss +6 -0
  172. package/build/web/shared/components/selectors/CustomDropdown/index.js +120 -0
  173. package/build/web/shared/components/selectors/CustomDropdown/index.js.map +1 -0
  174. package/build/web/shared/components/selectors/CustomDropdown/theme.scss +118 -0
  175. package/build/web/shared/components/selectors/NativeDropdown/index.js +82 -0
  176. package/build/web/shared/components/selectors/NativeDropdown/index.js.map +1 -0
  177. package/build/web/shared/components/selectors/NativeDropdown/theme.scss +81 -0
  178. package/build/web/shared/components/selectors/Switch/index.js +49 -0
  179. package/build/web/shared/components/selectors/Switch/index.js.map +1 -0
  180. package/build/web/shared/components/selectors/Switch/theme.scss +39 -0
  181. package/build/web/shared/components/selectors/common.js +12 -0
  182. package/build/web/shared/components/selectors/common.js.map +1 -0
  183. package/build/web/shared/components/selectors/index.js +4 -0
  184. package/build/web/shared/components/selectors/index.js.map +1 -0
  185. package/build/web/shared/utils/config.js +21 -0
  186. package/build/web/shared/utils/config.js.map +1 -0
  187. package/build/web/shared/utils/globalState.js +15 -0
  188. package/build/web/shared/utils/globalState.js.map +1 -0
  189. package/build/web/shared/utils/index.js +13 -0
  190. package/build/web/shared/utils/index.js.map +1 -0
  191. package/build/web/shared/utils/isomorphy/buildInfo.js +44 -0
  192. package/build/web/shared/utils/isomorphy/buildInfo.js.map +1 -0
  193. package/build/web/shared/utils/isomorphy/environment-check.js +17 -0
  194. package/build/web/shared/utils/isomorphy/environment-check.js.map +1 -0
  195. package/build/web/shared/utils/isomorphy/index.js +36 -0
  196. package/build/web/shared/utils/isomorphy/index.js.map +1 -0
  197. package/build/web/shared/utils/jest/E2eSsrEnv.js +230 -0
  198. package/build/web/shared/utils/jest/E2eSsrEnv.js.map +1 -0
  199. package/build/web/shared/utils/jest/global.js +4 -0
  200. package/build/web/shared/utils/jest/global.js.map +1 -0
  201. package/build/web/shared/utils/jest/index.js +165 -0
  202. package/build/web/shared/utils/jest/index.js.map +1 -0
  203. package/build/web/shared/utils/splitComponent.js +221 -0
  204. package/build/web/shared/utils/splitComponent.js.map +1 -0
  205. package/build/web/shared/utils/time.js +85 -0
  206. package/build/web/shared/utils/time.js.map +1 -0
  207. package/build/web/shared/utils/webpack.js +61 -0
  208. package/build/web/shared/utils/webpack.js.map +1 -0
  209. package/build/web/styles/_global/reset.css +52 -0
  210. package/build/web/styles/_mixins/fonts.scss +40 -0
  211. package/build/web/styles/_mixins/media.scss +150 -0
  212. package/build/web/styles/_mixins/typography.scss +83 -0
  213. package/build/web/styles/global.scss +11 -0
  214. package/build/web/styles/mixins.scss +6 -0
  215. package/config/babel/node-ssr.js +5 -2
  216. package/config/webpack/app-base.js +10 -7
  217. package/config/webpack/app-development.js +3 -2
  218. package/package.json +29 -40
  219. package/src/index.ts +7 -1
  220. package/src/server/index.ts +6 -8
  221. package/src/server/renderer.tsx +7 -9
  222. package/src/server/server.ts +3 -5
  223. package/src/shared/components/Modal/base-theme.scss +1 -1
  224. package/src/shared/components/PageLayout/base-theme.scss +1 -1
  225. package/src/shared/utils/jest/E2eSsrEnv.ts +8 -1
  226. package/tstyche.config.json +1 -1
  227. package/build/development/__chunk_groups__.json +0 -6
  228. package/build/development/style.css +0 -909
  229. package/build/development/web.bundle.js +0 -752
  230. package/build/production/__chunk_groups__.json +0 -6
  231. package/build/production/style.css +0 -2
  232. package/build/production/style.css.map +0 -1
  233. package/build/production/web.bundle.js +0 -3
  234. package/build/production/web.bundle.js.LICENSE.txt +0 -9
  235. package/build/production/web.bundle.js.map +0 -1
  236. package/dev-styles.js +0 -1
  237. package/null.js +0 -1
  238. package/prod-styles.js +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["useEffect","useRef","useState","themed","Tooltip","PLACEMENTS","defaultTheme","jsx","_jsx","jsxs","_jsxs","Wrapper","children","placement","ABOVE_CURSOR","tip","theme","current","heap","lastCursorX","lastCursorY","timerId","undefined","triggeredByTouch","tooltipRef","wrapperRef","showTooltip","setShowTooltip","updatePortalPosition","cursorX","cursorY","wrapperRect","getBoundingClientRect","left","right","top","bottom","pointTo","window","scrollX","scrollY","setTimeout","listener","addEventListener","removeEventListener","className","wrapper","onClick","clearTimeout","onMouseLeave","onMouseMove","e","clientX","clientY","onTouchStart","ref","role","ThemedWrapper"],"sources":["../../../../../src/shared/components/WithTooltip/index.tsx"],"sourcesContent":["/* global window */\n\nimport {\n type FunctionComponent,\n type ReactNode,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\nimport themed, { type Theme } from '@dr.pogodin/react-themes';\n\nimport Tooltip, {\n type ThemeKeysT as TooltipThemeKeysT,\n PLACEMENTS,\n} from './Tooltip';\n\nimport defaultTheme from './default-theme.scss';\n\ntype PropsT = {\n children?: ReactNode;\n placement?: PLACEMENTS;\n tip?: ReactNode;\n theme: Theme<'wrapper' | TooltipThemeKeysT>;\n};\n\ntype TooltipRefT = {\n pointTo: (\n x: number,\n y: number,\n placement: PLACEMENTS,\n wrapperRef: HTMLDivElement,\n ) => void;\n};\n\ntype HeapT = {\n lastCursorX: number;\n lastCursorY: number;\n triggeredByTouch: boolean;\n timerId?: NodeJS.Timeout;\n};\n\n/**\n * Implements a simple to use and themeable tooltip component, _e.g._\n * ```js\n * <WithTooltip tip=\"This is example tooltip.\">\n * <p>Hover to see the tooltip.</p>\n * </WithTooltip>\n * ```\n * **Children:** Children are rendered in the place of `<WithTooltip>`,\n * and when hovered the tooltip is shown. By default the wrapper itself is\n * `<div>` block with `display: inline-block`.\n * @param tip &ndash; Anything React is able to render,\n * _e.g._ a tooltip text. This will be the tooltip content.\n * @param {WithTooltipTheme} props.theme _Ad hoc_ theme.\n */\nconst Wrapper: FunctionComponent<PropsT> = ({\n children,\n placement = PLACEMENTS.ABOVE_CURSOR,\n tip,\n theme,\n}) => {\n const { current: heap } = useRef<HeapT>({\n lastCursorX: 0,\n lastCursorY: 0,\n timerId: undefined,\n triggeredByTouch: false,\n });\n const tooltipRef = useRef<TooltipRefT>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const [showTooltip, setShowTooltip] = useState(false);\n\n const updatePortalPosition = (cursorX: number, cursorY: number) => {\n if (showTooltip) {\n const wrapperRect = wrapperRef.current!.getBoundingClientRect();\n if (\n cursorX < wrapperRect.left\n || cursorX > wrapperRect.right\n || cursorY < wrapperRect.top\n || cursorY > wrapperRect.bottom\n ) {\n setShowTooltip(false);\n } else if (tooltipRef.current) {\n tooltipRef.current.pointTo(\n cursorX + window.scrollX,\n cursorY + window.scrollY,\n placement,\n wrapperRef.current!,\n );\n }\n } else {\n heap.lastCursorX = cursorX;\n heap.lastCursorY = cursorY;\n\n // If tooltip was triggered by a touch, we delay its opening by a bit,\n // to ensure it was not a touch-click - in the case of touch click we\n // want to do the click, rather than show the tooltip, and the delay\n // gives click handler a chance to abort the tooltip openning.\n if (heap.triggeredByTouch) {\n heap.timerId ??= setTimeout(() => {\n heap.triggeredByTouch = false;\n heap.timerId = undefined;\n setShowTooltip(true);\n }, 300);\n\n // Otherwise we can just open the tooltip right away.\n } else setShowTooltip(true);\n }\n };\n\n useEffect(() => {\n if (showTooltip && tip !== null) {\n // This is necessary to ensure that even when a single mouse event\n // arrives to a tool-tipped component, the tooltip is correctly positioned\n // once opened (because similar call above does not have effect until\n // the tooltip is fully mounted, and that is delayed to future rendering\n // cycle due to the implementation).\n if (tooltipRef.current) {\n tooltipRef.current.pointTo(\n heap.lastCursorX + window.scrollX,\n heap.lastCursorY + window.scrollY,\n placement,\n wrapperRef.current!,\n );\n }\n\n const listener = () => {\n setShowTooltip(false);\n };\n window.addEventListener('scroll', listener);\n return () => {\n window.removeEventListener('scroll', listener);\n };\n }\n return undefined;\n }, [\n heap.lastCursorX,\n heap.lastCursorY,\n placement,\n showTooltip,\n tip,\n ]);\n\n return (\n <div\n className={theme.wrapper}\n onClick={() => {\n if (heap.timerId) {\n clearTimeout(heap.timerId);\n heap.timerId = undefined;\n heap.triggeredByTouch = false;\n }\n }}\n onMouseLeave={() => {\n setShowTooltip(false);\n }}\n onMouseMove={(e) => {\n updatePortalPosition(e.clientX, e.clientY);\n }}\n onTouchStart={() => {\n heap.triggeredByTouch = true;\n }}\n ref={wrapperRef}\n role=\"presentation\"\n >\n {\n showTooltip && tip !== null\n ? <Tooltip ref={tooltipRef} theme={theme}>{tip}</Tooltip>\n : null\n }\n {children}\n </div>\n );\n};\n\nconst ThemedWrapper = themed(Wrapper, 'WithTooltip', defaultTheme);\n\ntype ExportT = typeof ThemedWrapper & {\n PLACEMENTS: typeof PLACEMENTS;\n};\n\nconst e: ExportT = ThemedWrapper as ExportT;\n\ne.PLACEMENTS = PLACEMENTS;\n\nexport default e;\n"],"mappings":"AAAA;;AAEA,SAGEA,SAAS,EACTC,MAAM,EACNC,QAAQ,QACH,OAAO;AAEd,OAAOC,MAAM,MAAsB,0BAA0B;AAE7D,OAAOC,OAAO,IAEZC,UAAU;AAGZ,OAAOC,YAAY;AAA6B,SAAAC,GAAA,IAAAC,IAAA,EAAAC,IAAA,IAAAC,KAAA;AAyBhD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,OAAkC,GAAGA,CAAC;EAC1CC,QAAQ;EACRC,SAAS,GAAGR,UAAU,CAACS,YAAY;EACnCC,GAAG;EACHC;AACF,CAAC,KAAK;EACJ,MAAM;IAAEC,OAAO,EAAEC;EAAK,CAAC,GAAGjB,MAAM,CAAQ;IACtCkB,WAAW,EAAE,CAAC;IACdC,WAAW,EAAE,CAAC;IACdC,OAAO,EAAEC,SAAS;IAClBC,gBAAgB,EAAE;EACpB,CAAC,CAAC;EACF,MAAMC,UAAU,GAAGvB,MAAM,CAAc,IAAI,CAAC;EAC5C,MAAMwB,UAAU,GAAGxB,MAAM,CAAiB,IAAI,CAAC;EAC/C,MAAM,CAACyB,WAAW,EAAEC,cAAc,CAAC,GAAGzB,QAAQ,CAAC,KAAK,CAAC;EAErD,MAAM0B,oBAAoB,GAAGA,CAACC,OAAe,EAAEC,OAAe,KAAK;IACjE,IAAIJ,WAAW,EAAE;MACf,MAAMK,WAAW,GAAGN,UAAU,CAACR,OAAO,CAAEe,qBAAqB,CAAC,CAAC;MAC/D,IACEH,OAAO,GAAGE,WAAW,CAACE,IAAI,IACvBJ,OAAO,GAAGE,WAAW,CAACG,KAAK,IAC3BJ,OAAO,GAAGC,WAAW,CAACI,GAAG,IACzBL,OAAO,GAAGC,WAAW,CAACK,MAAM,EAC/B;QACAT,cAAc,CAAC,KAAK,CAAC;MACvB,CAAC,MAAM,IAAIH,UAAU,CAACP,OAAO,EAAE;QAC7BO,UAAU,CAACP,OAAO,CAACoB,OAAO,CACxBR,OAAO,GAAGS,MAAM,CAACC,OAAO,EACxBT,OAAO,GAAGQ,MAAM,CAACE,OAAO,EACxB3B,SAAS,EACTY,UAAU,CAACR,OACb,CAAC;MACH;IACF,CAAC,MAAM;MACLC,IAAI,CAACC,WAAW,GAAGU,OAAO;MAC1BX,IAAI,CAACE,WAAW,GAAGU,OAAO;;MAE1B;MACA;MACA;MACA;MACA,IAAIZ,IAAI,CAACK,gBAAgB,EAAE;QACzBL,IAAI,CAACG,OAAO,KAAKoB,UAAU,CAAC,MAAM;UAChCvB,IAAI,CAACK,gBAAgB,GAAG,KAAK;UAC7BL,IAAI,CAACG,OAAO,GAAGC,SAAS;UACxBK,cAAc,CAAC,IAAI,CAAC;QACtB,CAAC,EAAE,GAAG,CAAC;;QAET;MACA,CAAC,MAAMA,cAAc,CAAC,IAAI,CAAC;IAC7B;EACF,CAAC;EAED3B,SAAS,CAAC,MAAM;IACd,IAAI0B,WAAW,IAAIX,GAAG,KAAK,IAAI,EAAE;MAC/B;MACA;MACA;MACA;MACA;MACA,IAAIS,UAAU,CAACP,OAAO,EAAE;QACtBO,UAAU,CAACP,OAAO,CAACoB,OAAO,CACxBnB,IAAI,CAACC,WAAW,GAAGmB,MAAM,CAACC,OAAO,EACjCrB,IAAI,CAACE,WAAW,GAAGkB,MAAM,CAACE,OAAO,EACjC3B,SAAS,EACTY,UAAU,CAACR,OACb,CAAC;MACH;MAEA,MAAMyB,QAAQ,GAAGA,CAAA,KAAM;QACrBf,cAAc,CAAC,KAAK,CAAC;MACvB,CAAC;MACDW,MAAM,CAACK,gBAAgB,CAAC,QAAQ,EAAED,QAAQ,CAAC;MAC3C,OAAO,MAAM;QACXJ,MAAM,CAACM,mBAAmB,CAAC,QAAQ,EAAEF,QAAQ,CAAC;MAChD,CAAC;IACH;IACA,OAAOpB,SAAS;EAClB,CAAC,EAAE,CACDJ,IAAI,CAACC,WAAW,EAChBD,IAAI,CAACE,WAAW,EAChBP,SAAS,EACTa,WAAW,EACXX,GAAG,CACJ,CAAC;EAEF,oBACEL,KAAA;IACEmC,SAAS,EAAE7B,KAAK,CAAC8B,OAAQ;IACzBC,OAAO,EAAEA,CAAA,KAAM;MACb,IAAI7B,IAAI,CAACG,OAAO,EAAE;QAChB2B,YAAY,CAAC9B,IAAI,CAACG,OAAO,CAAC;QAC1BH,IAAI,CAACG,OAAO,GAAGC,SAAS;QACxBJ,IAAI,CAACK,gBAAgB,GAAG,KAAK;MAC/B;IACF,CAAE;IACF0B,YAAY,EAAEA,CAAA,KAAM;MAClBtB,cAAc,CAAC,KAAK,CAAC;IACvB,CAAE;IACFuB,WAAW,EAAGC,CAAC,IAAK;MAClBvB,oBAAoB,CAACuB,CAAC,CAACC,OAAO,EAAED,CAAC,CAACE,OAAO,CAAC;IAC5C,CAAE;IACFC,YAAY,EAAEA,CAAA,KAAM;MAClBpC,IAAI,CAACK,gBAAgB,GAAG,IAAI;IAC9B,CAAE;IACFgC,GAAG,EAAE9B,UAAW;IAChB+B,IAAI,EAAC,cAAc;IAAA5C,QAAA,GAGjBc,WAAW,IAAIX,GAAG,KAAK,IAAI,gBACvBP,IAAA,CAACJ,OAAO;MAACmD,GAAG,EAAE/B,UAAW;MAACR,KAAK,EAAEA,KAAM;MAAAJ,QAAA,EAAEG;IAAG,CAAU,CAAC,GACvD,IAAI,EAETH,QAAQ;EAAA,CACN,CAAC;AAEV,CAAC;AAED,MAAM6C,aAAa,GAAGtD,MAAM,CAACQ,OAAO,EAAE,aAAa,EAAEL,YAAY,CAAC;AAMlE,MAAM6C,CAAU,GAAGM,aAAwB;AAE3CN,CAAC,CAAC9C,UAAU,GAAGA,UAAU;AAEzB,eAAe8C,CAAC","ignoreList":[]}
@@ -0,0 +1,15 @@
1
+ *,
2
+ .context,
3
+ .ad.hoc {
4
+ .container {
5
+ aspect-ratio: 16 / 9;
6
+ background: whitesmoke;
7
+ position: relative;
8
+ }
9
+
10
+ .video {
11
+ height: 100%;
12
+ position: absolute;
13
+ width: 100%;
14
+ }
15
+ }
@@ -0,0 +1,56 @@
1
+ import qs from 'qs';
2
+ import themed from '@dr.pogodin/react-themes';
3
+ import Throbber from "../Throbber";
4
+ import baseTheme from "./base.scss";
5
+ import throbberTheme from "./throbber.scss";
6
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
+ /**
8
+ * A component for embeding a YouTube video.
9
+ * @param [props] Component properties.
10
+ * @param [props.autoplay] If `true` the video will start to play
11
+ * automatically once loaded.
12
+ * @param [props.src] URL of the video to play. Can be in any of
13
+ * the following formats, and keeps any additional query parameters understood
14
+ * by the YouTube IFrame player:
15
+ * - `https://www.youtube.com/watch?v=NdF6Rmt6Ado`
16
+ * - `https://youtu.be/NdF6Rmt6Ado`
17
+ * - `https://www.youtube.com/embed/NdF6Rmt6Ado`
18
+ * @param [props.theme] _Ad hoc_ theme.
19
+ * @param [props.title] The `title` attribute to add to the player
20
+ * IFrame.
21
+ */
22
+ const YouTubeVideo = ({
23
+ autoplay,
24
+ src,
25
+ theme,
26
+ title
27
+ }) => {
28
+ const srcParts = src.split('?');
29
+ let [url] = srcParts;
30
+ const [, queryString] = srcParts;
31
+ const query = queryString ? qs.parse(queryString) : {};
32
+ const videoId = query.v ?? url?.match(/\/([a-zA-Z0-9-_]*)$/)?.[1];
33
+ url = `https://www.youtube.com/embed/${videoId}`;
34
+ delete query.v;
35
+ query.autoplay = autoplay ? '1' : '0';
36
+ url += `?${qs.stringify(query)}`;
37
+
38
+ // TODO: https://developers.google.com/youtube/player_parameters
39
+ // More query parameters can be exposed via the component props.
40
+
41
+ return /*#__PURE__*/_jsxs("div", {
42
+ className: theme.container,
43
+ children: [/*#__PURE__*/_jsx(Throbber, {
44
+ theme: throbberTheme
45
+ }), /*#__PURE__*/_jsx("iframe", {
46
+ // eslint-disable-line react/iframe-missing-sandbox
47
+ allow: "autoplay",
48
+ allowFullScreen: true,
49
+ className: theme.video,
50
+ src: url,
51
+ title: title
52
+ })]
53
+ });
54
+ };
55
+ export default themed(YouTubeVideo, 'YouTubeVideo', baseTheme);
56
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["qs","themed","Throbber","baseTheme","throbberTheme","jsx","_jsx","jsxs","_jsxs","YouTubeVideo","autoplay","src","theme","title","srcParts","split","url","queryString","query","parse","videoId","v","match","stringify","className","container","children","allow","allowFullScreen","video"],"sources":["../../../../../src/shared/components/YouTubeVideo/index.tsx"],"sourcesContent":["import qs from 'qs';\n\nimport themed, { type Theme } from '@dr.pogodin/react-themes';\n\nimport Throbber from 'components/Throbber';\n\nimport baseTheme from './base.scss';\nimport throbberTheme from './throbber.scss';\n\ntype ComponentThemeT = Theme<'container' | 'video'>;\n\ntype PropsT = {\n autoplay?: boolean;\n src: string;\n theme: ComponentThemeT;\n title?: string;\n};\n\n/**\n * A component for embeding a YouTube video.\n * @param [props] Component properties.\n * @param [props.autoplay] If `true` the video will start to play\n * automatically once loaded.\n * @param [props.src] URL of the video to play. Can be in any of\n * the following formats, and keeps any additional query parameters understood\n * by the YouTube IFrame player:\n * - `https://www.youtube.com/watch?v=NdF6Rmt6Ado`\n * - `https://youtu.be/NdF6Rmt6Ado`\n * - `https://www.youtube.com/embed/NdF6Rmt6Ado`\n * @param [props.theme] _Ad hoc_ theme.\n * @param [props.title] The `title` attribute to add to the player\n * IFrame.\n */\nconst YouTubeVideo: React.FunctionComponent<PropsT> = ({\n autoplay,\n src,\n theme,\n title,\n}) => {\n const srcParts = src.split('?');\n let [url] = srcParts;\n const [, queryString] = srcParts;\n const query = queryString ? qs.parse(queryString) : {};\n\n const videoId = query.v ?? url?.match(/\\/([a-zA-Z0-9-_]*)$/)?.[1];\n url = `https://www.youtube.com/embed/${videoId as string}`;\n\n delete query.v;\n query.autoplay = autoplay ? '1' : '0';\n url += `?${qs.stringify(query)}`;\n\n // TODO: https://developers.google.com/youtube/player_parameters\n // More query parameters can be exposed via the component props.\n\n return (\n <div className={theme.container}>\n <Throbber theme={throbberTheme} />\n {/* TODO: I guess, we better add the sanbox, but right now I don't have\n time to carefully explore which restrictions should be lifted to allow\n embed YouTube player to work... sometime later we'll take care of it */\n }\n <iframe // eslint-disable-line react/iframe-missing-sandbox\n allow=\"autoplay\"\n allowFullScreen\n className={theme.video}\n src={url}\n title={title}\n />\n </div>\n );\n};\n\nexport default themed(YouTubeVideo, 'YouTubeVideo', baseTheme);\n"],"mappings":"AAAA,OAAOA,EAAE,MAAM,IAAI;AAEnB,OAAOC,MAAM,MAAsB,0BAA0B;AAE7D,OAAOC,QAAQ;AAEf,OAAOC,SAAS;AAChB,OAAOC,aAAa;AAAwB,SAAAC,GAAA,IAAAC,IAAA,EAAAC,IAAA,IAAAC,KAAA;AAW5C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,YAA6C,GAAGA,CAAC;EACrDC,QAAQ;EACRC,GAAG;EACHC,KAAK;EACLC;AACF,CAAC,KAAK;EACJ,MAAMC,QAAQ,GAAGH,GAAG,CAACI,KAAK,CAAC,GAAG,CAAC;EAC/B,IAAI,CAACC,GAAG,CAAC,GAAGF,QAAQ;EACpB,MAAM,GAAGG,WAAW,CAAC,GAAGH,QAAQ;EAChC,MAAMI,KAAK,GAAGD,WAAW,GAAGjB,EAAE,CAACmB,KAAK,CAACF,WAAW,CAAC,GAAG,CAAC,CAAC;EAEtD,MAAMG,OAAO,GAAGF,KAAK,CAACG,CAAC,IAAIL,GAAG,EAAEM,KAAK,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC;EACjEN,GAAG,GAAG,iCAAiCI,OAAO,EAAY;EAE1D,OAAOF,KAAK,CAACG,CAAC;EACdH,KAAK,CAACR,QAAQ,GAAGA,QAAQ,GAAG,GAAG,GAAG,GAAG;EACrCM,GAAG,IAAI,IAAIhB,EAAE,CAACuB,SAAS,CAACL,KAAK,CAAC,EAAE;;EAEhC;EACA;;EAEA,oBACEV,KAAA;IAAKgB,SAAS,EAAEZ,KAAK,CAACa,SAAU;IAAAC,QAAA,gBAC9BpB,IAAA,CAACJ,QAAQ;MAACU,KAAK,EAAER;IAAc,CAAE,CAAC,eAKlCE,IAAA;MAAQ;MACNqB,KAAK,EAAC,UAAU;MAChBC,eAAe;MACfJ,SAAS,EAAEZ,KAAK,CAACiB,KAAM;MACvBlB,GAAG,EAAEK,GAAI;MACTH,KAAK,EAAEA;IAAM,CACd,CAAC;EAAA,CACC,CAAC;AAEV,CAAC;AAED,eAAeZ,MAAM,CAACQ,YAAY,EAAE,cAAc,EAAEN,SAAS,CAAC","ignoreList":[]}
@@ -0,0 +1,11 @@
1
+ *,
2
+ .context,
3
+ .ad.hoc {
4
+ .container {
5
+ position: absolute;
6
+ text-align: center;
7
+ top: 40%;
8
+ transform: translateY(50%);
9
+ width: 100%;
10
+ }
11
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Just an aggregation of all exported components into a single module.
3
+ */
4
+
5
+ export { MetaTags } from '@dr.pogodin/react-helmet';
6
+ export * from "./selectors";
7
+ export { default as Button, BaseButton } from "./Button";
8
+ export { default as Checkbox } from "./Checkbox";
9
+ export { default as Input } from "./Input";
10
+ export { default as Link } from "./Link";
11
+ export { default as PageLayout } from "./PageLayout";
12
+ export { default as Modal, BaseModal } from "./Modal";
13
+ export { default as NavLink } from "./NavLink";
14
+ export { default as Throbber } from "./Throbber";
15
+ export { default as WithTooltip } from "./WithTooltip";
16
+ export { default as YouTubeVideo } from "./YouTubeVideo";
17
+ export { default as TextArea } from "./TextArea";
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["MetaTags","default","Button","BaseButton","Checkbox","Input","Link","PageLayout","Modal","BaseModal","NavLink","Throbber","WithTooltip","YouTubeVideo","TextArea"],"sources":["../../../../src/shared/components/index.ts"],"sourcesContent":["/**\n * Just an aggregation of all exported components into a single module.\n */\n\nexport { MetaTags } from '@dr.pogodin/react-helmet';\n\nexport * from 'components/selectors';\n\nexport { default as Button, BaseButton } from 'components/Button';\nexport { default as Checkbox } from 'components/Checkbox';\nexport { default as Input } from 'components/Input';\nexport { default as Link } from 'components/Link';\nexport { default as PageLayout } from 'components/PageLayout';\nexport { default as Modal, BaseModal } from 'components/Modal';\nexport { default as NavLink } from 'components/NavLink';\nexport { default as Throbber } from 'components/Throbber';\nexport { default as WithTooltip } from 'components/WithTooltip';\nexport { default as YouTubeVideo } from 'components/YouTubeVideo';\n\nexport { default as TextArea } from './TextArea';\n"],"mappings":"AAAA;AACA;AACA;;AAEA,SAASA,QAAQ,QAAQ,0BAA0B;AAEnD;AAEA,SAASC,OAAO,IAAIC,MAAM,EAAEC,UAAU;AACtC,SAASF,OAAO,IAAIG,QAAQ;AAC5B,SAASH,OAAO,IAAII,KAAK;AACzB,SAASJ,OAAO,IAAIK,IAAI;AACxB,SAASL,OAAO,IAAIM,UAAU;AAC9B,SAASN,OAAO,IAAIO,KAAK,EAAEC,SAAS;AACpC,SAASR,OAAO,IAAIS,OAAO;AAC3B,SAAST,OAAO,IAAIU,QAAQ;AAC5B,SAASV,OAAO,IAAIW,WAAW;AAC/B,SAASX,OAAO,IAAIY,YAAY;AAEhC,SAASZ,OAAO,IAAIa,QAAQ","ignoreList":[]}
@@ -0,0 +1,79 @@
1
+ import { useImperativeHandle, useRef } from 'react';
2
+ import { BaseModal } from "../../../Modal";
3
+ import { optionValueName } from "../../common";
4
+ import S from "./style.scss";
5
+ import { jsx as _jsx } from "react/jsx-runtime";
6
+ export function areEqual(a, b) {
7
+ return a?.left === b?.left && a?.top === b?.top && a?.width === b?.width;
8
+ }
9
+ const Options = ({
10
+ containerClass,
11
+ containerStyle,
12
+ filter,
13
+ onCancel,
14
+ onChange,
15
+ optionClass,
16
+ options,
17
+ ref
18
+ }) => {
19
+ const opsRef = useRef(null);
20
+ useImperativeHandle(ref, () => ({
21
+ measure: () => {
22
+ const e = opsRef.current?.parentElement;
23
+ if (!e) return undefined;
24
+ const rect = opsRef.current.getBoundingClientRect();
25
+ const style = window.getComputedStyle(e);
26
+ const mBottom = parseFloat(style.marginBottom);
27
+ const mTop = parseFloat(style.marginTop);
28
+ rect.height += mBottom + mTop;
29
+ return rect;
30
+ }
31
+ }), []);
32
+ const optionNodes = [];
33
+ for (const option of options) {
34
+ if (!filter || filter(option)) {
35
+ const [iValue, iName] = optionValueName(option);
36
+ optionNodes.push(/*#__PURE__*/_jsx("div", {
37
+ className: optionClass,
38
+ onClick: e => {
39
+ onChange(iValue);
40
+ e.stopPropagation();
41
+ },
42
+ onKeyDown: e => {
43
+ if (e.key === 'Enter') {
44
+ onChange(iValue);
45
+ e.stopPropagation();
46
+ }
47
+ },
48
+ role: "button",
49
+ tabIndex: 0,
50
+ children: iName
51
+ }, iValue));
52
+ }
53
+ }
54
+ return /*#__PURE__*/_jsx(BaseModal
55
+ // Closes the dropdown (cancels the selection) on any page scrolling attempt.
56
+ // This is the same native <select> elements do on scrolling, and at least for
57
+ // now we have no reason to deal with complications needed to support open
58
+ // dropdowns during the scrolling (that would need to re-position it in
59
+ // response to the position changes of the root dropdown element).
60
+ , {
61
+ cancelOnScrolling: true,
62
+ dontDisableScrolling: true,
63
+ onCancel: onCancel,
64
+ style: containerStyle,
65
+ theme: {
66
+ ad: '',
67
+ container: containerClass,
68
+ context: '',
69
+ hoc: '',
70
+ overlay: S.overlay
71
+ },
72
+ children: /*#__PURE__*/_jsx("div", {
73
+ ref: opsRef,
74
+ children: optionNodes
75
+ })
76
+ });
77
+ };
78
+ export default Options;
79
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["useImperativeHandle","useRef","BaseModal","optionValueName","S","jsx","_jsx","areEqual","a","b","left","top","width","Options","containerClass","containerStyle","filter","onCancel","onChange","optionClass","options","ref","opsRef","measure","e","current","parentElement","undefined","rect","getBoundingClientRect","style","window","getComputedStyle","mBottom","parseFloat","marginBottom","mTop","marginTop","height","optionNodes","option","iValue","iName","push","className","onClick","stopPropagation","onKeyDown","key","role","tabIndex","children","cancelOnScrolling","dontDisableScrolling","theme","ad","container","context","hoc","overlay"],"sources":["../../../../../../../src/shared/components/selectors/CustomDropdown/Options/index.tsx"],"sourcesContent":["import {\n type FunctionComponent,\n type ReactNode,\n type RefObject,\n useImperativeHandle,\n useRef,\n} from 'react';\n\nimport { BaseModal } from 'components/Modal';\n\nimport {\n type OptionT,\n type OptionsT,\n type ValueT,\n optionValueName,\n} from '../../common';\n\nimport S from './style.scss';\n\nexport type ContainerPosT = {\n left: number;\n top: number;\n width: number;\n};\n\nexport function areEqual(a?: ContainerPosT, b?: ContainerPosT): boolean {\n return a?.left === b?.left && a?.top === b?.top && a?.width === b?.width;\n}\n\nexport type RefT = {\n measure: () => DOMRect | undefined;\n};\n\ntype PropsT = {\n containerClass: string;\n containerStyle?: ContainerPosT;\n filter?: (item: OptionT<ReactNode> | ValueT) => boolean;\n optionClass: string;\n options: Readonly<OptionsT<ReactNode>>;\n onCancel: () => void;\n onChange: (value: ValueT) => void;\n ref?: RefObject<RefT | null>;\n};\n\nconst Options: FunctionComponent<PropsT> = ({\n containerClass,\n containerStyle,\n filter,\n onCancel,\n onChange,\n optionClass,\n options,\n ref,\n}) => {\n const opsRef = useRef<HTMLDivElement>(null);\n\n useImperativeHandle(ref, () => ({\n measure: () => {\n const e = opsRef.current?.parentElement;\n if (!e) return undefined;\n\n const rect = opsRef.current!.getBoundingClientRect();\n const style = window.getComputedStyle(e);\n const mBottom = parseFloat(style.marginBottom);\n const mTop = parseFloat(style.marginTop);\n\n rect.height += mBottom + mTop;\n\n return rect;\n },\n }), []);\n\n const optionNodes: ReactNode[] = [];\n for (const option of options) {\n if (!filter || filter(option)) {\n const [iValue, iName] = optionValueName(option);\n optionNodes.push(\n <div\n className={optionClass}\n key={iValue}\n onClick={(e) => {\n onChange(iValue);\n e.stopPropagation();\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n onChange(iValue);\n e.stopPropagation();\n }\n }}\n role=\"button\"\n tabIndex={0}\n >\n {iName}\n </div>,\n );\n }\n }\n\n return (\n <BaseModal\n // Closes the dropdown (cancels the selection) on any page scrolling attempt.\n // This is the same native <select> elements do on scrolling, and at least for\n // now we have no reason to deal with complications needed to support open\n // dropdowns during the scrolling (that would need to re-position it in\n // response to the position changes of the root dropdown element).\n cancelOnScrolling\n dontDisableScrolling\n onCancel={onCancel}\n style={containerStyle}\n theme={{\n ad: '',\n container: containerClass,\n context: '',\n hoc: '',\n overlay: S.overlay,\n }}\n >\n <div ref={opsRef}>{optionNodes}</div>\n </BaseModal>\n );\n};\n\nexport default Options;\n"],"mappings":"AAAA,SAIEA,mBAAmB,EACnBC,MAAM,QACD,OAAO;AAEd,SAASC,SAAS;AAElB,SAIEC,eAAe;AAGjB,OAAOC,CAAC;AAAqB,SAAAC,GAAA,IAAAC,IAAA;AAQ7B,OAAO,SAASC,QAAQA,CAACC,CAAiB,EAAEC,CAAiB,EAAW;EACtE,OAAOD,CAAC,EAAEE,IAAI,KAAKD,CAAC,EAAEC,IAAI,IAAIF,CAAC,EAAEG,GAAG,KAAKF,CAAC,EAAEE,GAAG,IAAIH,CAAC,EAAEI,KAAK,KAAKH,CAAC,EAAEG,KAAK;AAC1E;AAiBA,MAAMC,OAAkC,GAAGA,CAAC;EAC1CC,cAAc;EACdC,cAAc;EACdC,MAAM;EACNC,QAAQ;EACRC,QAAQ;EACRC,WAAW;EACXC,OAAO;EACPC;AACF,CAAC,KAAK;EACJ,MAAMC,MAAM,GAAGrB,MAAM,CAAiB,IAAI,CAAC;EAE3CD,mBAAmB,CAACqB,GAAG,EAAE,OAAO;IAC9BE,OAAO,EAAEA,CAAA,KAAM;MACb,MAAMC,CAAC,GAAGF,MAAM,CAACG,OAAO,EAAEC,aAAa;MACvC,IAAI,CAACF,CAAC,EAAE,OAAOG,SAAS;MAExB,MAAMC,IAAI,GAAGN,MAAM,CAACG,OAAO,CAAEI,qBAAqB,CAAC,CAAC;MACpD,MAAMC,KAAK,GAAGC,MAAM,CAACC,gBAAgB,CAACR,CAAC,CAAC;MACxC,MAAMS,OAAO,GAAGC,UAAU,CAACJ,KAAK,CAACK,YAAY,CAAC;MAC9C,MAAMC,IAAI,GAAGF,UAAU,CAACJ,KAAK,CAACO,SAAS,CAAC;MAExCT,IAAI,CAACU,MAAM,IAAIL,OAAO,GAAGG,IAAI;MAE7B,OAAOR,IAAI;IACb;EACF,CAAC,CAAC,EAAE,EAAE,CAAC;EAEP,MAAMW,WAAwB,GAAG,EAAE;EACnC,KAAK,MAAMC,MAAM,IAAIpB,OAAO,EAAE;IAC5B,IAAI,CAACJ,MAAM,IAAIA,MAAM,CAACwB,MAAM,CAAC,EAAE;MAC7B,MAAM,CAACC,MAAM,EAAEC,KAAK,CAAC,GAAGvC,eAAe,CAACqC,MAAM,CAAC;MAC/CD,WAAW,CAACI,IAAI,cACdrC,IAAA;QACEsC,SAAS,EAAEzB,WAAY;QAEvB0B,OAAO,EAAGrB,CAAC,IAAK;UACdN,QAAQ,CAACuB,MAAM,CAAC;UAChBjB,CAAC,CAACsB,eAAe,CAAC,CAAC;QACrB,CAAE;QACFC,SAAS,EAAGvB,CAAC,IAAK;UAChB,IAAIA,CAAC,CAACwB,GAAG,KAAK,OAAO,EAAE;YACrB9B,QAAQ,CAACuB,MAAM,CAAC;YAChBjB,CAAC,CAACsB,eAAe,CAAC,CAAC;UACrB;QACF,CAAE;QACFG,IAAI,EAAC,QAAQ;QACbC,QAAQ,EAAE,CAAE;QAAAC,QAAA,EAEXT;MAAK,GAdDD,MAeF,CACP,CAAC;IACH;EACF;EAEA,oBACEnC,IAAA,CAACJ;EACC;EACA;EACA;EACA;EACA;EAAA;IACAkD,iBAAiB;IACjBC,oBAAoB;IACpBpC,QAAQ,EAAEA,QAAS;IACnBa,KAAK,EAAEf,cAAe;IACtBuC,KAAK,EAAE;MACLC,EAAE,EAAE,EAAE;MACNC,SAAS,EAAE1C,cAAc;MACzB2C,OAAO,EAAE,EAAE;MACXC,GAAG,EAAE,EAAE;MACPC,OAAO,EAAEvD,CAAC,CAACuD;IACb,CAAE;IAAAR,QAAA,eAEF7C,IAAA;MAAKe,GAAG,EAAEC,MAAO;MAAA6B,QAAA,EAAEZ;IAAW,CAAM;EAAC,CAC5B,CAAC;AAEhB,CAAC;AAED,eAAe1B,OAAO","ignoreList":[]}
@@ -0,0 +1,6 @@
1
+ .overlay {
2
+ inset: 0;
3
+ opacity: 0.2;
4
+ position: fixed;
5
+ z-index: 1000;
6
+ }
@@ -0,0 +1,120 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+ import themed from '@dr.pogodin/react-themes';
3
+ import { optionValueName } from "../common";
4
+ import Options, { areEqual } from "./Options";
5
+ import defaultTheme from "./theme.scss";
6
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
+ const BaseCustomDropdown = ({
8
+ filter,
9
+ label,
10
+ onChange,
11
+ options,
12
+ theme,
13
+ value
14
+ }) => {
15
+ const [active, setActive] = useState(false);
16
+ const dropdownRef = useRef(null);
17
+ const opsRef = useRef(null);
18
+ const [opsPos, setOpsPos] = useState();
19
+ const [upward, setUpward] = useState(false);
20
+ useEffect(() => {
21
+ if (!active) return undefined;
22
+ let id;
23
+ const cb = () => {
24
+ const anchor = dropdownRef.current?.getBoundingClientRect();
25
+ const opsRect = opsRef.current?.measure();
26
+ if (anchor && opsRect) {
27
+ const fitsDown = anchor.bottom + opsRect.height < (window.visualViewport?.height ?? 0);
28
+ const fitsUp = anchor.top - opsRect.height > 0;
29
+ const up = !fitsDown && fitsUp;
30
+ setUpward(up);
31
+ const pos = up ? {
32
+ left: anchor.left,
33
+ top: anchor.top - opsRect.height - 1,
34
+ width: anchor.width
35
+ } : {
36
+ left: anchor.left,
37
+ top: anchor.bottom,
38
+ width: anchor.width
39
+ };
40
+ setOpsPos(now => areEqual(now, pos) ? now : pos);
41
+ }
42
+ id = requestAnimationFrame(cb);
43
+ };
44
+ requestAnimationFrame(cb);
45
+ return () => {
46
+ cancelAnimationFrame(id);
47
+ };
48
+ }, [active]);
49
+ const openList = e => {
50
+ const view = window.visualViewport;
51
+ const rect = dropdownRef.current.getBoundingClientRect();
52
+ setActive(true);
53
+
54
+ // NOTE: This first opens the dropdown off-screen, where it is measured
55
+ // by an effect declared above, and then positioned below, or above
56
+ // the original dropdown element, depending where it fits best
57
+ // (if we first open it downward, it would flick if we immediately
58
+ // move it above, at least with the current position update via local
59
+ // react state, and not imperatively).
60
+ setOpsPos({
61
+ left: view?.width ?? 0,
62
+ top: view?.height ?? 0,
63
+ width: rect.width
64
+ });
65
+ e.stopPropagation();
66
+ };
67
+ let selected = /*#__PURE__*/_jsx(_Fragment, {
68
+ children: "\u200C"
69
+ });
70
+ for (const option of options) {
71
+ if (!filter || filter(option)) {
72
+ const [iValue, iName] = optionValueName(option);
73
+ if (iValue === value) {
74
+ selected = iName;
75
+ break;
76
+ }
77
+ }
78
+ }
79
+ let containerClassName = theme.container;
80
+ if (active) containerClassName += ` ${theme.active}`;
81
+ let opsContainerClass = theme.select ?? '';
82
+ if (upward) {
83
+ containerClassName += ` ${theme.upward}`;
84
+ opsContainerClass += ` ${theme.upward}`;
85
+ }
86
+ return /*#__PURE__*/_jsxs("div", {
87
+ className: containerClassName,
88
+ children: [label === undefined ? null : /*#__PURE__*/_jsx("div", {
89
+ className: theme.label,
90
+ children: label
91
+ }), /*#__PURE__*/_jsxs("div", {
92
+ className: theme.dropdown,
93
+ onClick: openList,
94
+ onKeyDown: e => {
95
+ if (e.key === 'Enter') openList(e);
96
+ },
97
+ ref: dropdownRef,
98
+ role: "listbox",
99
+ tabIndex: 0,
100
+ children: [selected, /*#__PURE__*/_jsx("div", {
101
+ className: theme.arrow
102
+ })]
103
+ }), active ? /*#__PURE__*/_jsx(Options, {
104
+ containerClass: opsContainerClass,
105
+ containerStyle: opsPos,
106
+ onCancel: () => {
107
+ setActive(false);
108
+ },
109
+ onChange: newValue => {
110
+ setActive(false);
111
+ if (onChange) onChange(newValue);
112
+ },
113
+ optionClass: theme.option ?? '',
114
+ options: options,
115
+ ref: opsRef
116
+ }) : null]
117
+ });
118
+ };
119
+ export default themed(BaseCustomDropdown, 'CustomDropdown', defaultTheme);
120
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["useEffect","useRef","useState","themed","optionValueName","Options","areEqual","defaultTheme","Fragment","_Fragment","jsx","_jsx","jsxs","_jsxs","BaseCustomDropdown","filter","label","onChange","options","theme","value","active","setActive","dropdownRef","opsRef","opsPos","setOpsPos","upward","setUpward","undefined","id","cb","anchor","current","getBoundingClientRect","opsRect","measure","fitsDown","bottom","height","window","visualViewport","fitsUp","top","up","pos","left","width","now","requestAnimationFrame","cancelAnimationFrame","openList","e","view","rect","stopPropagation","selected","children","option","iValue","iName","containerClassName","container","opsContainerClass","select","className","dropdown","onClick","onKeyDown","key","ref","role","tabIndex","arrow","containerClass","containerStyle","onCancel","newValue","optionClass"],"sources":["../../../../../../src/shared/components/selectors/CustomDropdown/index.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\n\nimport themed from '@dr.pogodin/react-themes';\n\nimport { type PropsT, type ValueT, optionValueName } from '../common';\n\nimport Options, { type ContainerPosT, type RefT, areEqual } from './Options';\n\nimport defaultTheme from './theme.scss';\n\nconst BaseCustomDropdown: React.FunctionComponent<\n PropsT<React.ReactNode, (value: ValueT) => void>\n> = ({\n filter,\n label,\n onChange,\n options,\n theme,\n value,\n}) => {\n const [active, setActive] = useState(false);\n\n const dropdownRef = useRef<HTMLDivElement>(null);\n const opsRef = useRef<RefT>(null);\n\n const [opsPos, setOpsPos] = useState<ContainerPosT>();\n const [upward, setUpward] = useState(false);\n\n useEffect(() => {\n if (!active) return undefined;\n\n let id: number;\n const cb = () => {\n const anchor = dropdownRef.current?.getBoundingClientRect();\n const opsRect = opsRef.current?.measure();\n if (anchor && opsRect) {\n const fitsDown = anchor.bottom + opsRect.height\n < (window.visualViewport?.height ?? 0);\n const fitsUp = anchor.top - opsRect.height > 0;\n\n const up = !fitsDown && fitsUp;\n setUpward(up);\n\n const pos = up ? {\n left: anchor.left,\n top: anchor.top - opsRect.height - 1,\n width: anchor.width,\n } : {\n left: anchor.left,\n top: anchor.bottom,\n width: anchor.width,\n };\n\n setOpsPos((now) => (areEqual(now, pos) ? now : pos));\n }\n id = requestAnimationFrame(cb);\n };\n requestAnimationFrame(cb);\n\n return () => {\n cancelAnimationFrame(id);\n };\n }, [active]);\n\n const openList = (\n e: React.KeyboardEvent<HTMLDivElement> | React.MouseEvent<HTMLDivElement>,\n ) => {\n const view = window.visualViewport;\n const rect = dropdownRef.current!.getBoundingClientRect();\n setActive(true);\n\n // NOTE: This first opens the dropdown off-screen, where it is measured\n // by an effect declared above, and then positioned below, or above\n // the original dropdown element, depending where it fits best\n // (if we first open it downward, it would flick if we immediately\n // move it above, at least with the current position update via local\n // react state, and not imperatively).\n setOpsPos({\n left: view?.width ?? 0,\n top: view?.height ?? 0,\n width: rect.width,\n });\n\n e.stopPropagation();\n };\n\n let selected: React.ReactNode = <>&zwnj;</>;\n for (const option of options) {\n if (!filter || filter(option)) {\n const [iValue, iName] = optionValueName(option);\n if (iValue === value) {\n selected = iName;\n break;\n }\n }\n }\n\n let containerClassName = theme.container;\n if (active) containerClassName += ` ${theme.active}`;\n\n let opsContainerClass = theme.select ?? '';\n if (upward) {\n containerClassName += ` ${theme.upward}`;\n opsContainerClass += ` ${theme.upward}`;\n }\n\n return (\n <div className={containerClassName}>\n {label === undefined ? null\n : <div className={theme.label}>{label}</div>}\n <div\n className={theme.dropdown}\n onClick={openList}\n onKeyDown={(e) => {\n if (e.key === 'Enter') openList(e);\n }}\n ref={dropdownRef}\n role=\"listbox\"\n tabIndex={0}\n >\n {selected}\n <div className={theme.arrow} />\n </div>\n {\n active ? (\n <Options\n containerClass={opsContainerClass}\n containerStyle={opsPos}\n onCancel={() => {\n setActive(false);\n }}\n onChange={(newValue) => {\n setActive(false);\n if (onChange) onChange(newValue);\n }}\n optionClass={theme.option ?? ''}\n options={options}\n ref={opsRef}\n />\n ) : null\n }\n </div>\n );\n};\n\nexport default themed(BaseCustomDropdown, 'CustomDropdown', defaultTheme);\n"],"mappings":"AAAA,SAASA,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,OAAO;AAEnD,OAAOC,MAAM,MAAM,0BAA0B;AAE7C,SAAmCC,eAAe;AAElD,OAAOC,OAAO,IAAmCC,QAAQ;AAEzD,OAAOC,YAAY;AAAqB,SAAAC,QAAA,IAAAC,SAAA,EAAAC,GAAA,IAAAC,IAAA,EAAAC,IAAA,IAAAC,KAAA;AAExC,MAAMC,kBAEL,GAAGA,CAAC;EACHC,MAAM;EACNC,KAAK;EACLC,QAAQ;EACRC,OAAO;EACPC,KAAK;EACLC;AACF,CAAC,KAAK;EACJ,MAAM,CAACC,MAAM,EAAEC,SAAS,CAAC,GAAGpB,QAAQ,CAAC,KAAK,CAAC;EAE3C,MAAMqB,WAAW,GAAGtB,MAAM,CAAiB,IAAI,CAAC;EAChD,MAAMuB,MAAM,GAAGvB,MAAM,CAAO,IAAI,CAAC;EAEjC,MAAM,CAACwB,MAAM,EAAEC,SAAS,CAAC,GAAGxB,QAAQ,CAAgB,CAAC;EACrD,MAAM,CAACyB,MAAM,EAAEC,SAAS,CAAC,GAAG1B,QAAQ,CAAC,KAAK,CAAC;EAE3CF,SAAS,CAAC,MAAM;IACd,IAAI,CAACqB,MAAM,EAAE,OAAOQ,SAAS;IAE7B,IAAIC,EAAU;IACd,MAAMC,EAAE,GAAGA,CAAA,KAAM;MACf,MAAMC,MAAM,GAAGT,WAAW,CAACU,OAAO,EAAEC,qBAAqB,CAAC,CAAC;MAC3D,MAAMC,OAAO,GAAGX,MAAM,CAACS,OAAO,EAAEG,OAAO,CAAC,CAAC;MACzC,IAAIJ,MAAM,IAAIG,OAAO,EAAE;QACrB,MAAME,QAAQ,GAAGL,MAAM,CAACM,MAAM,GAAGH,OAAO,CAACI,MAAM,IAC1CC,MAAM,CAACC,cAAc,EAAEF,MAAM,IAAI,CAAC,CAAC;QACxC,MAAMG,MAAM,GAAGV,MAAM,CAACW,GAAG,GAAGR,OAAO,CAACI,MAAM,GAAG,CAAC;QAE9C,MAAMK,EAAE,GAAG,CAACP,QAAQ,IAAIK,MAAM;QAC9Bd,SAAS,CAACgB,EAAE,CAAC;QAEb,MAAMC,GAAG,GAAGD,EAAE,GAAG;UACfE,IAAI,EAAEd,MAAM,CAACc,IAAI;UACjBH,GAAG,EAAEX,MAAM,CAACW,GAAG,GAAGR,OAAO,CAACI,MAAM,GAAG,CAAC;UACpCQ,KAAK,EAAEf,MAAM,CAACe;QAChB,CAAC,GAAG;UACFD,IAAI,EAAEd,MAAM,CAACc,IAAI;UACjBH,GAAG,EAAEX,MAAM,CAACM,MAAM;UAClBS,KAAK,EAAEf,MAAM,CAACe;QAChB,CAAC;QAEDrB,SAAS,CAAEsB,GAAG,IAAM1C,QAAQ,CAAC0C,GAAG,EAAEH,GAAG,CAAC,GAAGG,GAAG,GAAGH,GAAI,CAAC;MACtD;MACAf,EAAE,GAAGmB,qBAAqB,CAAClB,EAAE,CAAC;IAChC,CAAC;IACDkB,qBAAqB,CAAClB,EAAE,CAAC;IAEzB,OAAO,MAAM;MACXmB,oBAAoB,CAACpB,EAAE,CAAC;IAC1B,CAAC;EACH,CAAC,EAAE,CAACT,MAAM,CAAC,CAAC;EAEZ,MAAM8B,QAAQ,GACZC,CAAyE,IACtE;IACH,MAAMC,IAAI,GAAGb,MAAM,CAACC,cAAc;IAClC,MAAMa,IAAI,GAAG/B,WAAW,CAACU,OAAO,CAAEC,qBAAqB,CAAC,CAAC;IACzDZ,SAAS,CAAC,IAAI,CAAC;;IAEf;IACA;IACA;IACA;IACA;IACA;IACAI,SAAS,CAAC;MACRoB,IAAI,EAAEO,IAAI,EAAEN,KAAK,IAAI,CAAC;MACtBJ,GAAG,EAAEU,IAAI,EAAEd,MAAM,IAAI,CAAC;MACtBQ,KAAK,EAAEO,IAAI,CAACP;IACd,CAAC,CAAC;IAEFK,CAAC,CAACG,eAAe,CAAC,CAAC;EACrB,CAAC;EAED,IAAIC,QAAyB,gBAAG7C,IAAA,CAAAF,SAAA;IAAAgD,QAAA,EAAE;EAAM,CAAE,CAAC;EAC3C,KAAK,MAAMC,MAAM,IAAIxC,OAAO,EAAE;IAC5B,IAAI,CAACH,MAAM,IAAIA,MAAM,CAAC2C,MAAM,CAAC,EAAE;MAC7B,MAAM,CAACC,MAAM,EAAEC,KAAK,CAAC,GAAGxD,eAAe,CAACsD,MAAM,CAAC;MAC/C,IAAIC,MAAM,KAAKvC,KAAK,EAAE;QACpBoC,QAAQ,GAAGI,KAAK;QAChB;MACF;IACF;EACF;EAEA,IAAIC,kBAAkB,GAAG1C,KAAK,CAAC2C,SAAS;EACxC,IAAIzC,MAAM,EAAEwC,kBAAkB,IAAI,IAAI1C,KAAK,CAACE,MAAM,EAAE;EAEpD,IAAI0C,iBAAiB,GAAG5C,KAAK,CAAC6C,MAAM,IAAI,EAAE;EAC1C,IAAIrC,MAAM,EAAE;IACVkC,kBAAkB,IAAI,IAAI1C,KAAK,CAACQ,MAAM,EAAE;IACxCoC,iBAAiB,IAAI,IAAI5C,KAAK,CAACQ,MAAM,EAAE;EACzC;EAEA,oBACEd,KAAA;IAAKoD,SAAS,EAAEJ,kBAAmB;IAAAJ,QAAA,GAChCzC,KAAK,KAAKa,SAAS,GAAG,IAAI,gBACvBlB,IAAA;MAAKsD,SAAS,EAAE9C,KAAK,CAACH,KAAM;MAAAyC,QAAA,EAAEzC;IAAK,CAAM,CAAC,eAC9CH,KAAA;MACEoD,SAAS,EAAE9C,KAAK,CAAC+C,QAAS;MAC1BC,OAAO,EAAEhB,QAAS;MAClBiB,SAAS,EAAGhB,CAAC,IAAK;QAChB,IAAIA,CAAC,CAACiB,GAAG,KAAK,OAAO,EAAElB,QAAQ,CAACC,CAAC,CAAC;MACpC,CAAE;MACFkB,GAAG,EAAE/C,WAAY;MACjBgD,IAAI,EAAC,SAAS;MACdC,QAAQ,EAAE,CAAE;MAAAf,QAAA,GAEXD,QAAQ,eACT7C,IAAA;QAAKsD,SAAS,EAAE9C,KAAK,CAACsD;MAAM,CAAE,CAAC;IAAA,CAC5B,CAAC,EAEJpD,MAAM,gBACJV,IAAA,CAACN,OAAO;MACNqE,cAAc,EAAEX,iBAAkB;MAClCY,cAAc,EAAElD,MAAO;MACvBmD,QAAQ,EAAEA,CAAA,KAAM;QACdtD,SAAS,CAAC,KAAK,CAAC;MAClB,CAAE;MACFL,QAAQ,EAAG4D,QAAQ,IAAK;QACtBvD,SAAS,CAAC,KAAK,CAAC;QAChB,IAAIL,QAAQ,EAAEA,QAAQ,CAAC4D,QAAQ,CAAC;MAClC,CAAE;MACFC,WAAW,EAAE3D,KAAK,CAACuC,MAAM,IAAI,EAAG;MAChCxC,OAAO,EAAEA,OAAQ;MACjBoD,GAAG,EAAE9C;IAAO,CACb,CAAC,GACA,IAAI;EAAA,CAEP,CAAC;AAEV,CAAC;AAED,eAAerB,MAAM,CAACW,kBAAkB,EAAE,gBAAgB,EAAEP,YAAY,CAAC","ignoreList":[]}
@@ -0,0 +1,118 @@
1
+ $border: 1px solid gray;
2
+
3
+ *,
4
+ .context,
5
+ .ad.hoc {
6
+ // The outermost dropdown container, holding together the label (if any),
7
+ // and the select element with arrow. Note, that the dropdown option list,
8
+ // when opened, exists completely outside the dropdown DOM hierarchy, and
9
+ // is aligned into the correct position by JS.
10
+ &.container {
11
+ align-items: center;
12
+ display: inline-flex;
13
+ margin: 0.1em;
14
+ position: relative;
15
+ }
16
+
17
+ // Styling of default label next to the dropdown (has no effect on custom
18
+ // non-string label node, if provided).
19
+ &.label {
20
+ margin: 0 0.6em 0 1.2em;
21
+ }
22
+
23
+ &.dropdown {
24
+ border: $border;
25
+ border-radius: 0.3em;
26
+ cursor: pointer;
27
+ min-width: 200px;
28
+ outline: none;
29
+ padding: 0.3em 3.0em 0.3em 0.6em;
30
+ position: relative;
31
+ user-select: none;
32
+
33
+ &:focus {
34
+ border-color: blue;
35
+ box-shadow: 0 0 3px 1px lightblue;
36
+ }
37
+ }
38
+
39
+ &.option {
40
+ cursor: pointer;
41
+ outline: none ;
42
+ padding: 0 0.6em;
43
+
44
+ &:focus {
45
+ background: royalblue;
46
+ color: white;
47
+ }
48
+
49
+ &:hover {
50
+ background: royalblue;
51
+ color: white;
52
+ }
53
+ }
54
+
55
+ &.select {
56
+ background: white;
57
+ border: $border;
58
+ border-radius: 0 0 0.3em 0.3em;
59
+ border-top: none;
60
+ box-shadow: 0 6px 12px 3px lightgray;
61
+ position: fixed;
62
+ z-index: 1001;
63
+ }
64
+
65
+ &.arrow {
66
+ background-image: linear-gradient(to top, lightgray, white 50%, white);
67
+ border-left: $border;
68
+ border-radius: 0 0.3em 0.3em 0;
69
+ bottom: 0;
70
+ padding: 0.3em 0.6em;
71
+ position: absolute;
72
+ right: 0;
73
+ top: 0;
74
+
75
+ &::after {
76
+ content: '▼';
77
+ }
78
+ }
79
+
80
+ &.active {
81
+ .arrow {
82
+ border-radius: 0 0.3em 0 0;
83
+ }
84
+
85
+ .dropdown {
86
+ border-color: blue;
87
+ border-radius: 0.3em 0.3em 0 0;
88
+ }
89
+ }
90
+
91
+ &.upward {
92
+ &.active {
93
+ // NOTE: Here StyleLint complains about order & specifity of selectors in
94
+ // the compiled CSS, but it should have no effect on the actual styling.
95
+ // stylelint-disable no-descending-specificity
96
+ .arrow {
97
+ border-radius: 0 0 0.3em;
98
+ }
99
+
100
+ .dropdown {
101
+ border-radius: 0 0 0.3em 0.3em;
102
+ }
103
+ // stylelint-enable no-descending-specificity
104
+ }
105
+
106
+ &.select {
107
+ border-bottom: none;
108
+ border-top: $border;
109
+ border-radius: 0.3em 0.3em 0 0;
110
+
111
+ // NOTE: Here a normal (downward) shadow would weirdly cast over
112
+ // the dropdown element, and other ways to cast the shadow result in
113
+ // "upward" shadow, which is also weird. Thus, better no shadow at all
114
+ // for the upward-opened dropdown.
115
+ box-shadow: none;
116
+ }
117
+ }
118
+ }
@@ -0,0 +1,82 @@
1
+ // Implements dropdown based on the native HTML <select> element.
2
+
3
+ import themed from '@dr.pogodin/react-themes';
4
+ import { optionValueName } from "../common";
5
+ import defaultTheme from "./theme.scss";
6
+
7
+ /**
8
+ * Implements a themeable dropdown list. Internally it is rendered with help of
9
+ * the standard HTML `<select>` element, thus the styling support is somewhat
10
+ * limited.
11
+ * @param [props] Component properties.
12
+ * @param [props.filter] Options filter function. If provided, only
13
+ * those elements of `options` list will be used by the dropdown, for which this
14
+ * filter returns `true`.
15
+ * @param [props.label] Dropdown label.
16
+ * @param [props.onChange] Selection event handler.
17
+ * @param [props.options=[]] Array of dropdown
18
+ * options. For string elements the option value and name will be the same.
19
+ * It is allowed to mix DropdownOption and string elements in the same option
20
+ * list.
21
+ * @param [props.theme] _Ad hoc_ theme.
22
+ * @param [props.value] Currently selected value.
23
+ * @param [props....]
24
+ * [Other theming properties](https://www.npmjs.com/package/@dr.pogodin/react-themes#themed-component-properties)
25
+ */
26
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
27
+ const Dropdown = ({
28
+ filter,
29
+ label,
30
+ onChange,
31
+ options,
32
+ testId,
33
+ theme,
34
+ value
35
+ }) => {
36
+ let isValidValue = false;
37
+ const optionElements = [];
38
+ for (const option of options) {
39
+ if (!filter || filter(option)) {
40
+ const [iValue, iName] = optionValueName(option);
41
+ isValidValue ||= iValue === value;
42
+ optionElements.push(/*#__PURE__*/_jsx("option", {
43
+ className: theme.option,
44
+ value: iValue,
45
+ children: iName
46
+ }, iValue));
47
+ }
48
+ }
49
+
50
+ // NOTE: This element represents the current `value` when it does not match
51
+ // any valid option. In Chrome, and some other browsers, we are able to hide
52
+ // it from the opened dropdown; in others, e.g. Safari, the best we can do is
53
+ // to show it as disabled.
54
+ const hiddenOption = isValidValue ? null : /*#__PURE__*/_jsx("option", {
55
+ className: theme.hiddenOption,
56
+ disabled: true,
57
+ value: value,
58
+ children: value
59
+ }, "__reactUtilsHiddenOption");
60
+ let selectClassName = theme.select;
61
+ if (!isValidValue) selectClassName += ` ${theme.invalid}`;
62
+ return /*#__PURE__*/_jsxs("div", {
63
+ className: theme.container,
64
+ children: [label === undefined ? null : /*#__PURE__*/_jsx("div", {
65
+ className: theme.label,
66
+ children: label
67
+ }), /*#__PURE__*/_jsxs("div", {
68
+ className: theme.dropdown,
69
+ children: [/*#__PURE__*/_jsxs("select", {
70
+ className: selectClassName,
71
+ "data-testid": process.env.NODE_ENV === 'production' ? undefined : testId,
72
+ onChange: onChange,
73
+ value: value,
74
+ children: [hiddenOption, optionElements]
75
+ }), /*#__PURE__*/_jsx("div", {
76
+ className: theme.arrow
77
+ })]
78
+ })]
79
+ });
80
+ };
81
+ export default themed(Dropdown, 'Dropdown', defaultTheme);
82
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["themed","optionValueName","defaultTheme","jsx","_jsx","jsxs","_jsxs","Dropdown","filter","label","onChange","options","testId","theme","value","isValidValue","optionElements","option","iValue","iName","push","className","children","hiddenOption","disabled","selectClassName","select","invalid","container","undefined","dropdown","process","env","NODE_ENV","arrow"],"sources":["../../../../../../src/shared/components/selectors/NativeDropdown/index.tsx"],"sourcesContent":["// Implements dropdown based on the native HTML <select> element.\n\nimport themed from '@dr.pogodin/react-themes';\n\nimport { type PropsT, optionValueName } from '../common';\n\nimport defaultTheme from './theme.scss';\n\n/**\n * Implements a themeable dropdown list. Internally it is rendered with help of\n * the standard HTML `<select>` element, thus the styling support is somewhat\n * limited.\n * @param [props] Component properties.\n * @param [props.filter] Options filter function. If provided, only\n * those elements of `options` list will be used by the dropdown, for which this\n * filter returns `true`.\n * @param [props.label] Dropdown label.\n * @param [props.onChange] Selection event handler.\n * @param [props.options=[]] Array of dropdown\n * options. For string elements the option value and name will be the same.\n * It is allowed to mix DropdownOption and string elements in the same option\n * list.\n * @param [props.theme] _Ad hoc_ theme.\n * @param [props.value] Currently selected value.\n * @param [props....]\n * [Other theming properties](https://www.npmjs.com/package/@dr.pogodin/react-themes#themed-component-properties)\n */\nconst Dropdown: React.FunctionComponent<PropsT<string>> = ({\n filter,\n label,\n onChange,\n options,\n testId,\n theme,\n value,\n}) => {\n let isValidValue = false;\n const optionElements = [];\n\n for (const option of options) {\n if (!filter || filter(option)) {\n const [iValue, iName] = optionValueName(option);\n isValidValue ||= iValue === value;\n optionElements.push(\n <option className={theme.option} key={iValue} value={iValue}>\n {iName}\n </option>,\n );\n }\n }\n\n // NOTE: This element represents the current `value` when it does not match\n // any valid option. In Chrome, and some other browsers, we are able to hide\n // it from the opened dropdown; in others, e.g. Safari, the best we can do is\n // to show it as disabled.\n const hiddenOption = isValidValue ? null : (\n <option\n className={theme.hiddenOption}\n disabled\n key=\"__reactUtilsHiddenOption\"\n value={value}\n >\n {value}\n </option>\n );\n\n let selectClassName = theme.select;\n if (!isValidValue) selectClassName += ` ${theme.invalid}`;\n\n return (\n <div className={theme.container}>\n { label === undefined\n ? null : <div className={theme.label}>{label}</div> }\n <div className={theme.dropdown}>\n <select\n className={selectClassName}\n data-testid={process.env.NODE_ENV === 'production' ? undefined : testId}\n onChange={onChange}\n value={value}\n >\n {hiddenOption}\n {optionElements}\n </select>\n <div className={theme.arrow} />\n </div>\n </div>\n );\n};\n\nexport default themed(Dropdown, 'Dropdown', defaultTheme);\n"],"mappings":"AAAA;;AAEA,OAAOA,MAAM,MAAM,0BAA0B;AAE7C,SAAsBC,eAAe;AAErC,OAAOC,YAAY;;AAEnB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAlBA,SAAAC,GAAA,IAAAC,IAAA,EAAAC,IAAA,IAAAC,KAAA;AAmBA,MAAMC,QAAiD,GAAGA,CAAC;EACzDC,MAAM;EACNC,KAAK;EACLC,QAAQ;EACRC,OAAO;EACPC,MAAM;EACNC,KAAK;EACLC;AACF,CAAC,KAAK;EACJ,IAAIC,YAAY,GAAG,KAAK;EACxB,MAAMC,cAAc,GAAG,EAAE;EAEzB,KAAK,MAAMC,MAAM,IAAIN,OAAO,EAAE;IAC5B,IAAI,CAACH,MAAM,IAAIA,MAAM,CAACS,MAAM,CAAC,EAAE;MAC7B,MAAM,CAACC,MAAM,EAAEC,KAAK,CAAC,GAAGlB,eAAe,CAACgB,MAAM,CAAC;MAC/CF,YAAY,KAAKG,MAAM,KAAKJ,KAAK;MACjCE,cAAc,CAACI,IAAI,cACjBhB,IAAA;QAAQiB,SAAS,EAAER,KAAK,CAACI,MAAO;QAAcH,KAAK,EAAEI,MAAO;QAAAI,QAAA,EACzDH;MAAK,GAD8BD,MAE9B,CACV,CAAC;IACH;EACF;;EAEA;EACA;EACA;EACA;EACA,MAAMK,YAAY,GAAGR,YAAY,GAAG,IAAI,gBACtCX,IAAA;IACEiB,SAAS,EAAER,KAAK,CAACU,YAAa;IAC9BC,QAAQ;IAERV,KAAK,EAAEA,KAAM;IAAAQ,QAAA,EAEZR;EAAK,GAHF,0BAIE,CACT;EAED,IAAIW,eAAe,GAAGZ,KAAK,CAACa,MAAM;EAClC,IAAI,CAACX,YAAY,EAAEU,eAAe,IAAI,IAAIZ,KAAK,CAACc,OAAO,EAAE;EAEzD,oBACErB,KAAA;IAAKe,SAAS,EAAER,KAAK,CAACe,SAAU;IAAAN,QAAA,GAC5Bb,KAAK,KAAKoB,SAAS,GACjB,IAAI,gBAAGzB,IAAA;MAAKiB,SAAS,EAAER,KAAK,CAACJ,KAAM;MAAAa,QAAA,EAAEb;IAAK,CAAM,CAAC,eACrDH,KAAA;MAAKe,SAAS,EAAER,KAAK,CAACiB,QAAS;MAAAR,QAAA,gBAC7BhB,KAAA;QACEe,SAAS,EAAEI,eAAgB;QAC3B,eAAaM,OAAO,CAACC,GAAG,CAACC,QAAQ,KAAK,YAAY,GAAGJ,SAAS,GAAGjB,MAAO;QACxEF,QAAQ,EAAEA,QAAS;QACnBI,KAAK,EAAEA,KAAM;QAAAQ,QAAA,GAEZC,YAAY,EACZP,cAAc;MAAA,CACT,CAAC,eACTZ,IAAA;QAAKiB,SAAS,EAAER,KAAK,CAACqB;MAAM,CAAE,CAAC;IAAA,CAC5B,CAAC;EAAA,CACH,CAAC;AAEV,CAAC;AAED,eAAelC,MAAM,CAACO,QAAQ,EAAE,UAAU,EAAEL,YAAY,CAAC","ignoreList":[]}