@firecms/core 3.0.1 → 3.1.0-canary.02232f4

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 (334) hide show
  1. package/README.md +1 -1
  2. package/dist/components/AIIcon.d.ts +16 -0
  3. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +7 -1
  4. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +1 -1
  5. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +14 -0
  6. package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +6 -0
  7. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +5 -4
  8. package/dist/components/EntityCollectionTable/internal/EntityTableCell.d.ts +6 -0
  9. package/dist/components/EntityCollectionTable/internal/popup_field/useDraggable.d.ts +2 -2
  10. package/dist/components/EntityCollectionView/Board.d.ts +2 -0
  11. package/dist/components/EntityCollectionView/BoardColumn.d.ts +42 -0
  12. package/dist/components/EntityCollectionView/BoardColumnTitle.d.ts +9 -0
  13. package/dist/components/EntityCollectionView/BoardSortableList.d.ts +14 -0
  14. package/dist/components/EntityCollectionView/CollectionDataErrorBanner.d.ts +4 -0
  15. package/dist/components/EntityCollectionView/EntityBoardCard.d.ts +26 -0
  16. package/dist/components/EntityCollectionView/EntityCard.d.ts +19 -0
  17. package/dist/components/EntityCollectionView/EntityCollectionBoardView.d.ts +20 -0
  18. package/dist/components/EntityCollectionView/EntityCollectionCardView.d.ts +31 -0
  19. package/dist/components/EntityCollectionView/EntityCollectionViewActions.d.ts +2 -2
  20. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +7 -3
  21. package/dist/components/EntityCollectionView/FiltersDialog.d.ts +14 -0
  22. package/dist/components/EntityCollectionView/ViewModeToggle.d.ts +44 -0
  23. package/dist/components/EntityCollectionView/board_types.d.ts +105 -0
  24. package/dist/components/EntityCollectionView/useBoardDataController.d.ts +60 -0
  25. package/dist/components/ErrorBoundary.d.ts +4 -2
  26. package/dist/components/HomePage/DefaultHomePage.d.ts +0 -1
  27. package/dist/components/LanguageToggle.d.ts +1 -0
  28. package/dist/components/SelectableTable/SelectableTable.d.ts +5 -1
  29. package/dist/components/SelectableTable/filters/DateTimeFilterField.d.ts +2 -1
  30. package/dist/components/UnsavedChangesDialog.d.ts +1 -0
  31. package/dist/components/VirtualTable/VirtualTableCell.d.ts +6 -0
  32. package/dist/components/VirtualTable/VirtualTableHeader.d.ts +4 -1
  33. package/dist/components/VirtualTable/VirtualTableHeaderRow.d.ts +1 -1
  34. package/dist/components/VirtualTable/VirtualTableProps.d.ts +17 -1
  35. package/dist/components/VirtualTable/fields/VirtualTableDateField.d.ts +1 -0
  36. package/dist/components/VirtualTable/types.d.ts +3 -0
  37. package/dist/components/index.d.ts +4 -0
  38. package/dist/contexts/index.d.ts +10 -0
  39. package/dist/core/DrawerNavigationGroup.d.ts +45 -0
  40. package/dist/core/index.d.ts +1 -0
  41. package/dist/editor/components/SlashCommandMenu.d.ts +6 -0
  42. package/dist/editor/components/editor-bubble-item.d.ts +8 -0
  43. package/dist/editor/components/editor-bubble.d.ts +8 -0
  44. package/dist/editor/components/image-bubble.d.ts +5 -0
  45. package/dist/editor/components/index.d.ts +16 -0
  46. package/dist/editor/components/table-bubble.d.ts +5 -0
  47. package/dist/editor/editor.d.ts +30 -0
  48. package/dist/editor/extensions/HighlightDecorationExtension.d.ts +24 -0
  49. package/dist/editor/extensions/Image/index.d.ts +6 -0
  50. package/dist/editor/extensions/Image.d.ts +6 -0
  51. package/dist/editor/extensions/TextLoadingDecorationExtension.d.ts +16 -0
  52. package/dist/editor/extensions/clipboard.d.ts +7 -0
  53. package/dist/editor/extensions/custom-keymap.d.ts +1 -0
  54. package/dist/editor/extensions/drag-and-drop.d.ts +9 -0
  55. package/dist/editor/hooks/useProseMirror.d.ts +13 -0
  56. package/dist/editor/hooks/useProseMirrorContext.d.ts +9 -0
  57. package/dist/editor/index.d.ts +2 -0
  58. package/dist/editor/markdown.d.ts +5 -0
  59. package/dist/editor/nodeViews/ImageComponent.d.ts +3 -0
  60. package/dist/editor/nodeViews/ReactNodeView.d.ts +29 -0
  61. package/dist/editor/nodeViews/TaskItemComponent.d.ts +3 -0
  62. package/dist/editor/nodeViews/index.d.ts +6 -0
  63. package/dist/editor/plugins/index.d.ts +2 -0
  64. package/dist/editor/plugins/inputrules.d.ts +6 -0
  65. package/dist/editor/plugins/placeholderPlugin.d.ts +3 -0
  66. package/dist/editor/plugins/slashCommandPlugin.d.ts +12 -0
  67. package/dist/editor/schema.d.ts +2 -0
  68. package/dist/editor/selectors/ai-selector.d.ts +0 -0
  69. package/dist/editor/selectors/color-selector.d.ts +10 -0
  70. package/dist/editor/selectors/link-selector.d.ts +8 -0
  71. package/dist/editor/selectors/node-selector.d.ts +15 -0
  72. package/dist/editor/selectors/text-buttons.d.ts +1 -0
  73. package/dist/editor/types.d.ts +5 -0
  74. package/dist/editor/useProseMirror.d.ts +16 -0
  75. package/dist/editor/utils/prosemirror-utils.d.ts +6 -0
  76. package/dist/editor/utils/remove_classes.d.ts +1 -0
  77. package/dist/editor/utils/useDebouncedCallback.d.ts +1 -0
  78. package/dist/form/components/ErrorFocus.d.ts +1 -1
  79. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  80. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +1 -1
  81. package/dist/form/validation.d.ts +3 -2
  82. package/dist/hooks/index.d.ts +1 -0
  83. package/dist/hooks/useBreadcrumbsController.d.ts +16 -0
  84. package/dist/hooks/useBuildNavigationController.d.ts +0 -1
  85. package/dist/hooks/useCollapsedGroups.d.ts +6 -3
  86. package/dist/hooks/useTranslation.d.ts +17 -0
  87. package/dist/i18n/FireCMSi18nProvider.d.ts +33 -0
  88. package/dist/index.d.ts +5 -0
  89. package/dist/index.es.js +30146 -15178
  90. package/dist/index.es.js.map +1 -1
  91. package/dist/index.umd.js +30032 -15085
  92. package/dist/index.umd.js.map +1 -1
  93. package/dist/internal/useRestoreScroll.d.ts +1 -1
  94. package/dist/locales/de.d.ts +2 -0
  95. package/dist/locales/en.d.ts +10 -0
  96. package/dist/locales/es.d.ts +10 -0
  97. package/dist/locales/fr.d.ts +2 -0
  98. package/dist/locales/hi.d.ts +2 -0
  99. package/dist/locales/it.d.ts +2 -0
  100. package/dist/locales/pt.d.ts +7 -0
  101. package/dist/preview/PropertyPreviewProps.d.ts +5 -0
  102. package/dist/preview/components/DatePreview.d.ts +13 -3
  103. package/dist/preview/components/ImagePreview.d.ts +5 -1
  104. package/dist/preview/components/StorageThumbnail.d.ts +2 -1
  105. package/dist/preview/components/UrlComponentPreview.d.ts +2 -1
  106. package/dist/preview/property_previews/ArrayOfStorageComponentsPreview.d.ts +1 -1
  107. package/dist/preview/property_previews/ArrayOfStringsPreview.d.ts +1 -1
  108. package/dist/preview/property_previews/SkeletonPropertyComponent.d.ts +1 -1
  109. package/dist/types/analytics.d.ts +1 -1
  110. package/dist/types/collections.d.ts +88 -2
  111. package/dist/types/customization_controller.d.ts +2 -1
  112. package/dist/types/datasource.d.ts +0 -1
  113. package/dist/types/firecms.d.ts +2 -1
  114. package/dist/types/index.d.ts +1 -0
  115. package/dist/types/navigation.d.ts +2 -2
  116. package/dist/types/plugins.d.ts +69 -1
  117. package/dist/types/properties.d.ts +268 -12
  118. package/dist/types/storage.d.ts +1 -0
  119. package/dist/types/translations.d.ts +669 -0
  120. package/dist/util/__tests__/conditions.test.d.ts +1 -0
  121. package/dist/util/__tests__/objects.test.d.ts +1 -0
  122. package/dist/util/conditions.d.ts +26 -0
  123. package/dist/util/entities.d.ts +2 -3
  124. package/dist/util/index.d.ts +3 -1
  125. package/dist/util/lazy_eager.d.ts +7 -0
  126. package/dist/util/objects.d.ts +1 -0
  127. package/dist/util/property_utils.d.ts +2 -1
  128. package/dist/util/resolutions.d.ts +3 -3
  129. package/dist/util/useStorageUploadController.d.ts +10 -1
  130. package/package.json +51 -12
  131. package/src/app/Scaffold.tsx +20 -19
  132. package/src/components/AIIcon.tsx +41 -0
  133. package/src/components/ArrayContainer.tsx +7 -8
  134. package/src/components/ClearFilterSortButton.tsx +25 -19
  135. package/src/components/ConfirmationDialog.tsx +4 -4
  136. package/src/components/DeleteEntityDialog.tsx +12 -11
  137. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +82 -43
  138. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +130 -79
  139. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +121 -104
  140. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +132 -103
  141. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +6 -3
  142. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +24 -44
  143. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +90 -49
  144. package/src/components/EntityCollectionTable/internal/EntityTableCellActions.tsx +1 -1
  145. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +3 -2
  146. package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +11 -11
  147. package/src/components/EntityCollectionView/Board.tsx +324 -0
  148. package/src/components/EntityCollectionView/BoardColumn.tsx +158 -0
  149. package/src/components/EntityCollectionView/BoardColumnTitle.tsx +45 -0
  150. package/src/components/EntityCollectionView/BoardSortableList.tsx +174 -0
  151. package/src/components/EntityCollectionView/CollectionDataErrorBanner.tsx +43 -0
  152. package/src/components/EntityCollectionView/EntityBoardCard.tsx +212 -0
  153. package/src/components/EntityCollectionView/EntityCard.tsx +235 -0
  154. package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +706 -0
  155. package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +236 -0
  156. package/src/components/EntityCollectionView/EntityCollectionView.tsx +531 -209
  157. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +35 -22
  158. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +86 -15
  159. package/src/components/EntityCollectionView/FiltersDialog.tsx +252 -0
  160. package/src/components/EntityCollectionView/ViewModeToggle.tsx +202 -0
  161. package/src/components/EntityCollectionView/board_types.ts +113 -0
  162. package/src/components/EntityCollectionView/useBoardDataController.tsx +490 -0
  163. package/src/components/EntityJsonPreview.tsx +2 -1
  164. package/src/components/EntityView.tsx +3 -2
  165. package/src/components/ErrorBoundary.tsx +27 -15
  166. package/src/components/ErrorTooltip.tsx +2 -1
  167. package/src/components/HomePage/DefaultHomePage.tsx +65 -22
  168. package/src/components/HomePage/HomePageDnD.tsx +59 -42
  169. package/src/components/HomePage/NavigationCard.tsx +20 -18
  170. package/src/components/HomePage/NavigationGroup.tsx +20 -17
  171. package/src/components/HomePage/RenameGroupDialog.tsx +15 -15
  172. package/src/components/HomePage/SmallNavigationCard.tsx +10 -9
  173. package/src/components/LanguageToggle.tsx +66 -0
  174. package/src/components/NotFoundPage.tsx +5 -3
  175. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +12 -17
  176. package/src/components/ReferenceWidget.tsx +5 -6
  177. package/src/components/SearchIconsView.tsx +3 -1
  178. package/src/components/SelectableTable/SelectableTable.tsx +75 -67
  179. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +7 -6
  180. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +50 -40
  181. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +53 -40
  182. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +60 -58
  183. package/src/components/UnsavedChangesDialog.tsx +6 -6
  184. package/src/components/UserDisplay.tsx +4 -4
  185. package/src/components/VirtualTable/VirtualTable.performance.test.tsx +1 -0
  186. package/src/components/VirtualTable/VirtualTable.tsx +275 -119
  187. package/src/components/VirtualTable/VirtualTableCell.tsx +18 -2
  188. package/src/components/VirtualTable/VirtualTableHeader.tsx +76 -64
  189. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +163 -42
  190. package/src/components/VirtualTable/VirtualTableProps.tsx +21 -2
  191. package/src/components/VirtualTable/VirtualTableRow.tsx +1 -1
  192. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +3 -0
  193. package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +19 -6
  194. package/src/components/VirtualTable/types.tsx +3 -0
  195. package/src/components/common/default_entity_actions.tsx +4 -0
  196. package/src/components/common/useColumnsIds.tsx +95 -3
  197. package/src/components/common/useDataSourceTableController.tsx +12 -4
  198. package/src/components/index.tsx +5 -0
  199. package/src/contexts/BreacrumbsContext.tsx +15 -8
  200. package/src/contexts/index.ts +10 -0
  201. package/src/core/DefaultAppBar.tsx +49 -32
  202. package/src/core/DefaultDrawer.tsx +49 -57
  203. package/src/core/DrawerNavigationGroup.tsx +120 -0
  204. package/src/core/DrawerNavigationItem.tsx +4 -3
  205. package/src/core/EntityEditView.tsx +94 -50
  206. package/src/core/EntityEditViewFormActions.tsx +24 -17
  207. package/src/core/EntitySidePanel.tsx +34 -30
  208. package/src/core/FireCMS.tsx +33 -6
  209. package/src/core/SideDialogs.tsx +4 -2
  210. package/src/core/field_configs.tsx +18 -11
  211. package/src/core/index.tsx +1 -0
  212. package/src/editor/components/SlashCommandMenu.tsx +516 -0
  213. package/src/editor/components/editor-bubble-item.tsx +32 -0
  214. package/src/editor/components/editor-bubble.tsx +118 -0
  215. package/src/editor/components/image-bubble.tsx +156 -0
  216. package/src/editor/components/index.ts +14 -0
  217. package/src/editor/components/table-bubble.tsx +165 -0
  218. package/src/editor/editor.tsx +455 -0
  219. package/src/editor/extensions/HighlightDecorationExtension.ts +114 -0
  220. package/src/editor/extensions/Image/index.ts +133 -0
  221. package/src/editor/extensions/Image.ts +159 -0
  222. package/src/editor/extensions/TextLoadingDecorationExtension.tsx +107 -0
  223. package/src/editor/extensions/clipboard.ts +72 -0
  224. package/src/editor/extensions/custom-keymap.ts +24 -0
  225. package/src/editor/extensions/drag-and-drop.tsx +480 -0
  226. package/src/editor/hooks/useProseMirror.ts +124 -0
  227. package/src/editor/hooks/useProseMirrorContext.ts +15 -0
  228. package/src/editor/index.ts +2 -0
  229. package/src/editor/markdown.ts +172 -0
  230. package/src/editor/nodeViews/ImageComponent.tsx +20 -0
  231. package/src/editor/nodeViews/ReactNodeView.tsx +89 -0
  232. package/src/editor/nodeViews/TaskItemComponent.tsx +29 -0
  233. package/src/editor/nodeViews/index.ts +35 -0
  234. package/src/editor/plugins/index.ts +58 -0
  235. package/src/editor/plugins/inputrules.ts +82 -0
  236. package/src/editor/plugins/placeholderPlugin.ts +55 -0
  237. package/src/editor/plugins/slashCommandPlugin.ts +61 -0
  238. package/src/editor/schema.ts +240 -0
  239. package/src/editor/selectors/ai-selector.tsx +111 -0
  240. package/src/editor/selectors/color-selector.tsx +200 -0
  241. package/src/editor/selectors/link-selector.tsx +118 -0
  242. package/src/editor/selectors/node-selector.tsx +157 -0
  243. package/src/editor/selectors/text-buttons.tsx +86 -0
  244. package/src/editor/types.ts +6 -0
  245. package/src/editor/useProseMirror.ts +126 -0
  246. package/src/editor/utils/prosemirror-utils.ts +108 -0
  247. package/src/editor/utils/remove_classes.ts +17 -0
  248. package/src/editor/utils/useDebouncedCallback.ts +25 -0
  249. package/src/form/EntityForm.tsx +149 -67
  250. package/src/form/EntityFormActions.tsx +19 -12
  251. package/src/form/PropertyFieldBinding.tsx +68 -51
  252. package/src/form/components/ErrorFocus.tsx +3 -3
  253. package/src/form/components/LocalChangesMenu.tsx +13 -13
  254. package/src/form/components/StorageItemPreview.tsx +5 -3
  255. package/src/form/components/StorageUploadProgress.tsx +18 -3
  256. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +18 -5
  257. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +22 -10
  258. package/src/form/field_bindings/BlockFieldBinding.tsx +26 -9
  259. package/src/form/field_bindings/DateTimeFieldBinding.tsx +18 -17
  260. package/src/form/field_bindings/KeyValueFieldBinding.tsx +46 -25
  261. package/src/form/field_bindings/MapFieldBinding.tsx +88 -70
  262. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +93 -52
  263. package/src/form/field_bindings/MultiSelectFieldBinding.tsx +15 -1
  264. package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +25 -11
  265. package/src/form/field_bindings/ReferenceFieldBinding.tsx +25 -11
  266. package/src/form/field_bindings/RepeatFieldBinding.tsx +21 -6
  267. package/src/form/field_bindings/SelectFieldBinding.tsx +7 -5
  268. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +110 -92
  269. package/src/form/field_bindings/SwitchFieldBinding.tsx +31 -14
  270. package/src/form/field_bindings/TextFieldBinding.tsx +77 -38
  271. package/src/form/field_bindings/UserSelectFieldBinding.tsx +7 -5
  272. package/src/form/validation.ts +245 -160
  273. package/src/hooks/index.tsx +1 -0
  274. package/src/hooks/useBreadcrumbsController.tsx +18 -0
  275. package/src/hooks/useBuildNavigationController.tsx +91 -41
  276. package/src/hooks/useCollapsedGroups.ts +18 -9
  277. package/src/hooks/useTranslation.ts +31 -0
  278. package/src/hooks/useValidateAuthenticator.tsx +1 -1
  279. package/src/i18n/FireCMSi18nProvider.tsx +160 -0
  280. package/src/index.ts +5 -0
  281. package/src/internal/useBuildDataSource.ts +68 -34
  282. package/src/internal/useBuildSideDialogsController.tsx +11 -8
  283. package/src/internal/useBuildSideEntityController.tsx +24 -24
  284. package/src/internal/useRestoreScroll.tsx +26 -14
  285. package/src/locales/de.ts +718 -0
  286. package/src/locales/en.ts +730 -0
  287. package/src/locales/es.ts +730 -0
  288. package/src/locales/fr.ts +718 -0
  289. package/src/locales/hi.ts +718 -0
  290. package/src/locales/it.ts +718 -0
  291. package/src/locales/pt.ts +727 -0
  292. package/src/preview/PropertyPreview.tsx +43 -33
  293. package/src/preview/PropertyPreviewProps.tsx +6 -0
  294. package/src/preview/components/DatePreview.tsx +72 -4
  295. package/src/preview/components/EmptyValue.tsx +1 -1
  296. package/src/preview/components/ImagePreview.tsx +37 -21
  297. package/src/preview/components/ReferencePreview.tsx +2 -1
  298. package/src/preview/components/StorageThumbnail.tsx +16 -12
  299. package/src/preview/components/UrlComponentPreview.tsx +32 -27
  300. package/src/preview/components/UserPreview.tsx +3 -1
  301. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +9 -7
  302. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +11 -9
  303. package/src/preview/property_previews/ArrayPropertyPreview.tsx +26 -24
  304. package/src/preview/property_previews/MapPropertyPreview.tsx +49 -27
  305. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +61 -56
  306. package/src/routes/CustomCMSRoute.tsx +1 -0
  307. package/src/routes/FireCMSRoute.tsx +87 -65
  308. package/src/types/analytics.ts +10 -0
  309. package/src/types/collections.ts +97 -3
  310. package/src/types/customization_controller.tsx +2 -1
  311. package/src/types/datasource.ts +54 -56
  312. package/src/types/firecms.tsx +2 -1
  313. package/src/types/index.ts +1 -0
  314. package/src/types/navigation.ts +2 -2
  315. package/src/types/plugins.tsx +77 -1
  316. package/src/types/properties.ts +359 -37
  317. package/src/types/storage.ts +2 -1
  318. package/src/types/translations.ts +752 -0
  319. package/src/util/__tests__/conditions.test.ts +506 -0
  320. package/src/util/__tests__/objects.test.ts +196 -0
  321. package/src/util/callbacks.ts +6 -3
  322. package/src/util/collections.ts +51 -6
  323. package/src/util/conditions.ts +339 -0
  324. package/src/util/entities.ts +29 -30
  325. package/src/util/entity_cache.ts +2 -1
  326. package/src/util/index.ts +3 -1
  327. package/src/util/join_collections.ts +10 -8
  328. package/src/util/lazy_eager.tsx +33 -0
  329. package/src/util/objects.ts +46 -13
  330. package/src/util/{references.ts → previews.ts} +16 -2
  331. package/src/util/property_utils.tsx +37 -11
  332. package/src/util/resolutions.ts +62 -58
  333. package/src/util/useStorageUploadController.tsx +23 -29
  334. /package/dist/util/{references.d.ts → previews.d.ts} +0 -0
