@eightshift/frontend-libs-tailwind 1.0.0

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 (275) hide show
  1. package/.gitattributes +37 -0
  2. package/.github/CODEOWNERS +1 -0
  3. package/.github/CONTRIBUTING.md +17 -0
  4. package/.github/ISSUE_TEMPLATE/bug_report.md +10 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.md +10 -0
  6. package/.github/ISSUE_TEMPLATE/question.md +10 -0
  7. package/.github/PULL_REQUEST_TEMPLATE.md +11 -0
  8. package/.github/workflows/ci.yml +31 -0
  9. package/.husky/pre-commit +1 -0
  10. package/.prettierrc +14 -0
  11. package/.stylelintrc +4 -0
  12. package/CHANGELOG.md +13 -0
  13. package/CODE_OF_CONDUCT.md +76 -0
  14. package/LICENSE +21 -0
  15. package/README.md +24 -0
  16. package/blocks/init/assets/application-admin.js +10 -0
  17. package/blocks/init/assets/application.js +13 -0
  18. package/blocks/init/assets/images/index.js +5 -0
  19. package/blocks/init/assets/scripts/application-admin.js +7 -0
  20. package/blocks/init/assets/scripts/application.js +3 -0
  21. package/blocks/init/assets/scripts/theme-colors.js +39 -0
  22. package/blocks/init/assets/styles/application-admin.css +6 -0
  23. package/blocks/init/assets/styles/application.css +3 -0
  24. package/blocks/init/src/Blocks/assets/application-blocks-editor.js +14 -0
  25. package/blocks/init/src/Blocks/assets/application-blocks-frontend.js +13 -0
  26. package/blocks/init/src/Blocks/assets/application-blocks.js +9 -0
  27. package/blocks/init/src/Blocks/assets/scripts/application-blocks-editor.js +65 -0
  28. package/blocks/init/src/Blocks/assets/scripts/application-blocks-frontend.js +24 -0
  29. package/blocks/init/src/Blocks/assets/scripts/link-section-editor.js +258 -0
  30. package/blocks/init/src/Blocks/assets/scripts/shared.js +25 -0
  31. package/blocks/init/src/Blocks/assets/styles/application-blocks-editor.css +15 -0
  32. package/blocks/init/src/Blocks/assets/styles/application-blocks-frontend.css +12 -0
  33. package/blocks/init/src/Blocks/assets/styles/application-blocks.css +0 -0
  34. package/blocks/init/src/Blocks/assets/styles/editor/editor-overrides.css +15 -0
  35. package/blocks/init/src/Blocks/assets/styles/tailwind.css +34 -0
  36. package/blocks/init/src/Blocks/components/admin-theme-options/admin-theme-options.php +20 -0
  37. package/blocks/init/src/Blocks/components/admin-theme-options/assets-admin/index.js +36 -0
  38. package/blocks/init/src/Blocks/components/admin-theme-options/assets-admin/pages/parts.js +55 -0
  39. package/blocks/init/src/Blocks/components/admin-theme-options/manifest.json +5 -0
  40. package/blocks/init/src/Blocks/components/button/assets/index.js +22 -0
  41. package/blocks/init/src/Blocks/components/button/button.php +77 -0
  42. package/blocks/init/src/Blocks/components/button/components/button-editor.js +40 -0
  43. package/blocks/init/src/Blocks/components/button/components/button-options.js +135 -0
  44. package/blocks/init/src/Blocks/components/button/manifest.json +299 -0
  45. package/blocks/init/src/Blocks/components/card/card.php +42 -0
  46. package/blocks/init/src/Blocks/components/card/components/card-editor.js +39 -0
  47. package/blocks/init/src/Blocks/components/card/components/card-options.js +56 -0
  48. package/blocks/init/src/Blocks/components/card/manifest.json +111 -0
  49. package/blocks/init/src/Blocks/components/head/head.php +52 -0
  50. package/blocks/init/src/Blocks/components/head/manifest.json +17 -0
  51. package/blocks/init/src/Blocks/components/heading/components/heading-editor.js +30 -0
  52. package/blocks/init/src/Blocks/components/heading/components/heading-options.js +62 -0
  53. package/blocks/init/src/Blocks/components/heading/heading.php +45 -0
  54. package/blocks/init/src/Blocks/components/heading/manifest.json +76 -0
  55. package/blocks/init/src/Blocks/components/hero/components/hero-editor.js +42 -0
  56. package/blocks/init/src/Blocks/components/hero/components/hero-options.js +100 -0
  57. package/blocks/init/src/Blocks/components/hero/hero.php +45 -0
  58. package/blocks/init/src/Blocks/components/hero/manifest.json +96 -0
  59. package/blocks/init/src/Blocks/components/icon/components/icon-editor.js +23 -0
  60. package/blocks/init/src/Blocks/components/icon/components/icon-options.js +65 -0
  61. package/blocks/init/src/Blocks/components/icon/icon.php +34 -0
  62. package/blocks/init/src/Blocks/components/icon/manifest.json +1234 -0
  63. package/blocks/init/src/Blocks/components/image/components/image-editor.js +65 -0
  64. package/blocks/init/src/Blocks/components/image/components/image-options.js +111 -0
  65. package/blocks/init/src/Blocks/components/image/image.php +62 -0
  66. package/blocks/init/src/Blocks/components/image/manifest.json +125 -0
  67. package/blocks/init/src/Blocks/components/list/components/list-editor.js +30 -0
  68. package/blocks/init/src/Blocks/components/list/components/list-options.js +62 -0
  69. package/blocks/init/src/Blocks/components/list/list.php +32 -0
  70. package/blocks/init/src/Blocks/components/list/manifest.json +127 -0
  71. package/blocks/init/src/Blocks/components/load-more/assets/index.js +19 -0
  72. package/blocks/init/src/Blocks/components/load-more/assets/load-more.js +147 -0
  73. package/blocks/init/src/Blocks/components/load-more/components/load-more-editor.js +14 -0
  74. package/blocks/init/src/Blocks/components/load-more/components/load-more-options.js +18 -0
  75. package/blocks/init/src/Blocks/components/load-more/load-more.php +47 -0
  76. package/blocks/init/src/Blocks/components/load-more/manifest.json +39 -0
  77. package/blocks/init/src/Blocks/components/modal/assets/index.js +27 -0
  78. package/blocks/init/src/Blocks/components/modal/manifest.json +48 -0
  79. package/blocks/init/src/Blocks/components/modal/modal.php +46 -0
  80. package/blocks/init/src/Blocks/components/paragraph/components/paragraph-editor.js +40 -0
  81. package/blocks/init/src/Blocks/components/paragraph/components/paragraph-options.js +48 -0
  82. package/blocks/init/src/Blocks/components/paragraph/manifest.json +83 -0
  83. package/blocks/init/src/Blocks/components/paragraph/paragraph.php +33 -0
  84. package/blocks/init/src/Blocks/components/post-header/manifest.json +5 -0
  85. package/blocks/init/src/Blocks/components/post-header/post-header.php +24 -0
  86. package/blocks/init/src/Blocks/components/quote/components/quote-editor.js +53 -0
  87. package/blocks/init/src/Blocks/components/quote/components/quote-options.js +29 -0
  88. package/blocks/init/src/Blocks/components/quote/manifest.json +47 -0
  89. package/blocks/init/src/Blocks/components/quote/quote.php +49 -0
  90. package/blocks/init/src/Blocks/components/share/assets/index.js +39 -0
  91. package/blocks/init/src/Blocks/components/share/components/share-editor.js +25 -0
  92. package/blocks/init/src/Blocks/components/share/components/share-options.js +112 -0
  93. package/blocks/init/src/Blocks/components/share/manifest.json +72 -0
  94. package/blocks/init/src/Blocks/components/share/share.php +66 -0
  95. package/blocks/init/src/Blocks/components/tracking-before-body-end/manifest.json +5 -0
  96. package/blocks/init/src/Blocks/components/tracking-before-body-end/tracking-before-body-end.php +9 -0
  97. package/blocks/init/src/Blocks/components/tracking-head/manifest.json +5 -0
  98. package/blocks/init/src/Blocks/components/tracking-head/tracking-head.php +9 -0
  99. package/blocks/init/src/Blocks/components/video/components/video-editor.js +74 -0
  100. package/blocks/init/src/Blocks/components/video/components/video-options.js +280 -0
  101. package/blocks/init/src/Blocks/components/video/manifest.json +116 -0
  102. package/blocks/init/src/Blocks/components/video/video.php +72 -0
  103. package/blocks/init/src/Blocks/custom/accordion/accordion-block.js +15 -0
  104. package/blocks/init/src/Blocks/custom/accordion/accordion.php +28 -0
  105. package/blocks/init/src/Blocks/custom/accordion/assets/index.js +37 -0
  106. package/blocks/init/src/Blocks/custom/accordion/components/accordion-editor.js +17 -0
  107. package/blocks/init/src/Blocks/custom/accordion/components/accordion-options.js +18 -0
  108. package/blocks/init/src/Blocks/custom/accordion/manifest.json +32 -0
  109. package/blocks/init/src/Blocks/custom/accordion-item/accordion-item-block.js +19 -0
  110. package/blocks/init/src/Blocks/custom/accordion-item/accordion-item.php +46 -0
  111. package/blocks/init/src/Blocks/custom/accordion-item/components/accordion-item-editor.js +60 -0
  112. package/blocks/init/src/Blocks/custom/accordion-item/manifest.json +69 -0
  113. package/blocks/init/src/Blocks/custom/button/button-block.js +13 -0
  114. package/blocks/init/src/Blocks/custom/button/button.php +11 -0
  115. package/blocks/init/src/Blocks/custom/button/components/button-editor.js +12 -0
  116. package/blocks/init/src/Blocks/custom/button/components/button-options.js +12 -0
  117. package/blocks/init/src/Blocks/custom/button/manifest.json +18 -0
  118. package/blocks/init/src/Blocks/custom/card/card-block.js +13 -0
  119. package/blocks/init/src/Blocks/custom/card/card.php +11 -0
  120. package/blocks/init/src/Blocks/custom/card/components/card-editor.js +12 -0
  121. package/blocks/init/src/Blocks/custom/card/components/card-options.js +15 -0
  122. package/blocks/init/src/Blocks/custom/card/manifest.json +18 -0
  123. package/blocks/init/src/Blocks/custom/carousel/assets/index.js +46 -0
  124. package/blocks/init/src/Blocks/custom/carousel/assets/navigation.js +31 -0
  125. package/blocks/init/src/Blocks/custom/carousel/assets/pagination.js +39 -0
  126. package/blocks/init/src/Blocks/custom/carousel/carousel-block.js +21 -0
  127. package/blocks/init/src/Blocks/custom/carousel/carousel.php +61 -0
  128. package/blocks/init/src/Blocks/custom/carousel/components/carousel-editor.js +25 -0
  129. package/blocks/init/src/Blocks/custom/carousel/components/carousel-options.js +47 -0
  130. package/blocks/init/src/Blocks/custom/carousel/manifest.json +130 -0
  131. package/blocks/init/src/Blocks/custom/column/column-block.js +21 -0
  132. package/blocks/init/src/Blocks/custom/column/column-hooks.js +32 -0
  133. package/blocks/init/src/Blocks/custom/column/column.php +21 -0
  134. package/blocks/init/src/Blocks/custom/column/components/column-editor.js +19 -0
  135. package/blocks/init/src/Blocks/custom/column/components/column-options.js +579 -0
  136. package/blocks/init/src/Blocks/custom/column/manifest.json +625 -0
  137. package/blocks/init/src/Blocks/custom/columns/columns-block.js +20 -0
  138. package/blocks/init/src/Blocks/custom/columns/columns.php +21 -0
  139. package/blocks/init/src/Blocks/custom/columns/components/columns-editor.js +81 -0
  140. package/blocks/init/src/Blocks/custom/columns/components/columns-options.js +104 -0
  141. package/blocks/init/src/Blocks/custom/columns/manifest.json +555 -0
  142. package/blocks/init/src/Blocks/custom/featured-content/components/featured-content-editor.js +28 -0
  143. package/blocks/init/src/Blocks/custom/featured-content/components/featured-content-options.js +239 -0
  144. package/blocks/init/src/Blocks/custom/featured-content/featured-content-block.js +13 -0
  145. package/blocks/init/src/Blocks/custom/featured-content/featured-content.php +139 -0
  146. package/blocks/init/src/Blocks/custom/featured-content/manifest.json +131 -0
  147. package/blocks/init/src/Blocks/custom/featured-content/partials/cards.php +39 -0
  148. package/blocks/init/src/Blocks/custom/group/components/group-editor.js +8 -0
  149. package/blocks/init/src/Blocks/custom/group/group-block.js +14 -0
  150. package/blocks/init/src/Blocks/custom/group/group.php +10 -0
  151. package/blocks/init/src/Blocks/custom/group/manifest.json +32 -0
  152. package/blocks/init/src/Blocks/custom/heading/components/heading-editor.js +15 -0
  153. package/blocks/init/src/Blocks/custom/heading/components/heading-options.js +38 -0
  154. package/blocks/init/src/Blocks/custom/heading/heading-block.js +13 -0
  155. package/blocks/init/src/Blocks/custom/heading/heading-transforms.js +25 -0
  156. package/blocks/init/src/Blocks/custom/heading/heading.php +15 -0
  157. package/blocks/init/src/Blocks/custom/heading/manifest.json +79 -0
  158. package/blocks/init/src/Blocks/custom/hero/components/hero-editor.js +12 -0
  159. package/blocks/init/src/Blocks/custom/hero/components/hero-options.js +14 -0
  160. package/blocks/init/src/Blocks/custom/hero/hero-block.js +13 -0
  161. package/blocks/init/src/Blocks/custom/hero/hero.php +11 -0
  162. package/blocks/init/src/Blocks/custom/hero/manifest.json +17 -0
  163. package/blocks/init/src/Blocks/custom/image/components/image-editor.js +12 -0
  164. package/blocks/init/src/Blocks/custom/image/components/image-options.js +14 -0
  165. package/blocks/init/src/Blocks/custom/image/image-block.js +13 -0
  166. package/blocks/init/src/Blocks/custom/image/image.php +11 -0
  167. package/blocks/init/src/Blocks/custom/image/manifest.json +105 -0
  168. package/blocks/init/src/Blocks/custom/list/components/list-editor.js +14 -0
  169. package/blocks/init/src/Blocks/custom/list/components/list-options.js +39 -0
  170. package/blocks/init/src/Blocks/custom/list/list-block.js +13 -0
  171. package/blocks/init/src/Blocks/custom/list/list.php +15 -0
  172. package/blocks/init/src/Blocks/custom/list/manifest.json +58 -0
  173. package/blocks/init/src/Blocks/custom/map/assets/index.js +20 -0
  174. package/blocks/init/src/Blocks/custom/map/assets/map-controller.js +42 -0
  175. package/blocks/init/src/Blocks/custom/map/assets/utils.js +161 -0
  176. package/blocks/init/src/Blocks/custom/map/components/map-components.js +403 -0
  177. package/blocks/init/src/Blocks/custom/map/components/map-editor.js +56 -0
  178. package/blocks/init/src/Blocks/custom/map/components/map-options.js +557 -0
  179. package/blocks/init/src/Blocks/custom/map/manifest.json +79 -0
  180. package/blocks/init/src/Blocks/custom/map/map-block.js +13 -0
  181. package/blocks/init/src/Blocks/custom/map/map.php +52 -0
  182. package/blocks/init/src/Blocks/custom/map/styles.css +2 -0
  183. package/blocks/init/src/Blocks/custom/modal/components/modal-editor.js +90 -0
  184. package/blocks/init/src/Blocks/custom/modal/manifest.json +51 -0
  185. package/blocks/init/src/Blocks/custom/modal/modal-block.js +14 -0
  186. package/blocks/init/src/Blocks/custom/modal/modal.php +20 -0
  187. package/blocks/init/src/Blocks/custom/paragraph/components/paragraph-editor.js +41 -0
  188. package/blocks/init/src/Blocks/custom/paragraph/components/paragraph-options.js +38 -0
  189. package/blocks/init/src/Blocks/custom/paragraph/manifest.json +79 -0
  190. package/blocks/init/src/Blocks/custom/paragraph/paragraph-block.js +13 -0
  191. package/blocks/init/src/Blocks/custom/paragraph/paragraph-transforms.js +45 -0
  192. package/blocks/init/src/Blocks/custom/paragraph/paragraph.php +15 -0
  193. package/blocks/init/src/Blocks/custom/quote/components/quote-editor.js +12 -0
  194. package/blocks/init/src/Blocks/custom/quote/components/quote-options.js +14 -0
  195. package/blocks/init/src/Blocks/custom/quote/manifest.json +17 -0
  196. package/blocks/init/src/Blocks/custom/quote/quote-block.js +13 -0
  197. package/blocks/init/src/Blocks/custom/quote/quote.php +13 -0
  198. package/blocks/init/src/Blocks/custom/share/components/share-editor.js +6 -0
  199. package/blocks/init/src/Blocks/custom/share/components/share-options.js +12 -0
  200. package/blocks/init/src/Blocks/custom/share/manifest.json +19 -0
  201. package/blocks/init/src/Blocks/custom/share/share-block.js +13 -0
  202. package/blocks/init/src/Blocks/custom/share/share.php +11 -0
  203. package/blocks/init/src/Blocks/custom/site-footer/components/site-footer-editor.js +57 -0
  204. package/blocks/init/src/Blocks/custom/site-footer/components/site-footer-options.js +106 -0
  205. package/blocks/init/src/Blocks/custom/site-footer/manifest.json +73 -0
  206. package/blocks/init/src/Blocks/custom/site-footer/site-footer-block.js +13 -0
  207. package/blocks/init/src/Blocks/custom/site-footer/site-footer.php +72 -0
  208. package/blocks/init/src/Blocks/custom/site-navigation/assets/index.js +9 -0
  209. package/blocks/init/src/Blocks/custom/site-navigation/components/site-navigation-editor.js +42 -0
  210. package/blocks/init/src/Blocks/custom/site-navigation/components/site-navigation-options.js +73 -0
  211. package/blocks/init/src/Blocks/custom/site-navigation/manifest.json +77 -0
  212. package/blocks/init/src/Blocks/custom/site-navigation/site-navigation-block.js +13 -0
  213. package/blocks/init/src/Blocks/custom/site-navigation/site-navigation.php +99 -0
  214. package/blocks/init/src/Blocks/custom/table-of-contents/assets/index.js +58 -0
  215. package/blocks/init/src/Blocks/custom/table-of-contents/components/table-of-contents-editor.js +38 -0
  216. package/blocks/init/src/Blocks/custom/table-of-contents/components/table-of-contents-options.js +32 -0
  217. package/blocks/init/src/Blocks/custom/table-of-contents/manifest.json +48 -0
  218. package/blocks/init/src/Blocks/custom/table-of-contents/table-of-contents-block.js +13 -0
  219. package/blocks/init/src/Blocks/custom/table-of-contents/table-of-contents.php +31 -0
  220. package/blocks/init/src/Blocks/custom/video/components/video-editor.js +6 -0
  221. package/blocks/init/src/Blocks/custom/video/components/video-options.js +12 -0
  222. package/blocks/init/src/Blocks/custom/video/manifest.json +105 -0
  223. package/blocks/init/src/Blocks/custom/video/video-block.js +13 -0
  224. package/blocks/init/src/Blocks/custom/video/video.php +22 -0
  225. package/blocks/init/src/Blocks/manifest.json +14 -0
  226. package/blocks/init/src/Blocks/variations/card-simple/manifest.json +17 -0
  227. package/blocks/init/src/Blocks/wrapper/components/wrapper-editor.js +12 -0
  228. package/blocks/init/src/Blocks/wrapper/components/wrapper-options.js +348 -0
  229. package/blocks/init/src/Blocks/wrapper/manifest.json +450 -0
  230. package/blocks/init/src/Blocks/wrapper/styles-editor.css +13 -0
  231. package/blocks/init/src/Blocks/wrapper/styles.css +19 -0
  232. package/blocks/init/src/Blocks/wrapper/wrapper.js +30 -0
  233. package/blocks/init/src/Blocks/wrapper/wrapper.php +34 -0
  234. package/eslint.config.mjs +3 -0
  235. package/linters/base.config.mjs +81 -0
  236. package/linters/eslint.config.mjs +4 -0
  237. package/linters/ignore-gitignored.mjs +9 -0
  238. package/linters/stylelint.config.js +146 -0
  239. package/package.json +93 -0
  240. package/readme/packages.md +74 -0
  241. package/schemas/block.json +302 -0
  242. package/schemas/component.json +240 -0
  243. package/schemas/globalManifest.json +73 -0
  244. package/schemas/variation.json +83 -0
  245. package/scripts/components/block-inserter.js +70 -0
  246. package/scripts/components/file-picker.js +243 -0
  247. package/scripts/components/index.js +6 -0
  248. package/scripts/components/link-section-editor.js +319 -0
  249. package/scripts/components/media-picker.js +86 -0
  250. package/scripts/components/server-side-render.js +37 -0
  251. package/scripts/components/settings/settings.js +48 -0
  252. package/scripts/components/settings/use-theme-options.js +52 -0
  253. package/scripts/editor/attributes.js +304 -0
  254. package/scripts/editor/colors.js +64 -0
  255. package/scripts/editor/editor.js +139 -0
  256. package/scripts/editor/fetch.js +102 -0
  257. package/scripts/editor/hooks.js +44 -0
  258. package/scripts/editor/index.js +11 -0
  259. package/scripts/editor/options.js +152 -0
  260. package/scripts/editor/registration.js +901 -0
  261. package/scripts/editor/store.js +213 -0
  262. package/scripts/editor/tailwindcss.js +224 -0
  263. package/scripts/editor/utility.js +47 -0
  264. package/scripts/helpers/breakpoints.js +48 -0
  265. package/scripts/helpers/cookies.js +65 -0
  266. package/scripts/helpers/dynamic-import.js +19 -0
  267. package/scripts/helpers/index.js +11 -0
  268. package/scripts/index.js +22 -0
  269. package/scripts/plugins/index.js +3 -0
  270. package/scripts/plugins/yoast-seo.js +70 -0
  271. package/webpack/base.mjs +138 -0
  272. package/webpack/helpers.mjs +67 -0
  273. package/webpack/index.mjs +52 -0
  274. package/webpack/production.mjs +60 -0
  275. package/webpack/project.mjs +55 -0
@@ -0,0 +1,243 @@
1
+ import React from 'react';
2
+ import { __ } from '@wordpress/i18n';
3
+ import { MediaUpload, MediaUploadCheck } from '@wordpress/block-editor';
4
+ import { Button, HStack, VStack, FilePlaceholder, AnimatedVisibility } from '@eightshift/ui-components';
5
+ import { icons } from '@eightshift/ui-components/icons';
6
+
7
+ /**
8
+ * A customizable button for managing files from the Media library.
9
+ *
10
+ * @component
11
+ * @param {Object} props - Component props.
12
+ * @property {ManageFileButtonType} [props.type] - The type of the button (browse, upload, replace).
13
+ * @property {Function} props.onChange - Function that handles the change event.
14
+ * @property {string} [props.currentId] - ID of the currently selected item. Used for the 'replace' type, to mark the currently selected item.
15
+ * @property {boolean} [props.compact] - Whether the button is compact (icon-only).
16
+ * @property {string[]} props.allowedTypes - Determines types of files which are allowed to be uploaded.
17
+ * @property {ManageFileButtonKind} [props.kind] - The kind of file to manage. Controls labels and icons on the buttons.
18
+ * @property {Object} [props.labels] - Custom UI labels for the buttons. Applies only if `kind` is set to `custom`.
19
+ *
20
+ * @returns {JSX.Element} The ManageFileButton component.
21
+ *
22
+ * @typedef {'browse' | 'upload' | 'replace'} ManageFileButtonType
23
+ * @typedef {'file' | 'image' | 'video' | 'subtitle' | 'geoJson' | 'custom'} ManageFileButtonKind
24
+ *
25
+ * @example
26
+ * <ManageFileButton />
27
+ *
28
+ */
29
+ export const ManageFileButton = (props) => {
30
+ const {
31
+ type = 'browse',
32
+ onChange,
33
+ currentId,
34
+
35
+ labels,
36
+ allowedTypes,
37
+
38
+ kind = 'file',
39
+
40
+ compact = false,
41
+ } = props;
42
+
43
+ const strings = {
44
+ file: {
45
+ buttonTooltip: {
46
+ browse: __('Select a file from Media library', '%g_textdomain%'),
47
+ upload: __('Upload a file', '%g_textdomain%'),
48
+ replace: __('Replace file', '%g_textdomain%'),
49
+ },
50
+ buttonLabel: {
51
+ browse: __('Select', '%g_textdomain%'),
52
+ upload: __('Upload', '%g_textdomain%'),
53
+ replace: __('Replace', '%g_textdomain%'),
54
+ },
55
+ modalTitle: {
56
+ browse: __('Select a file', '%g_textdomain%'),
57
+ upload: __('Upload a file', '%g_textdomain%'),
58
+ replace: __('Select a new file', '%g_textdomain%'),
59
+ },
60
+ buttonIcon: {
61
+ browse: icons.itemSelect,
62
+ upload: icons.upload,
63
+ replace: icons.swap,
64
+ },
65
+ },
66
+ video: {
67
+ buttonTooltip: {
68
+ browse: __('Select a video from Media library', '%g_textdomain%'),
69
+ upload: __('Upload a video', '%g_textdomain%'),
70
+ replace: __('Replace video', '%g_textdomain%'),
71
+ },
72
+ modalTitle: {
73
+ browse: __('Select a video', '%g_textdomain%'),
74
+ upload: __('Upload a video', '%g_textdomain%'),
75
+ replace: __('Select a new video', '%g_textdomain%'),
76
+ },
77
+ },
78
+ image: {
79
+ buttonTooltip: {
80
+ browse: __('Select an image from Media library', '%g_textdomain%'),
81
+ upload: __('Upload an image', '%g_textdomain%'),
82
+ replace: __('Replace image', '%g_textdomain%'),
83
+ },
84
+ modalTitle: {
85
+ browse: __('Select an image', '%g_textdomain%'),
86
+ upload: __('Upload an image', '%g_textdomain%'),
87
+ replace: __('Select a new image', '%g_textdomain%'),
88
+ },
89
+ },
90
+ subtitle: {
91
+ buttonTooltip: {
92
+ browse: __('Select a subtitle file', '%g_textdomain%'),
93
+ upload: __('Upload a subtitle file', '%g_textdomain%'),
94
+ replace: __('Replace subtitle file', '%g_textdomain%'),
95
+ },
96
+ modalTitle: {
97
+ browse: __('Select a subtitle file', '%g_textdomain%'),
98
+ upload: __('Upload a subtitle file', '%g_textdomain%'),
99
+ replace: __('Select a new subtitle file', '%g_textdomain%'),
100
+ },
101
+ },
102
+ geoJson: {
103
+ buttonTooltip: {
104
+ browse: __('Select a GeoJSON file', '%g_textdomain%'),
105
+ upload: __('Upload a GeoJSON file', '%g_textdomain%'),
106
+ replace: __('Replace GeoJSON file', '%g_textdomain%'),
107
+ },
108
+ modalTitle: {
109
+ browse: __('Select a GeoJSON file', '%g_textdomain%'),
110
+ upload: __('Upload a GeoJSON file', '%g_textdomain%'),
111
+ replace: __('Select a new GeoJSON file', '%g_textdomain%'),
112
+ },
113
+ },
114
+ custom: {
115
+ buttonTooltip: labels?.buttonTooltip,
116
+ buttonLabel: labels?.buttonLabel,
117
+ modalTitle: labels?.modalTitle,
118
+ buttonIcon: labels?.buttonIcon,
119
+ },
120
+ };
121
+
122
+ const buttonTooltip = strings?.[kind]?.buttonTooltip?.[type] ?? strings.file.buttonTooltip?.[type];
123
+ const buttonLabel = strings?.[kind]?.buttonLabel?.[type] ?? strings.file.buttonLabel?.[type];
124
+ const buttonIcon = strings?.[kind]?.buttonIcon?.[type] ?? strings.file.buttonIcon?.[type];
125
+ const modalTitle = strings?.[kind]?.modalTitle?.[type] ?? strings.file.modalTitle?.[type];
126
+
127
+ return (
128
+ <MediaUploadCheck>
129
+ <MediaUpload
130
+ onSelect={({ id, url, ...rest }) => onChange({ id, url, ...rest })}
131
+ allowedTypes={allowedTypes}
132
+ value={type === 'replace' && currentId}
133
+ title={modalTitle}
134
+ mode={type === 'upload' ? 'upload' : 'browse'}
135
+ render={({ open }) => (
136
+ <Button
137
+ onPress={open}
138
+ icon={compact && buttonIcon}
139
+ tooltip={buttonTooltip}
140
+ >
141
+ {!compact && buttonLabel}
142
+ </Button>
143
+ )}
144
+ />
145
+ </MediaUploadCheck>
146
+ );
147
+ };
148
+
149
+ /**
150
+ * Renders a component for managing a media file
151
+ *
152
+ * @component
153
+ * @param {Object} props - Component props.
154
+ * @property {Function} props.onChange - The function that handles the change event.
155
+ * @property {string} props.fileId - ID of the currently selected file. Used to mark the currently selected item when replacing the file.
156
+ * @property {string} props.fileName - URL of the currently selected image.
157
+ * @property {boolean} [props.noDelete] - If `true`, the delete button will be hidden.
158
+ * @property {boolean} [props.noUpload] - If `true`, the upload button will be hidden.
159
+ * @property {string[]} props.allowedTypes - Determines types of files which are allowed to be uploaded.
160
+ * @property {FileKind} [props.kind] - The kind of file to manage.
161
+ * @property {Object} [props.labels] - Custom UI labels for the buttons. Applies only if `kind` is set to `custom`.
162
+ *
163
+ * @returns {JSX.Element} The FileSelector component.
164
+ *
165
+ * @typedef {'file' | 'image' | 'video' | 'subtitle' | 'geoJson' | 'custom'} FileKind
166
+ *
167
+ * @example
168
+ * <FileSelector
169
+ * onChange={onChange}
170
+ * fileId={fileId}
171
+ * fileName={fileName}
172
+ * allowedTypes={['video']}
173
+ * />
174
+ *
175
+ */
176
+ export const FileSelector = (props) => {
177
+ const { onChange, fileId, fileName, noDelete, noUpload, labels, allowedTypes, kind = 'file' } = props;
178
+
179
+ const commonManageFileButtonProps = {
180
+ onChange,
181
+ allowedTypes,
182
+ kind,
183
+ labels,
184
+ };
185
+
186
+ const removeTooltips = {
187
+ file: __('Remove file', '%g_textdomain%'),
188
+ image: __('Remove image', '%g_textdomain%'),
189
+ video: __('Remove video', '%g_textdomain%'),
190
+ subtitle: __('Remove subtitle file', '%g_textdomain%'),
191
+ geoJson: __('Remove GeoJSON file', '%g_textdomain%'),
192
+ custom: labels?.removeTooltip,
193
+ };
194
+
195
+ const fileIcons = {
196
+ image: icons.imageFile,
197
+ video: icons.videoFile,
198
+ subtitle: icons.closedCaptions,
199
+ geoJson: icons.fileMetadata,
200
+ custom: labels?.removeIcon,
201
+ };
202
+
203
+ return (
204
+ <VStack noWrap>
205
+ <FilePlaceholder
206
+ icon={fileIcons[kind] ?? icons.file}
207
+ fileName={fileName}
208
+ >
209
+ <HStack noWrap className='es-uic-pl-1'>
210
+ <ManageFileButton {...commonManageFileButtonProps} />
211
+ {!noUpload && (
212
+ <ManageFileButton
213
+ {...commonManageFileButtonProps}
214
+ type='upload'
215
+ compact
216
+ />
217
+ )}
218
+ </HStack>
219
+ </FilePlaceholder>
220
+
221
+ <AnimatedVisibility
222
+ visible={fileName}
223
+ noInitial
224
+ >
225
+ <HStack noWrap>
226
+ <ManageFileButton
227
+ {...commonManageFileButtonProps}
228
+ currentId={fileId}
229
+ type='replace'
230
+ />
231
+ {!noDelete && (
232
+ <Button
233
+ icon={icons.trash}
234
+ tooltip={removeTooltips[kind] ?? removeTooltips.file}
235
+ onPress={() => onChange({ id: undefined, url: undefined })}
236
+ type='danger'
237
+ />
238
+ )}
239
+ </HStack>
240
+ </AnimatedVisibility>
241
+ </VStack>
242
+ );
243
+ };
@@ -0,0 +1,6 @@
1
+ export { BlockInserter } from './block-inserter';
2
+ export { LinkSectionEditor } from './link-section-editor';
3
+ export { ManageFileButton, FileSelector } from './file-picker';
4
+ export { MediaPicker } from './media-picker';
5
+ export { ServerSideRender } from './server-side-render';
6
+ export { ThemeOptionsPage, EsThemeOptionsContext } from './settings/settings';
@@ -0,0 +1,319 @@
1
+ /* eslint-disable react-hooks/exhaustive-deps */
2
+ import { useCallback } from 'react';
3
+ import { __ } from '@wordpress/i18n';
4
+ import { RichText } from '@wordpress/block-editor';
5
+ import { Button } from '@eightshift/ui-components';
6
+ import { icons } from '@eightshift/ui-components/icons';
7
+
8
+ /**
9
+ * Link section editor.
10
+ *
11
+ * @component
12
+ * @param {object} props - Component props.
13
+ * @param {{ header: string, items: [{ text: string, url: string, newTab: boolean }] }[]} props.links - Link data.
14
+ * @param {Function} props.onChange - Function to call when links are changed.
15
+ * @param {object} props.classNames - Classes to pass to inner components.
16
+ *
17
+ * @returns {object} LinkSectionEditor component.
18
+ *
19
+ * @example
20
+ * const links = [
21
+ * {
22
+ * header: 'Section 1',
23
+ * items: [
24
+ * {
25
+ * text: 'Link 1',
26
+ * url: 'https://example.com',
27
+ * newTab: false,
28
+ * },
29
+ * {
30
+ * text: 'Link 2',
31
+ * url: 'https://example.com',
32
+ * newTab: false,
33
+ * },
34
+ * ],
35
+ * },
36
+ * {
37
+ * header: 'Section 2',
38
+ * items: [
39
+ * {
40
+ * text: 'Link 1',
41
+ * url: 'https://example.com',
42
+ * newTab: false,
43
+ * },
44
+ * {
45
+ * text: 'Link 2',
46
+ * url: 'https://example.com',
47
+ * newTab: false,
48
+ * },
49
+ * ],
50
+ * },
51
+ * ];
52
+ *
53
+ * <LinkSectionEditor
54
+ * links={links}
55
+ * onChange={(newLinks) => setAttributes({ links: newLinks })}
56
+ * classNames={{
57
+ * sectionContainer: 'section-container',
58
+ * sectionTitle: 'section-title',
59
+ * link: 'link',
60
+ * }}
61
+ * />;
62
+ */
63
+ export const LinkSectionEditor = (props) => {
64
+ const { links, onChange, classNames } = props;
65
+
66
+ const defaultLink = {
67
+ text: '',
68
+ url: '',
69
+ newTab: false,
70
+ };
71
+
72
+ const defaultSection = {
73
+ header: '',
74
+ items: [defaultLink],
75
+ };
76
+
77
+ const addSection = useCallback(
78
+ (index = -1) => {
79
+ if (index < 0) {
80
+ onChange([...links, defaultSection]);
81
+ return;
82
+ }
83
+
84
+ const before = links.slice(0, index + 1);
85
+ const after = links.slice(index + 1);
86
+
87
+ onChange([...before, defaultSection, ...after]);
88
+ },
89
+ [links, onChange],
90
+ );
91
+
92
+ const addLink = useCallback(
93
+ (items, itemIndex, index = -1) => {
94
+ if (index < 0) {
95
+ updateItem(itemIndex, {
96
+ items: [defaultLink, ...items],
97
+ });
98
+ return;
99
+ }
100
+ const before = items.slice(0, index + 1);
101
+ const after = items.slice(index + 1);
102
+
103
+ updateItem(itemIndex, {
104
+ items: [...before, defaultLink, ...after],
105
+ });
106
+ },
107
+ [links, onChange],
108
+ );
109
+
110
+ const updateItem = useCallback(
111
+ (index, value) => {
112
+ const newValue = [...links];
113
+
114
+ newValue[index] = {
115
+ ...newValue[index],
116
+ ...value,
117
+ };
118
+
119
+ onChange(newValue);
120
+ },
121
+ [links, onChange],
122
+ );
123
+
124
+ const updateInnerItem = useCallback(
125
+ (index, innerIndex, value) => {
126
+ const newValue = [...links];
127
+
128
+ newValue[index].items[innerIndex] = {
129
+ ...newValue[index].items[innerIndex],
130
+ ...value,
131
+ };
132
+
133
+ onChange(newValue);
134
+ },
135
+ [links, onChange],
136
+ );
137
+
138
+ const jumpToEnd = useCallback(
139
+ (element) => {
140
+ if (!element) {
141
+ return;
142
+ }
143
+
144
+ const textNode = element.childNodes[0];
145
+
146
+ if (!textNode) {
147
+ return;
148
+ }
149
+
150
+ const selection = document.getSelection();
151
+ selection.removeAllRanges();
152
+
153
+ const range = document.createRange();
154
+ range.setStart(textNode, textNode?.length);
155
+ range.setEnd(textNode, textNode?.length);
156
+
157
+ selection.addRange(range);
158
+ },
159
+ [links, onChange],
160
+ );
161
+
162
+ const handleKeyDown = useCallback(
163
+ (event, index, i, text) => {
164
+ const { code, target } = event;
165
+
166
+ const { baseNode, baseOffset } = document.getSelection();
167
+
168
+ if (!baseNode || !target.contains(baseNode)) {
169
+ return;
170
+ }
171
+
172
+ if (baseOffset === text.length && code === 'Enter' && !(event.metaKey || event.ctrlKey)) {
173
+ event.preventDefault();
174
+ addLink(links[index].items, index, i);
175
+
176
+ setTimeout(() => {
177
+ target.nextElementSibling?.focus();
178
+ }, 25);
179
+ } else if (baseOffset === text.length && code === 'Enter' && (event.metaKey || event.ctrlKey)) {
180
+ event.preventDefault();
181
+ addSection(index);
182
+
183
+ setTimeout(() => {
184
+ target.parentElement.nextElementSibling?.querySelector('[contenteditable="true"]')?.focus();
185
+ }, 25);
186
+ } else if (code === 'Backspace' && text === '') {
187
+ event.preventDefault();
188
+
189
+ jumpToEnd(target.previousElementSibling);
190
+
191
+ updateItem(index, {
192
+ items: links[index].items.filter((_, j) => j !== i),
193
+ });
194
+ } else if (code === 'Delete' && links?.[index].items[i + 1]?.text?.length < 1) {
195
+ event.preventDefault();
196
+
197
+ updateItem(index, {
198
+ items: links[index].items.filter((_, j) => j !== i + 1),
199
+ });
200
+ }
201
+ },
202
+ [links, onChange],
203
+ );
204
+
205
+ // keyDown handling for section headers.
206
+ const handleSectionKeyDown = useCallback(
207
+ (event, index, items, header) => {
208
+ const { code, target } = event;
209
+
210
+ const { baseNode, baseOffset } = document.getSelection();
211
+
212
+ if (!target.contains(baseNode)) {
213
+ return;
214
+ }
215
+
216
+ if (baseOffset === header.length && code === 'Enter' && !(event.metaKey || event.ctrlKey)) {
217
+ event.preventDefault();
218
+
219
+ // Add a link to the first spot.
220
+ if (items.length > 1 || items?.[0]?.text !== '') {
221
+ addLink(links[index].items, index);
222
+ }
223
+
224
+ // Focus on that link.
225
+ setTimeout(() => {
226
+ target.nextElementSibling?.focus();
227
+ }, 25);
228
+ } else if (baseOffset === header.length && code === 'Enter' && (event.metaKey || event.ctrlKey)) {
229
+ event.preventDefault();
230
+
231
+ // Add a section.
232
+ addSection(index);
233
+
234
+ // Focus on next text input.
235
+ setTimeout(() => {
236
+ target.parentElement.nextElementSibling?.querySelector('[contenteditable="true"]')?.focus();
237
+ }, 25);
238
+ } else if (code === 'Backspace' && header === '' && (items?.length < 1 || items?.every(({ text }) => text === ''))) {
239
+ event.preventDefault();
240
+
241
+ // Jump to end of previous input.
242
+ jumpToEnd(target.parentElement.previousElementSibling?.querySelector('[contenteditable="true"]:last-of-type'));
243
+
244
+ // Remove current element.
245
+ setTimeout(() => {
246
+ onChange(links.filter((_, i) => i !== index));
247
+ }, 25);
248
+ }
249
+ },
250
+ [links, onChange],
251
+ );
252
+
253
+ return (
254
+ <>
255
+ {links.map(({ items, header }, index) => {
256
+ return (
257
+ <div className={classNames?.sectionContainer} key={index}>
258
+ <RichText
259
+ placeholder={__('Section', 'eightshift-frontend-libs')}
260
+ value={header}
261
+ onChange={(value) => updateItem(index, { header: value })}
262
+ allowedFormats={[]}
263
+ className={classNames?.sectionTitle}
264
+ onKeyDown={(event) => handleSectionKeyDown(event, index, items, header)}
265
+ withoutInteractiveFormatting
266
+ disableLineBreaks
267
+ />
268
+
269
+ {items.map(({ text }, i) => {
270
+ return (
271
+ <RichText
272
+ key={i}
273
+ placeholder={__('Item', 'eightshift-frontend-libs')}
274
+ value={text}
275
+ onChange={(value) => updateInnerItem(index, i, { text: value })}
276
+ allowedFormats={[]}
277
+ className={classNames?.link}
278
+ onKeyDown={(event) => handleKeyDown(event, index, i, text)}
279
+ withoutInteractiveFormatting
280
+ disableLineBreaks
281
+ />
282
+ );
283
+ })}
284
+
285
+ <Button
286
+ size='small'
287
+ icon={icons.add}
288
+ tooltip={__('Add link', 'eightshift-frontend-libs')}
289
+ onPress={({ target }) => {
290
+ updateItem(index, {
291
+ items: [...links[index].items, defaultLink],
292
+ });
293
+
294
+ setTimeout(() => {
295
+ target?.previousElementSibling?.focus();
296
+ }, 20);
297
+ }}
298
+ />
299
+ </div>
300
+ );
301
+ })}
302
+
303
+ <Button
304
+ onPress={({ target }) => {
305
+ addSection();
306
+
307
+ setTimeout(() => {
308
+ target?.previousElementSibling?.querySelector('[contenteditable="true"]')?.focus();
309
+ }, 20);
310
+ }}
311
+ icon={icons.add}
312
+ tooltip={__('Add a section', 'eightshift-frontend-libs')}
313
+ className='justify-self-start'
314
+ >
315
+ {__('Section', 'eightshift-frontend-libs')}
316
+ </Button>
317
+ </>
318
+ );
319
+ };
@@ -0,0 +1,86 @@
1
+ import React from 'react';
2
+ import { __ } from '@wordpress/i18n';
3
+ import { Button, HStack, ImagePlaceholder } from '@eightshift/ui-components';
4
+ import { icons } from '@eightshift/ui-components/icons';
5
+ import { ManageFileButton } from '@eightshift/frontend-libs-tailwind/scripts/components/file-picker';
6
+
7
+ const MediaButton = (props) => {
8
+ return (
9
+ <ManageFileButton
10
+ {...props}
11
+ kind='image'
12
+ allowedTypes={['image']}
13
+ />
14
+ );
15
+ };
16
+
17
+ /**
18
+ * Renders a component for managing a media file
19
+ *
20
+ * @component
21
+ * @param {Object} props - Component props.
22
+ * @property {Function} props.onChange - The function that handles the change event.
23
+ * @property {string} props.imageId - ID of the currently selected image. Used to mark the currently selected item when replacing the image.
24
+ * @property {string} props.imageAlt - Alt text of the currently selected image.
25
+ * @property {string} props.imageUrl - URL of the currently selected image.
26
+ * @property {boolean} [props.noDelete] - If `true`, the delete button will be hidden.
27
+ * @property {boolean} [props.noUpload] - If `true`, the upload button will be hidden.
28
+ * @param {ImagePlaceholderImageMode} [props.imageMode='cover'] - Determines inner image display mode.
29
+ *
30
+ * @returns {JSX.Element} The MediaPicker component.
31
+ *
32
+ * @typedef {'cover'|'contain' | 'fill'} ImagePlaceholderImageMode
33
+ *
34
+ * @example
35
+ * <MediaPicker
36
+ * onChange={onChange}
37
+ * imageId={imageId}
38
+ * imageAlt={imageAlt}
39
+ * imageUrl={imageUrl}
40
+ * />
41
+ *
42
+ */
43
+ export const MediaPicker = (props) => {
44
+ const { onChange, imageId, imageAlt, imageUrl, noDelete, noUpload, imageMode } = props;
45
+
46
+ return (
47
+ <HStack noWrap>
48
+ <ImagePlaceholder
49
+ url={imageUrl}
50
+ alt={imageAlt}
51
+ imageMode={imageMode}
52
+ />
53
+
54
+ {!imageUrl && (
55
+ <>
56
+ <MediaButton onChange={onChange} />
57
+ {!noUpload && (
58
+ <MediaButton
59
+ onChange={onChange}
60
+ type='upload'
61
+ compact
62
+ />
63
+ )}
64
+ </>
65
+ )}
66
+
67
+ {imageUrl && (
68
+ <>
69
+ <MediaButton
70
+ type='replace'
71
+ onChange={onChange}
72
+ imageId={imageId}
73
+ />
74
+ {!noDelete && (
75
+ <Button
76
+ icon={icons.trash}
77
+ tooltip={__('Remove image', 'infobip')}
78
+ onPress={() => onChange({ id: undefined, url: undefined })}
79
+ type='danger'
80
+ />
81
+ )}
82
+ </>
83
+ )}
84
+ </HStack>
85
+ );
86
+ };
@@ -0,0 +1,37 @@
1
+ import GutenbergSsr from '@wordpress/server-side-render';
2
+ import { clsx } from '@eightshift/ui-components/utilities';
3
+
4
+ /**
5
+ * Wraps Gutenberg's ServerSideRender component with a bit of extra styles.
6
+ *
7
+ * @component
8
+ * @param {Object} props - Component props.
9
+ * @param {string} props.block - Fully qualified block name.
10
+ * @param {Object} props.attributes - Block attributes.
11
+ * @param {string} props.className - Classes to pass to the rendered content wrapper.
12
+ *
13
+ * @returns {JSX.Element} The ServerSideRender component.
14
+ *
15
+ * @example
16
+ * <ServerSideRender
17
+ * block="core/latest-posts"
18
+ * attributes={{
19
+ * myProp: true,
20
+ * }}
21
+ * />
22
+ *
23
+ */
24
+ export const ServerSideRender = (props) => {
25
+ const { block, attributes, className, ...rest } = props;
26
+ return (
27
+ <GutenbergSsr
28
+ {...rest}
29
+ block={block}
30
+ attributes={attributes}
31
+ className={clsx(
32
+ 'es-uic-pointer-events-none es-uic-rounded-lg es-uic-border es-uic-border-dotted es-uic-border-gray-300 es-uic-p-2 es-uic-flow-root',
33
+ className,
34
+ )}
35
+ />
36
+ );
37
+ };