@@ -30,15 +30,20 @@ import { NavigationCardBinding } from "./NavigationCardBinding";
30
30
  import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable";
31
31
  import { restrictToVerticalAxis, restrictToWindowEdges } from "@dnd-kit/modifiers";
32
32
  import { RenameGroupDialog } from "./RenameGroupDialog";
33
+ import { useTranslation } from "../../hooks/useTranslation";
33
34
 
34
- export const DEFAULT_GROUP_NAME = "Views";
35
+ /**
36
+ * Internal sentinel key for ungrouped navigation entries.
37
+ * Not displayed — display components use t("views_group") when group is undefined.
38
+ */
39
+ const DEFAULT_GROUP_KEY = "__default__";
35
40
  export const ADMIN_GROUP_NAME = "Admin";
36
41
 
37
42
  export function DefaultHomePage({
38
- additionalActions,
39
- additionalChildrenStart,
40
- additionalChildrenEnd
41
- }: {
43
+ additionalActions,
44
+ additionalChildrenStart,
45
+ additionalChildrenEnd
46
+ }: {
42
47
  additionalActions?: React.ReactNode;
43
48
  additionalChildrenStart?: React.ReactNode;
44
49
  additionalChildrenEnd?: React.ReactNode;
@@ -47,6 +52,7 @@ export function DefaultHomePage({
47
52
  const context = useFireCMSContext();
48
53
  const customizationController = useCustomizationController();
49
54
  const navigationController = useNavigationController();
55
+ const { t } = useTranslation();
50
56
 
51
57
  if (!navigationController.topLevelNavigation)
52
58
  throw Error("Navigation not ready");
@@ -95,6 +101,10 @@ export function DefaultHomePage({
95
101
  entries: NavigationEntry[];
96
102
  } | null>(null);
97
103
 
104
+ // Flag to prevent useEffect from overwriting local DnD state
105
+ const isDndDirtyRef = useRef(false);
106
+ const dndDirtyTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
107
+
98
108
  // Memoize the processed groups to avoid unnecessary recalculations
99
109
  const processedGroups = useMemo(() => {
100
110
  const src = filteredNavigationEntries;
@@ -104,7 +114,7 @@ export function DefaultHomePage({
104
114
  const g =
105
115
  e.type === "admin"
106
116
  ? ADMIN_GROUP_NAME
107
- : e.group ?? DEFAULT_GROUP_NAME;
117
+ : e.group ?? DEFAULT_GROUP_KEY;
108
118
  (entriesByGroup[g] ??= []).push(e);
109
119
  });
110
120
 
@@ -115,7 +125,7 @@ export function DefaultHomePage({
115
125
 
116
126
  if (performingSearch) {
117
127
  const ordered = [
118
- ...new Set(src.map((e) => e.group ?? DEFAULT_GROUP_NAME))
128
+ ...new Set(src.map((e) => e.group ?? DEFAULT_GROUP_KEY))
119
129
  ];
120
130
  allProcessed = ordered
121
131
  .map((name) => ({
@@ -125,11 +135,11 @@ export function DefaultHomePage({
125
135
  .filter((g) => g.entries.length);
126
136
  } else {
127
137
  allProcessed = groupOrderFromNavController.map((g) => ({
128
- name: g,
129
- entries: entriesByGroup[g] || []
138
+ name: g ?? DEFAULT_GROUP_KEY,
139
+ entries: entriesByGroup[g ?? DEFAULT_GROUP_KEY] || []
130
140
  }));
131
141
  Object.keys(entriesByGroup).forEach((g) => {
132
- if (!groupOrderFromNavController.includes(g))
142
+ if (!groupOrderFromNavController.map(x => x ?? DEFAULT_GROUP_KEY).includes(g))
133
143
  allProcessed.push({
134
144
  name: g,
135
145
  entries: entriesByGroup[g]
@@ -137,9 +147,9 @@ export function DefaultHomePage({
137
147
  });
138
148
 
139
149
  // Ensure default group exists if there are plugin additional cards but no collections
140
- if (hasPluginAdditionalCards && !allProcessed.some(g => g.name === DEFAULT_GROUP_NAME)) {
150
+ if (hasPluginAdditionalCards && !allProcessed.some(g => g.name === DEFAULT_GROUP_KEY)) {
141
151
  allProcessed.push({
142
- name: DEFAULT_GROUP_NAME,
152
+ name: DEFAULT_GROUP_KEY,
143
153
  entries: []
144
154
  });
145
155
  }
@@ -147,7 +157,7 @@ export function DefaultHomePage({
147
157
  allProcessed = allProcessed.filter(
148
158
  (g) =>
149
159
  g.entries.length ||
150
- (g.name === DEFAULT_GROUP_NAME && hasPluginAdditionalCards)
160
+ (g.name === DEFAULT_GROUP_KEY && hasPluginAdditionalCards)
151
161
  );
152
162
  }
153
163
 
@@ -159,7 +169,12 @@ export function DefaultHomePage({
159
169
  }, [filteredNavigationEntries, performingSearch, groupOrderFromNavController, customizationController.plugins]);
160
170
 
161
171
  // Update state only when processedGroups actually changes
172
+ // Skip update if DnD just made a local change (dirty flag is set)
162
173
  useEffect(() => {
174
+ if (isDndDirtyRef.current) {
175
+ // DnD just updated the state, skip this sync
176
+ return;
177
+ }
163
178
  setAdminGroupData(processedGroups.adminGroupData);
164
179
  setItems(processedGroups.items);
165
180
  }, [processedGroups]);
@@ -171,8 +186,8 @@ export function DefaultHomePage({
171
186
  updater:
172
187
  | { name: string; entries: NavigationEntry[] }[]
173
188
  | ((
174
- prev: { name: string; entries: NavigationEntry[] }[]
175
- ) => { name: string; entries: NavigationEntry[] }[])
189
+ prev: { name: string; entries: NavigationEntry[] }[]
190
+ ) => { name: string; entries: NavigationEntry[] }[])
176
191
  ) => {
177
192
  setItems(updater); // local only
178
193
  };
@@ -180,6 +195,19 @@ export function DefaultHomePage({
180
195
  const persistNavigationGroups = (
181
196
  latest: { name: string; entries: NavigationEntry[] }[]
182
197
  ) => {
198
+ // Set dirty flag to prevent useEffect from overwriting local state
199
+ isDndDirtyRef.current = true;
200
+
201
+ // Clear any existing timeout
202
+ if (dndDirtyTimeoutRef.current) {
203
+ clearTimeout(dndDirtyTimeoutRef.current);
204
+ }
205
+
206
+ // Clear dirty flag after a delay to allow navigation to update
207
+ dndDirtyTimeoutRef.current = setTimeout(() => {
208
+ isDndDirtyRef.current = false;
209
+ }, 1000);
210
+
183
211
  // Map ALL groups including "Views"
184
212
  const draggable: NavigationGroupMapping[] = latest.map((g) => ({
185
213
  name: g.name,
@@ -205,7 +233,7 @@ export function DefaultHomePage({
205
233
  ...(adminGroupData ? [adminGroupData.name] : [])
206
234
  ], [items, adminGroupData]);
207
235
 
208
- const { isGroupCollapsed, toggleGroupCollapsed } = useCollapsedGroups(groupNames);
236
+ const { isGroupCollapsed, toggleGroupCollapsed } = useCollapsedGroups(groupNames, "home");
209
237
 
210
238
 
211
239
  const {
@@ -264,6 +292,7 @@ export function DefaultHomePage({
264
292
  let additionalPluginChildrenStart: React.ReactNode | undefined;
265
293
  let additionalPluginChildrenEnd: React.ReactNode | undefined;
266
294
  let additionalPluginSections: React.ReactNode | undefined;
295
+ let additionalPluginActions: React.ReactNode | undefined;
267
296
 
268
297
  if (customizationController.plugins) {
269
298
  const sectionProps: PluginGenericProps = { context };
@@ -311,6 +340,19 @@ export function DefaultHomePage({
311
340
  ))}
312
341
  </div>
313
342
  );
343
+
344
+ // Collect additionalActions from plugins
345
+ additionalPluginActions = (
346
+ <>
347
+ {customizationController.plugins
348
+ .filter((p) => p.homePage?.additionalActions)
349
+ .map((plugin, i) => (
350
+ <React.Fragment key={`plugin_actions_${i}`}>
351
+ {plugin.homePage!.additionalActions}
352
+ </React.Fragment>
353
+ ))}
354
+ </>
355
+ );
314
356
  }
315
357
 
316
358
  /* ───────────────────────────────────────────────────────────────
@@ -326,15 +368,16 @@ export function DefaultHomePage({
326
368
  >
327
369
  <SearchBar
328
370
  onTextSearch={updateSearch}
329
- placeholder="Search collections"
371
+ placeholder={t("search_collections")}
330
372
  autoFocus
331
373
  innerClassName="w-full"
332
374
  className="w-full flex-grow"
333
375
  />
334
376
  {additionalActions}
377
+ {additionalPluginActions}
335
378
  </div>
336
379
 
337
- <FavouritesView hidden={performingSearch}/>
380
+ <FavouritesView hidden={performingSearch} />
338
381
 
339
382
  {additionalChildrenStart}
340
383
  {additionalPluginChildrenStart}
@@ -375,7 +418,7 @@ export function DefaultHomePage({
375
418
 
376
419
  const actionProps: PluginHomePageAdditionalCardsProps = {
377
420
  group:
378
- groupKey === DEFAULT_GROUP_NAME
421
+ groupKey === DEFAULT_GROUP_KEY
379
422
  ? undefined
380
423
  : groupKey,
381
424
  context
@@ -395,7 +438,7 @@ export function DefaultHomePage({
395
438
  >
396
439
  <NavigationGroup
397
440
  group={
398
- groupKey === DEFAULT_GROUP_NAME
441
+ groupKey === DEFAULT_GROUP_KEY
399
442
  ? undefined
400
443
  : groupKey
401
444
  }
@@ -487,7 +530,7 @@ export function DefaultHomePage({
487
530
 
488
531
  <DragOverlay adjustScale={false} dropAnimation={dropAnimation}>
489
532
  {activeGroupData &&
490
- draggingGroupId === activeGroupData.name ? (
533
+ draggingGroupId === activeGroupData.name ? (
491
534
  <div
492
535
  className="rounded-lg bg-transparent"
493
536
  style={{
@@ -498,7 +541,7 @@ export function DefaultHomePage({
498
541
  <NavigationGroup
499
542
  group={
500
543
  activeGroupData.name ===
501
- DEFAULT_GROUP_NAME
544
+ DEFAULT_GROUP_KEY
502
545
  ? undefined
503
546
  : activeGroupData.name
504
547
  }
@@ -29,7 +29,8 @@ import { CSS } from "@dnd-kit/utilities";
29
29
 
30
30
  import { NavigationCardBinding } from "./NavigationCardBinding";
31
31
  import { NavigationEntry } from "../../types";
32
- import { cls } from "@firecms/ui";
32
+ import { cls, defaultBorderMixin } from "@firecms/ui";
33
+ import { useTranslation } from "../../hooks";
33
34
 
34
35
  const animateLayoutChanges: AnimateLayoutChanges = (args) =>
35
36
  defaultAnimateLayoutChanges({
@@ -65,9 +66,9 @@ const cloneItemsForDnd = (items: { name: string; entries: NavigationEntry[] }[])
65
66
 
66
67
  /* ─────────────────────────────────────────────────────────── */
67
68
  export function SortableNavigationCard({
68
- entry,
69
- onClick
70
- }: {
69
+ entry,
70
+ onClick
71
+ }: {
71
72
  entry: NavigationEntry;
72
73
  onClick?: () => void;
73
74
  }) {
@@ -92,17 +93,17 @@ export function SortableNavigationCard({
92
93
 
93
94
  return (
94
95
  <div ref={setNodeRef} style={style} {...attributes} {...listeners}>
95
- <NavigationCardBinding {...entry} onClick={onClick}/>
96
+ <NavigationCardBinding {...entry} onClick={onClick} />
96
97
  </div>
97
98
  );
98
99
  }
99
100
 
100
101
  export function NavigationGroupDroppable({
101
- id,
102
- itemIds,
103
- children,
104
- isPotentialCardDropTarget = false
105
- }: {
102
+ id,
103
+ itemIds,
104
+ children,
105
+ isPotentialCardDropTarget = false
106
+ }: {
106
107
  id: UniqueIdentifier;
107
108
  itemIds: UniqueIdentifier[];
108
109
  children: React.ReactNode;
@@ -128,10 +129,10 @@ export function NavigationGroupDroppable({
128
129
  }
129
130
 
130
131
  export function SortableNavigationGroup({
131
- groupName,
132
- children,
133
- disabled
134
- }: {
132
+ groupName,
133
+ children,
134
+ disabled
135
+ }: {
135
136
  groupName: string;
136
137
  children: React.ReactNode;
137
138
  disabled?: boolean;
@@ -164,21 +165,21 @@ export function SortableNavigationGroup({
164
165
  }
165
166
 
166
167
  export function useHomePageDnd({
167
- items,
168
- setItems,
169
- disabled,
170
- onCardMovedBetweenGroups,
171
- onGroupMoved,
172
- onNewGroupDrop,
173
- onPersist
174
- }: {
168
+ items,
169
+ setItems,
170
+ disabled,
171
+ onCardMovedBetweenGroups,
172
+ onGroupMoved,
173
+ onNewGroupDrop,
174
+ onPersist
175
+ }: {
175
176
  items: { name: string; entries: NavigationEntry[] }[];
176
177
  setItems: (
177
178
  newItemsOrUpdater:
178
179
  | { name: string; entries: NavigationEntry[] }[]
179
180
  | ((
180
- currentItems: { name: string; entries: NavigationEntry[] }[]
181
- ) => { name: string; entries: NavigationEntry[] }[])
181
+ currentItems: { name: string; entries: NavigationEntry[] }[]
182
+ ) => { name: string; entries: NavigationEntry[] }[])
182
183
  ) => void;
183
184
  disabled: boolean;
184
185
  onCardMovedBetweenGroups?: (card: NavigationEntry) => void;
@@ -344,9 +345,9 @@ export function useHomePageDnd({
344
345
  };
345
346
 
346
347
  const handleDragOver = ({
347
- active,
348
- over
349
- }: { active: Active; over: any }) => {
348
+ active,
349
+ over
350
+ }: { active: Active; over: any }) => {
350
351
  if (disabled || !over) return;
351
352
 
352
353
  const activeIdNow = active.id;
@@ -395,9 +396,9 @@ export function useHomePageDnd({
395
396
  };
396
397
 
397
398
  const handleDragEnd = ({
398
- active,
399
- over
400
- }: { active: Active; over: any }) => {
399
+ active,
400
+ over
401
+ }: { active: Active; over: any }) => {
401
402
  if (disabled || !over) {
402
403
  resetDragState();
403
404
  return;
@@ -505,10 +506,10 @@ export function useHomePageDnd({
505
506
  }
506
507
  }
507
508
  } else if (overCont && activeCont !== overCont) {
508
- // Card moved between different groups - use CLEAN pre-drag state
509
+ // Card moved between different groups - use CLEAN pre-drag state for the MOVE
510
+ // but use current dndItems for the POSITION
509
511
  const finalState = cloneItemsForDnd(sourceState);
510
512
 
511
- // Find target container from clean state too
512
513
  const finalOverId = lastOverId.current || overIdNow;
513
514
  const cleanOverCont = findContainerInState(finalOverId as string, sourceState) || overCont;
514
515
 
@@ -524,17 +525,32 @@ export function useHomePageDnd({
524
525
  // Remove from source
525
526
  const [moved] = src.entries.splice(idxInSrc, 1);
526
527
 
527
- // Calculate insertion position in target
528
+ // Calculate insertion position using CURRENT dndItems (visual state after handleDragOver)
529
+ // because that's where the user visually dropped it
530
+ const currentTgt = dndItems.find((g) => g.name === cleanOverCont);
528
531
  const overIsContainer = finalOverId === cleanOverCont;
532
+
529
533
  if (overIsContainer) {
530
534
  tgt.entries.push(moved);
531
- } else {
532
- const overIdx = tgt.entries.findIndex((e) => e.url === finalOverId);
533
- if (overIdx !== -1) {
534
- tgt.entries.splice(overIdx, 0, moved);
535
+ } else if (currentTgt) {
536
+ // Find position in current visual state
537
+ const currentOverIdx = currentTgt.entries.findIndex((e) => e.url === finalOverId);
538
+ if (currentOverIdx !== -1) {
539
+ // Find the card at that position in currentTgt, get its URL
540
+ // Then find that same card in tgt (pre-drag state) and insert before it
541
+ const cardAtPosition = currentTgt.entries[currentOverIdx];
542
+ const tgtOverIdx = tgt.entries.findIndex((e) => e.url === cardAtPosition.url);
543
+ if (tgtOverIdx !== -1) {
544
+ tgt.entries.splice(tgtOverIdx, 0, moved);
545
+ } else {
546
+ // Card might have been the dragged one in current state, just push
547
+ tgt.entries.push(moved);
548
+ }
535
549
  } else {
536
550
  tgt.entries.push(moved);
537
551
  }
552
+ } else {
553
+ tgt.entries.push(moved);
538
554
  }
539
555
 
540
556
  // Remove empty source group if needed
@@ -648,12 +664,13 @@ export function useHomePageDnd({
648
664
  }
649
665
 
650
666
  export function NewGroupDropZone({
651
- disabled,
652
- setIsHovering
653
- }: {
667
+ disabled,
668
+ setIsHovering
669
+ }: {
654
670
  disabled: boolean;
655
671
  setIsHovering: (v: boolean) => void;
656
672
  }) {
673
+ const { t } = useTranslation();
657
674
  const {
658
675
  setNodeRef,
659
676
  isOver
@@ -690,11 +707,11 @@ export function NewGroupDropZone({
690
707
  "fixed right-8 top-1/2 -translate-y-1/2 w-[200px] h-[120px] border border-dashed rounded-lg flex items-center justify-center transition-all",
691
708
  isOver
692
709
  ? "bg-surface-accent-100 dark:bg-surface-accent-800 border-surface-300 dark:border-surface-600"
693
- : "bg-surface-50 dark:bg-surface-900 border-surface-200 dark:border-surface-700"
710
+ : "bg-surface-50 dark:bg-surface-900 " + defaultBorderMixin
694
711
  )}>
695
712
  <div className="text-center p-4">
696
713
  <span className="block font-medium text-sm">
697
- Drop here to create a new group
714
+ {t("drop_here_create_group")}
698
715
  </span>
699
716
  </div>
700
717
  </div>
@@ -12,18 +12,19 @@ export type NavigationCardProps = {
12
12
 
13
13
  // Wrap the component with React.memo
14
14
  export const NavigationCard = React.memo(function NavigationCard({
15
- name,
16
- description,
17
- icon,
18
- actions,
19
- onClick,
20
- shrink
21
- }: NavigationCardProps) {
15
+ name,
16
+ description,
17
+ icon,
18
+ actions,
19
+ onClick,
20
+ shrink
21
+ }: NavigationCardProps) {
22
22
 
23
23
  return (
24
24
  <Card
25
25
  className={cls(
26
- "h-full p-4 cursor-pointer min-h-[230px] transition-all duration-200 ease-in-out",
26
+ "h-full px-4 py-3 cursor-pointer min-h-[160px] transition-all duration-200 ease-in-out",
27
+ "hover:-translate-y-0.5 hover:shadow-md hover:shadow-primary/5",
27
28
  shrink && "w-full max-w-full min-h-0 scale-75"
28
29
  )}
29
30
  onClick={() => {
@@ -36,12 +37,12 @@ export const NavigationCard = React.memo(function NavigationCard({
36
37
  className="flex-grow w-full">
37
38
 
38
39
  <div
39
- className="h-10 flex items-center w-full justify-between text-surface-300 dark:text-surface-600">
40
+ className="h-8 flex items-center w-full justify-between text-surface-300 dark:text-surface-600">
40
41
 
41
42
  {icon}
42
43
 
43
44
  <div
44
- className="flex items-center gap-1"
45
+ className="flex items-center gap-0.5"
45
46
  onClick={(event: React.MouseEvent) => {
46
47
  event.preventDefault();
47
48
  event.stopPropagation();
@@ -53,22 +54,23 @@ export const NavigationCard = React.memo(function NavigationCard({
53
54
 
54
55
  </div>
55
56
 
56
- <Typography gutterBottom variant="h5"
57
- component="h2">
57
+ <Typography gutterBottom variant="subtitle1"
58
+ className="font-medium mt-1"
59
+ component="h2">
58
60
  {name}
59
61
  </Typography>
60
62
 
61
- {description && <Typography variant="body2"
62
- color="secondary"
63
- component="div">
64
- <Markdown source={description} size={"small"}/>
63
+ {description && <Typography variant="caption"
64
+ color="secondary"
65
+ component="div">
66
+ <Markdown source={description} size={"small"} />
65
67
  </Typography>}
66
68
  </div>
67
69
 
68
70
  <div style={{ alignSelf: "flex-end" }}>
69
71
 
70
- <div className={"p-4"}>
71
- <ArrowForwardIcon className="text-primary"/>
72
+ <div className={"p-2"}>
73
+ <ArrowForwardIcon className="text-primary" size={"small"} />
72
74
  </div>
73
75
  </div>
74
76
 
@@ -1,17 +1,18 @@
1
1
  import React, { PropsWithChildren, useState } from "react";
2
2
  import { cls, EditIcon, IconButton, Typography, ExpandablePanel } from "@firecms/ui";
3
+ import { useTranslation } from "../../hooks/useTranslation";
3
4
 
4
5
  export function NavigationGroup({
5
- children,
6
- group,
7
- minimised,
8
- isPreview,
9
- isPotentialCardDropTarget,
10
- onEditGroup,
11
- dndDisabled,
12
- collapsed,
13
- onToggleCollapsed
14
- }: PropsWithChildren<{
6
+ children,
7
+ group,
8
+ minimised,
9
+ isPreview,
10
+ isPotentialCardDropTarget,
11
+ onEditGroup,
12
+ dndDisabled,
13
+ collapsed,
14
+ onToggleCollapsed
15
+ }: PropsWithChildren<{
15
16
  group: string | undefined,
16
17
  minimised?: boolean,
17
18
  isPreview?: boolean,
@@ -22,8 +23,9 @@ export function NavigationGroup({
22
23
  onToggleCollapsed?: () => void;
23
24
  }>) {
24
25
 
26
+ const { t } = useTranslation();
25
27
  const [isHovered, setIsHovered] = useState(false);
26
- const currentGroupName = group ?? "Views";
28
+ const currentGroupName = group ?? t("views_group");
27
29
 
28
30
  // Show caret only when not in preview and there is a toggle handler
29
31
  const showCaret = !isPreview && !!onToggleCollapsed;
@@ -52,7 +54,7 @@ export function NavigationGroup({
52
54
  }}
53
55
  className={cls("ml-2 ", isHovered ? "opacity-100" : "opacity-0", "transition-opacity duration-100")}
54
56
  >
55
- <EditIcon size="smallest"/>
57
+ <EditIcon size="smallest" />
56
58
  </IconButton>
57
59
  )}
58
60
  </div>
@@ -70,7 +72,7 @@ export function NavigationGroup({
70
72
  <div
71
73
  className={cls(
72
74
  "flex items-center justify-between w-full",
73
- "p-4 py-2"
75
+ "p-4 py-2"
74
76
  )}
75
77
  onMouseEnter={() => setIsHovered(true)}
76
78
  onMouseLeave={() => setIsHovered(false)}
@@ -94,9 +96,10 @@ export function NavigationGroup({
94
96
  className={cls("mt-6")}
95
97
  titleClassName={cls(
96
98
  "min-h-0 p-0 border-none",
97
- "rounded-t flex items-center justify-between w-full",
99
+ "rounded flex items-center justify-between w-full",
98
100
  "hover:bg-transparent",
99
- "cursor-pointer select-none"
101
+ "cursor-pointer select-none",
102
+ collapsed && "bg-surface-100 dark:bg-surface-800/50"
100
103
  )}
101
104
  innerClassName={cls("mt-4", !minimised ? "pt-0" : "")}
102
105
  title={
@@ -111,7 +114,7 @@ export function NavigationGroup({
111
114
  >
112
115
  {minimised ? (
113
116
  <div className={cls("mt-4 p-8 bg-surface-accent-200 dark:bg-surface-accent-800 rounded-lg")}
114
- style={{ minHeight: "50px" }}>
117
+ style={{ minHeight: "50px" }}>
115
118
  </div>
116
119
  ) : (
117
120
  <div className={cls("mt-4", !minimised ? "pt-0" : "")}>
@@ -138,7 +141,7 @@ export function NavigationGroup({
138
141
  {!collapsed && (
139
142
  minimised ? (
140
143
  <div className={cls("mt-4 p-8 bg-surface-accent-200 dark:bg-surface-accent-800 rounded-lg")}
141
- style={{ minHeight: "50px" }}>
144
+ style={{ minHeight: "50px" }}>
142
145
  </div>
143
146
  ) : (
144
147
  <div className={cls("mt-4", !minimised ? "pt-0" : "")}>