@intlayer/design-system 7.5.7 → 7.5.9

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 (149) hide show
  1. package/dist/esm/components/ContentEditor/ContentEditorTextArea.mjs +1 -1
  2. package/dist/esm/components/DictionaryEditor/DictionaryEditor.mjs +9 -19
  3. package/dist/esm/components/DictionaryEditor/DictionaryEditor.mjs.map +1 -1
  4. package/dist/esm/components/DictionaryFieldEditor/ContentEditor.mjs +2 -2
  5. package/dist/esm/components/DictionaryFieldEditor/ContentEditor.mjs.map +1 -1
  6. package/dist/esm/components/DictionaryFieldEditor/ContentEditorView/TextEditor.mjs +28 -23
  7. package/dist/esm/components/DictionaryFieldEditor/ContentEditorView/TextEditor.mjs.map +1 -1
  8. package/dist/esm/components/DictionaryFieldEditor/DictionaryFieldEditor.mjs +3 -2
  9. package/dist/esm/components/DictionaryFieldEditor/DictionaryFieldEditor.mjs.map +1 -1
  10. package/dist/esm/components/DictionaryFieldEditor/NavigationView/NavigationViewNode.mjs +28 -6
  11. package/dist/esm/components/DictionaryFieldEditor/NavigationView/NavigationViewNode.mjs.map +1 -1
  12. package/dist/esm/components/DictionaryFieldEditor/SaveForm/SaveForm.mjs +2 -0
  13. package/dist/esm/components/DictionaryFieldEditor/SaveForm/SaveForm.mjs.map +1 -1
  14. package/dist/esm/components/DictionaryFieldEditor/index.mjs +2 -1
  15. package/dist/esm/components/Form/FormLabel.mjs +1 -1
  16. package/dist/esm/components/Form/elements/AutoSizeTextAreaElement.mjs +1 -1
  17. package/dist/esm/components/Form/elements/EditableFieldTextAreaElement.mjs +1 -1
  18. package/dist/esm/components/Form/elements/MultiselectElement.mjs +1 -1
  19. package/dist/esm/components/Form/elements/SelectElement.mjs +1 -1
  20. package/dist/esm/components/Form/elements/SwitchSelectorElement.mjs +1 -1
  21. package/dist/esm/components/Form/elements/TextAreaElement.mjs +1 -1
  22. package/dist/esm/components/IDE/CodeFormatSelector.mjs +1 -1
  23. package/dist/esm/components/IDE/ContentDeclarationFormatSelector.mjs +1 -1
  24. package/dist/esm/components/IDE/PackageManagerSelector.mjs +1 -1
  25. package/dist/esm/components/KeyboardShortcut/KeyboardShortcut.mjs +8 -4
  26. package/dist/esm/components/KeyboardShortcut/KeyboardShortcut.mjs.map +1 -1
  27. package/dist/esm/components/LocaleSwitcherContentDropDown/LocaleSwitcherContent.mjs +1 -1
  28. package/dist/esm/components/MarkDownRender/MarkDownRender.mjs +1 -1
  29. package/dist/esm/components/MarkDownRender/MarkDownRender.mjs.map +1 -1
  30. package/dist/esm/components/Modal/Modal.mjs +2 -2
  31. package/dist/esm/components/Modal/Modal.mjs.map +1 -1
  32. package/dist/esm/components/RightDrawer/RightDrawer.mjs +83 -127
  33. package/dist/esm/components/RightDrawer/RightDrawer.mjs.map +1 -1
  34. package/dist/esm/components/RightDrawer/rightDrawer.content.mjs +51 -0
  35. package/dist/esm/components/RightDrawer/rightDrawer.content.mjs.map +1 -0
  36. package/dist/esm/components/Table/Table.mjs +1 -1
  37. package/dist/esm/components/index.mjs +12 -11
  38. package/dist/types/components/Badge/index.d.ts +2 -2
  39. package/dist/types/components/Breadcrumb/breadcrumb.content.d.ts +3 -3
  40. package/dist/types/components/Breadcrumb/breadcrumb.content.d.ts.map +1 -1
  41. package/dist/types/components/Breadcrumb/index.d.ts +2 -2
  42. package/dist/types/components/Browser/Browser.d.ts +2 -2
  43. package/dist/types/components/Browser/browser.content.d.ts +17 -17
  44. package/dist/types/components/Browser/browser.content.d.ts.map +1 -1
  45. package/dist/types/components/Button/Button.d.ts +6 -6
  46. package/dist/types/components/Button/Button.d.ts.map +1 -1
  47. package/dist/types/components/CollapsibleTable/CollapsibleTable.d.ts +2 -2
  48. package/dist/types/components/CollapsibleTable/CollapsibleTable.d.ts.map +1 -1
  49. package/dist/types/components/Command/index.d.ts +17 -17
  50. package/dist/types/components/Command/index.d.ts.map +1 -1
  51. package/dist/types/components/Container/index.d.ts +7 -7
  52. package/dist/types/components/Container/index.d.ts.map +1 -1
  53. package/dist/types/components/CopyButton/CopyButton.content.d.ts +3 -3
  54. package/dist/types/components/DictionaryEditor/DictionaryEditor.d.ts +0 -1
  55. package/dist/types/components/DictionaryEditor/DictionaryEditor.d.ts.map +1 -1
  56. package/dist/types/components/DictionaryFieldEditor/ContentEditorView/TextEditor.d.ts.map +1 -1
  57. package/dist/types/components/DictionaryFieldEditor/DictionaryCreationForm/dictionaryCreationForm.content.d.ts +25 -25
  58. package/dist/types/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.content.d.ts +9 -9
  59. package/dist/types/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.content.d.ts.map +1 -1
  60. package/dist/types/components/DictionaryFieldEditor/DictionaryDetails/dictionaryDetails.content.d.ts +33 -33
  61. package/dist/types/components/DictionaryFieldEditor/DictionaryDetails/useDictionaryDetailsSchema.content.d.ts +25 -25
  62. package/dist/types/components/DictionaryFieldEditor/NavigationView/navigationViewNode.content.d.ts +25 -25
  63. package/dist/types/components/DictionaryFieldEditor/SaveForm/saveForm.content.d.ts +33 -33
  64. package/dist/types/components/DictionaryFieldEditor/StructureView/structureView.content.d.ts +9 -9
  65. package/dist/types/components/DictionaryFieldEditor/VersionSwitcherDropDown/versionSwitcherDropDown.content.d.ts +3 -3
  66. package/dist/types/components/DictionaryFieldEditor/dictionaryFieldEditor.content.d.ts +5 -5
  67. package/dist/types/components/DictionaryFieldEditor/index.d.ts +2 -1
  68. package/dist/types/components/DictionaryFieldEditor/nodeTypeSelector.content.d.ts +31 -31
  69. package/dist/types/components/DictionaryFieldEditor/nodeTypeSelector.content.d.ts.map +1 -1
  70. package/dist/types/components/EditableField/EditableFieldInput.d.ts.map +1 -1
  71. package/dist/types/components/ExpandCollapse/expandCollapse.content.d.ts +3 -3
  72. package/dist/types/components/Form/FormBase.d.ts +2 -2
  73. package/dist/types/components/Form/FormField.d.ts +2 -2
  74. package/dist/types/components/Form/FormField.d.ts.map +1 -1
  75. package/dist/types/components/Form/FormItem.d.ts +2 -2
  76. package/dist/types/components/Form/FormItem.d.ts.map +1 -1
  77. package/dist/types/components/Form/elements/EditableFieldInputElement.d.ts +2 -2
  78. package/dist/types/components/Form/elements/EditableFieldInputElement.d.ts.map +1 -1
  79. package/dist/types/components/Form/elements/EditableFieldTextAreaElement.d.ts +2 -2
  80. package/dist/types/components/Form/elements/EditableFieldTextAreaElement.d.ts.map +1 -1
  81. package/dist/types/components/Form/elements/FormElement.d.ts +2 -2
  82. package/dist/types/components/Form/elements/FormElement.d.ts.map +1 -1
  83. package/dist/types/components/Form/elements/MultiselectElement.d.ts +2 -2
  84. package/dist/types/components/Form/elements/MultiselectElement.d.ts.map +1 -1
  85. package/dist/types/components/Form/elements/OTPElement.d.ts +2 -2
  86. package/dist/types/components/Form/elements/SelectElement.d.ts +2 -2
  87. package/dist/types/components/Form/elements/SelectElement.d.ts.map +1 -1
  88. package/dist/types/components/Form/elements/SwitchSelectorElement.d.ts +2 -2
  89. package/dist/types/components/IDE/CodeContext.d.ts +2 -2
  90. package/dist/types/components/IDE/CodeContext.d.ts.map +1 -1
  91. package/dist/types/components/IDE/code.content.d.ts +5 -5
  92. package/dist/types/components/IDE/copyCode.content.d.ts +5 -5
  93. package/dist/types/components/IDE/selectors.content.d.ts +7 -7
  94. package/dist/types/components/Input/Checkbox.d.ts +3 -3
  95. package/dist/types/components/Input/Checkbox.d.ts.map +1 -1
  96. package/dist/types/components/Input/Input.d.ts +2 -2
  97. package/dist/types/components/Input/OTPInput.d.ts +6 -6
  98. package/dist/types/components/Input/OTPInput.d.ts.map +1 -1
  99. package/dist/types/components/Input/SearchInput.d.ts +2 -2
  100. package/dist/types/components/Input/SearchInput.d.ts.map +1 -1
  101. package/dist/types/components/Link/Link.d.ts +5 -5
  102. package/dist/types/components/Loader/index.content.d.ts +3 -3
  103. package/dist/types/components/Loader/index.content.d.ts.map +1 -1
  104. package/dist/types/components/Loader/spinner.d.ts +2 -2
  105. package/dist/types/components/Loader/spinner.d.ts.map +1 -1
  106. package/dist/types/components/LocaleSwitcherContentDropDown/localeSwitcher.content.d.ts +17 -17
  107. package/dist/types/components/LocaleSwitcherContentDropDown/localeSwitcher.content.d.ts.map +1 -1
  108. package/dist/types/components/LocaleSwitcherDropDown/localeSwitcher.content.d.ts +13 -13
  109. package/dist/types/components/LocaleSwitcherDropDown/localeSwitcher.content.d.ts.map +1 -1
  110. package/dist/types/components/MarkDownRender/MarkDownRender.d.ts.map +1 -1
  111. package/dist/types/components/MaxWidthSmoother/index.d.ts +2 -2
  112. package/dist/types/components/Modal/Modal.d.ts.map +1 -1
  113. package/dist/types/components/Navbar/Burger.d.ts +2 -2
  114. package/dist/types/components/Navbar/Burger.d.ts.map +1 -1
  115. package/dist/types/components/Navbar/DesktopNavbar.d.ts +2 -2
  116. package/dist/types/components/Navbar/DesktopNavbar.d.ts.map +1 -1
  117. package/dist/types/components/Navbar/MobileNavbar.d.ts +2 -2
  118. package/dist/types/components/Navbar/MobileNavbar.d.ts.map +1 -1
  119. package/dist/types/components/Navbar/index.d.ts +2 -2
  120. package/dist/types/components/Navbar/index.d.ts.map +1 -1
  121. package/dist/types/components/Pagination/Pagination.d.ts +4 -4
  122. package/dist/types/components/Pagination/Pagination.d.ts.map +1 -1
  123. package/dist/types/components/Pagination/pagination.content.d.ts +11 -11
  124. package/dist/types/components/RightDrawer/RightDrawer.d.ts +7 -150
  125. package/dist/types/components/RightDrawer/RightDrawer.d.ts.map +1 -1
  126. package/dist/types/components/RightDrawer/rightDrawer.content.d.ts +93 -0
  127. package/dist/types/components/RightDrawer/rightDrawer.content.d.ts.map +1 -0
  128. package/dist/types/components/Select/Select.d.ts +3 -3
  129. package/dist/types/components/Select/Select.d.ts.map +1 -1
  130. package/dist/types/components/SocialNetworks/index.d.ts +2 -2
  131. package/dist/types/components/SocialNetworks/index.d.ts.map +1 -1
  132. package/dist/types/components/SwitchSelector/index.d.ts +7 -7
  133. package/dist/types/components/SwitchSelector/index.d.ts.map +1 -1
  134. package/dist/types/components/Tab/Tab.d.ts +6 -6
  135. package/dist/types/components/Tab/Tab.d.ts.map +1 -1
  136. package/dist/types/components/Tab/TabContext.d.ts +2 -2
  137. package/dist/types/components/Tab/TabContext.d.ts.map +1 -1
  138. package/dist/types/components/TabSelector/TabSelector.d.ts +5 -5
  139. package/dist/types/components/TabSelector/TabSelector.d.ts.map +1 -1
  140. package/dist/types/components/Table/table.content.d.ts +3 -3
  141. package/dist/types/components/Table/table.content.d.ts.map +1 -1
  142. package/dist/types/components/Tag/index.d.ts +3 -3
  143. package/dist/types/components/Terminal/terminal.content.d.ts +5 -5
  144. package/dist/types/components/Terminal/terminal.content.d.ts.map +1 -1
  145. package/dist/types/components/Toaster/Toast.d.ts +2 -2
  146. package/dist/types/components/Toaster/Toaster.d.ts +2 -2
  147. package/dist/types/components/Toaster/Toaster.d.ts.map +1 -1
  148. package/dist/types/components/index.d.ts +2 -1
  149. package/package.json +14 -14
@@ -1 +1 @@
1
- {"version":3,"file":"NavigationViewNode.mjs","names":["traceKeys: string[]","NavigationViewNode: FC<NodeWrapperProps>","keyPath","childKeyPath: KeyPath[]","newKeyPath: KeyPath[]"],"sources":["../../../../../src/components/DictionaryFieldEditor/NavigationView/NavigationViewNode.tsx"],"sourcesContent":["import configuration from '@intlayer/config/built';\nimport { camelCaseToSentence } from '@intlayer/config/client';\nimport {\n getContentNodeByKeyPath,\n getEmptyNode,\n getNodeType,\n isSameKeyPath,\n} from '@intlayer/core';\nimport {\n useEditedContentActions,\n useEditorLocale,\n useFocusUnmergedDictionary,\n} from '@intlayer/editor-react';\nimport {\n type KeyPath,\n type LocalDictionaryId,\n NodeType,\n} from '@intlayer/types';\nimport type { ContentNode, Dictionary } from 'intlayer';\nimport { ChevronRight, Plus } from 'lucide-react';\nimport type { FC } from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { Accordion } from '../../Accordion';\nimport {\n Button,\n ButtonColor,\n ButtonTextAlign,\n ButtonVariant,\n} from '../../Button';\nimport { getIsEditableSection } from '../getIsEditableSection';\n\nexport const traceKeys: string[] = ['filePath', 'id', 'nodeType'];\n\nexport type NodeWrapperProps = {\n keyPath: KeyPath[];\n section: ContentNode;\n dictionary: Dictionary;\n};\n\nexport const NavigationViewNode: FC<NodeWrapperProps> = ({\n section: sectionProp,\n keyPath,\n dictionary,\n}) => {\n const { locales } = configuration.internationalization;\n const currentLocale = useEditorLocale();\n const section = getContentNodeByKeyPath(sectionProp, keyPath, currentLocale);\n const { addEditedContent } = useEditedContentActions();\n const { setFocusedContentKeyPath, focusedContent } =\n useFocusUnmergedDictionary();\n const { addNewElement, goToField } = useIntlayer('navigation-view');\n const nodeType = getNodeType(section);\n const getIsSelected = (keyPath: KeyPath[]) =>\n (focusedContent?.keyPath?.length ?? 0) > 0 &&\n isSameKeyPath(keyPath, focusedContent?.keyPath ?? []);\n const isEditableSubSection = getIsEditableSection(section);\n\n if (!section) return <></>;\n\n if (isEditableSubSection) {\n return (\n <Button\n label={goToField.label.value}\n variant={ButtonVariant.HOVERABLE}\n color={ButtonColor.TEXT}\n className=\"w-full\"\n onClick={() => setFocusedContentKeyPath(keyPath)}\n IconRight={ChevronRight}\n >\n {camelCaseToSentence(keyPath[keyPath.length - 1].key as string)}\n </Button>\n );\n }\n\n if (typeof section === 'object') {\n if (nodeType === NodeType.ReactNode) {\n return <>React Node</>;\n }\n\n if (nodeType === NodeType.Translation) {\n return (\n <div className=\"flex flex-col justify-between gap-2\">\n {locales.map((translationKey) => {\n const childKeyPath: KeyPath[] = [\n ...keyPath,\n { type: NodeType.Translation, key: translationKey },\n ];\n\n return (\n <NavigationViewNode\n key={translationKey}\n keyPath={childKeyPath}\n section={sectionProp}\n dictionary={dictionary}\n />\n );\n })}\n </div>\n );\n }\n\n if (nodeType === NodeType.Enumeration || nodeType === NodeType.Condition) {\n return (\n <div className=\"flex flex-col justify-between gap-2\">\n {Object.keys(\n (section as any)[nodeType as unknown as keyof typeof section]\n ).map((key) => {\n const childKeyPath: KeyPath[] = [\n ...keyPath,\n { type: nodeType, key },\n ];\n\n return (\n <NavigationViewNode\n key={key}\n keyPath={childKeyPath}\n section={sectionProp}\n dictionary={dictionary}\n />\n );\n })}\n </div>\n );\n }\n\n if (nodeType === NodeType.Array) {\n return (\n <div className=\"flex flex-col justify-between gap-2\">\n {(section as unknown as ContentNode[]).map((_, index) => {\n const childKeyPath: KeyPath[] = [\n ...keyPath,\n { type: NodeType.Array, key: index },\n ];\n\n return (\n <NavigationViewNode\n key={JSON.stringify(childKeyPath)}\n keyPath={childKeyPath}\n section={sectionProp}\n dictionary={dictionary}\n />\n );\n })}\n\n <Button\n label={addNewElement.label.value}\n variant={ButtonVariant.HOVERABLE}\n color={ButtonColor.NEUTRAL}\n textAlign={ButtonTextAlign.LEFT}\n onClick={() => {\n const newKeyPath: KeyPath[] = [\n ...keyPath,\n {\n type: NodeType.Array,\n key: (section as unknown as ContentNode[]).length,\n },\n ];\n const sectionArray = section as unknown as ContentNode[];\n const emptySectionEl =\n getEmptyNode(\n sectionArray[\n (sectionArray.length - 1) as keyof typeof sectionArray\n ] as ContentNode\n ) ?? '';\n addEditedContent(\n dictionary.localId as LocalDictionaryId,\n emptySectionEl,\n newKeyPath,\n false\n );\n setFocusedContentKeyPath(newKeyPath);\n }}\n Icon={Plus}\n >\n {addNewElement.text}\n </Button>\n </div>\n );\n }\n\n if (typeof section.nodeType === 'string') {\n const childKeyPath: KeyPath[] = [\n ...keyPath,\n { type: section.nodeType } as KeyPath,\n ];\n\n return (\n <NavigationViewNode\n keyPath={childKeyPath}\n section={sectionProp}\n dictionary={dictionary}\n />\n );\n }\n\n const sectionArray = Object.keys(section);\n return (\n <div className=\"flex w-full max-w-full flex-col justify-between gap-2\">\n {sectionArray.map((key) => {\n const childKeyPath: KeyPath[] = [\n ...keyPath,\n { type: NodeType.Object, key },\n ];\n\n const subSection = getContentNodeByKeyPath(sectionProp, childKeyPath);\n const isEditableSubSection = getIsEditableSection(subSection);\n\n if (isEditableSubSection) {\n return (\n <Button\n label={`${goToField.label.value} ${key}`}\n key={key}\n isActive={getIsSelected(childKeyPath)}\n variant={ButtonVariant.HOVERABLE}\n color={ButtonColor.TEXT}\n className=\"w-full\"\n onClick={() => setFocusedContentKeyPath(childKeyPath)}\n IconRight={ChevronRight}\n >\n {camelCaseToSentence(key)}\n </Button>\n );\n }\n\n return (\n <Accordion\n key={key}\n label={`${goToField.label.value} ${key}`}\n isActive={getIsSelected(childKeyPath)}\n onClick={() => setFocusedContentKeyPath(childKeyPath)}\n header={camelCaseToSentence(key)}\n >\n <div className=\"mt-2 flex w-full max-w-full\">\n <div className=\"flex-1 pl-10\">\n <NavigationViewNode\n keyPath={childKeyPath}\n section={sectionProp}\n dictionary={dictionary}\n />\n </div>\n </div>\n </Accordion>\n );\n })}\n </div>\n );\n }\n\n return (\n <>\n Error loading section --\n {nodeType}\n --\n {JSON.stringify(section)}\n --\n {JSON.stringify(keyPath)}\n </>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"NavigationViewNode.mjs","names":["traceKeys: string[]","NavigationViewNode: FC<NodeWrapperProps>","keyPath","childKeyPath: KeyPath[]","newKeyPath: KeyPath[]"],"sources":["../../../../../src/components/DictionaryFieldEditor/NavigationView/NavigationViewNode.tsx"],"sourcesContent":["import configuration from '@intlayer/config/built';\nimport { camelCaseToSentence } from '@intlayer/config/client';\nimport {\n getContentNodeByKeyPath,\n getEmptyNode,\n getNodeType,\n isSameKeyPath,\n} from '@intlayer/core';\nimport {\n useEditedContentActions,\n useEditorLocale,\n useFocusUnmergedDictionary,\n} from '@intlayer/editor-react';\nimport {\n type KeyPath,\n type LocalDictionaryId,\n NodeType,\n} from '@intlayer/types';\nimport type { ContentNode, Dictionary } from 'intlayer';\nimport { ChevronRight, Plus } from 'lucide-react';\nimport type { FC } from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { Accordion } from '../../Accordion';\nimport {\n Button,\n ButtonColor,\n ButtonTextAlign,\n ButtonVariant,\n} from '../../Button';\nimport { getIsEditableSection } from '../getIsEditableSection';\n\nexport const traceKeys: string[] = ['filePath', 'id', 'nodeType'];\n\nexport type NodeWrapperProps = {\n keyPath: KeyPath[];\n section: ContentNode;\n dictionary: Dictionary;\n};\n\nexport const NavigationViewNode: FC<NodeWrapperProps> = ({\n section: sectionProp,\n keyPath,\n dictionary,\n}) => {\n const { locales } = configuration.internationalization;\n const currentLocale = useEditorLocale();\n const section = getContentNodeByKeyPath(sectionProp, keyPath, currentLocale);\n const { addEditedContent } = useEditedContentActions();\n const { setFocusedContentKeyPath, focusedContent } =\n useFocusUnmergedDictionary();\n const { addNewElement, goToField } = useIntlayer('navigation-view');\n const nodeType = getNodeType(section);\n const getIsSelected = (keyPath: KeyPath[]) =>\n (focusedContent?.keyPath?.length ?? 0) > 0 &&\n isSameKeyPath(keyPath, focusedContent?.keyPath ?? []);\n const isEditableSubSection = getIsEditableSection(section);\n\n if (!section) return <></>;\n\n if (isEditableSubSection) {\n return (\n <Button\n label={goToField.label.value}\n variant={ButtonVariant.HOVERABLE}\n color={ButtonColor.TEXT}\n className=\"w-full\"\n onClick={() => setFocusedContentKeyPath(keyPath)}\n IconRight={ChevronRight}\n >\n {camelCaseToSentence(keyPath[keyPath.length - 1].key as string)}\n </Button>\n );\n }\n\n if (typeof section === 'object') {\n if (nodeType === NodeType.ReactNode) {\n return <>React Node</>;\n }\n\n if (nodeType === NodeType.Translation) {\n return (\n <div className=\"flex flex-col justify-between gap-2\">\n {locales.map((translationKey) => {\n const childKeyPath: KeyPath[] = [\n ...keyPath,\n { type: NodeType.Translation, key: translationKey },\n ];\n\n return (\n <NavigationViewNode\n key={translationKey}\n keyPath={childKeyPath}\n section={sectionProp}\n dictionary={dictionary}\n />\n );\n })}\n </div>\n );\n }\n\n if (nodeType === NodeType.Enumeration || nodeType === NodeType.Condition) {\n return (\n <div className=\"flex flex-col justify-between gap-2\">\n {Object.keys(\n (section as any)[nodeType as unknown as keyof typeof section]\n ).map((key) => {\n const childKeyPath: KeyPath[] = [\n ...keyPath,\n { type: nodeType, key },\n ];\n\n return (\n <NavigationViewNode\n key={key}\n keyPath={childKeyPath}\n section={sectionProp}\n dictionary={dictionary}\n />\n );\n })}\n </div>\n );\n }\n\n if (nodeType === NodeType.Array) {\n return (\n <div className=\"flex flex-col justify-between gap-2\">\n {(section as unknown as ContentNode[]).map((subSection, index) => {\n const childKeyPath: KeyPath[] = [\n ...keyPath,\n { type: NodeType.Array, key: index },\n ];\n\n const isEditableSubSection = getIsEditableSection(subSection);\n\n if (isEditableSubSection) {\n return (\n <Button\n key={JSON.stringify(childKeyPath)}\n label={`${goToField.label.value} ${index}`}\n variant={ButtonVariant.HOVERABLE}\n color={ButtonColor.TEXT}\n className=\"w-full\"\n onClick={() => setFocusedContentKeyPath(childKeyPath)}\n IconRight={ChevronRight}\n isActive={getIsSelected(childKeyPath)}\n >\n Item {index}\n </Button>\n );\n }\n\n return (\n <Accordion\n key={JSON.stringify(childKeyPath)}\n label={`${goToField.label.value} ${index}`}\n header={`Item ${index}`}\n isActive={getIsSelected(childKeyPath)}\n onClick={() => setFocusedContentKeyPath(childKeyPath)}\n >\n <div className=\"mt-2 flex w-full max-w-full\">\n <div className=\"flex-1 pl-10\">\n <NavigationViewNode\n keyPath={childKeyPath}\n section={sectionProp}\n dictionary={dictionary}\n />\n </div>\n </div>\n </Accordion>\n );\n })}\n\n <Button\n label={addNewElement.label.value}\n variant={ButtonVariant.HOVERABLE}\n color={ButtonColor.NEUTRAL}\n textAlign={ButtonTextAlign.LEFT}\n onClick={() => {\n const newKeyPath: KeyPath[] = [\n ...keyPath,\n {\n type: NodeType.Array,\n key: (section as unknown as ContentNode[]).length,\n },\n ];\n const sectionArray = section as unknown as ContentNode[];\n const emptySectionEl =\n getEmptyNode(\n sectionArray[\n (sectionArray.length - 1) as keyof typeof sectionArray\n ] as ContentNode\n ) ?? '';\n addEditedContent(\n dictionary.localId as LocalDictionaryId,\n emptySectionEl,\n newKeyPath,\n false\n );\n setFocusedContentKeyPath(newKeyPath);\n }}\n Icon={Plus}\n >\n {addNewElement.text}\n </Button>\n </div>\n );\n }\n\n if (typeof section.nodeType === 'string') {\n const childKeyPath: KeyPath[] = [\n ...keyPath,\n { type: section.nodeType } as KeyPath,\n ];\n\n return (\n <NavigationViewNode\n keyPath={childKeyPath}\n section={sectionProp}\n dictionary={dictionary}\n />\n );\n }\n\n const sectionArray = Object.keys(section);\n return (\n <div className=\"flex w-full max-w-full flex-col justify-between gap-2\">\n {sectionArray.map((key) => {\n const childKeyPath: KeyPath[] = [\n ...keyPath,\n { type: NodeType.Object, key },\n ];\n\n const subSection = getContentNodeByKeyPath(sectionProp, childKeyPath);\n const isEditableSubSection = getIsEditableSection(subSection);\n\n if (isEditableSubSection) {\n return (\n <Button\n label={`${goToField.label.value} ${key}`}\n key={key}\n isActive={getIsSelected(childKeyPath)}\n variant={ButtonVariant.HOVERABLE}\n color={ButtonColor.TEXT}\n className=\"w-full\"\n onClick={() => setFocusedContentKeyPath(childKeyPath)}\n IconRight={ChevronRight}\n >\n {camelCaseToSentence(key)}\n </Button>\n );\n }\n\n return (\n <Accordion\n key={key}\n label={`${goToField.label.value} ${key}`}\n isActive={getIsSelected(childKeyPath)}\n onClick={() => setFocusedContentKeyPath(childKeyPath)}\n header={camelCaseToSentence(key)}\n >\n <div className=\"mt-2 flex w-full max-w-full\">\n <div className=\"flex-1 pl-10\">\n <NavigationViewNode\n keyPath={childKeyPath}\n section={sectionProp}\n dictionary={dictionary}\n />\n </div>\n </div>\n </Accordion>\n );\n })}\n </div>\n );\n }\n\n return (\n <>\n Error loading section --\n {nodeType}\n --\n {JSON.stringify(section)}\n --\n {JSON.stringify(keyPath)}\n </>\n );\n};\n"],"mappings":"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"}
@@ -34,6 +34,7 @@ const SaveForm = ({ dictionary, mode, className, onDelete, onSave, ...props }) =
34
34
  } }, { onSuccess: () => {
35
35
  setLocaleDictionary(editedContent?.[dictionary.localId]);
36
36
  restoreEditedContent(dictionary.localId);
37
+ setIsFormatAlertModalOpen(false);
37
38
  onSave?.();
38
39
  } });
39
40
  };
@@ -60,6 +61,7 @@ const SaveForm = ({ dictionary, mode, className, onDelete, onSave, ...props }) =
60
61
  isOpen: isFormatAlertModalOpen,
61
62
  title: confirmation.title.value,
62
63
  size: ModalSize.MD,
64
+ onClose: () => setIsFormatAlertModalOpen(false),
63
65
  children: /* @__PURE__ */ jsxs("form", {
64
66
  className: "size-full px-3",
65
67
  children: [/* @__PURE__ */ jsx("p", {
@@ -1 +1 @@
1
- {"version":3,"file":"SaveForm.mjs","names":["SaveForm: FC<DictionaryDetailsProps>"],"sources":["../../../../../src/components/DictionaryFieldEditor/SaveForm/SaveForm.tsx"],"sourcesContent":["'use client';\n\nimport type { Dictionary as DistantDictionary } from '@intlayer/backend';\nimport {\n useDictionariesRecordActions,\n useEditedContent,\n} from '@intlayer/editor-react';\nimport type { Dictionary } from '@intlayer/types';\nimport {\n ArrowUpFromLine,\n Download,\n RotateCcw,\n Save,\n Trash,\n} from 'lucide-react';\nimport {\n type DetailedHTMLProps,\n type FC,\n type FormHTMLAttributes,\n useState,\n} from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { Modal, ModalSize } from '../../../components/Modal';\nimport {\n useAuth,\n useDeleteDictionary,\n usePushDictionaries,\n useWriteDictionary,\n} from '../../../hooks';\nimport { cn } from '../../../utils/cn';\nimport { ButtonColor, ButtonVariant } from '../../Button';\nimport { Form } from '../../Form';\n\ntype DictionaryDetailsProps = {\n dictionary: Dictionary;\n mode: ('local' | 'remote')[];\n onDelete?: () => void;\n onSave?: () => void;\n} & DetailedHTMLProps<FormHTMLAttributes<HTMLFormElement>, HTMLFormElement>;\n\nexport const SaveForm: FC<DictionaryDetailsProps> = ({\n dictionary,\n mode,\n className,\n onDelete,\n onSave,\n ...props\n}) => {\n const [isFormatAlertModalOpen, setIsFormatAlertModalOpen] = useState(false);\n const { setLocaleDictionary } = useDictionariesRecordActions();\n const { mutate: deleteDictionary, isPending: isDeleting } =\n useDeleteDictionary();\n const { mutate: writeDictionary, isPending: isWriting } =\n useWriteDictionary();\n const { mutate: pushDictionaries, isPending: isPushing } =\n usePushDictionaries();\n const isLoading = isWriting || isPushing;\n\n const { editedContent, restoreEditedContent } = useEditedContent();\n const {\n deleteButton,\n resetButton,\n saveButton,\n publishButton,\n downloadButton,\n confirmation,\n } = useIntlayer('save-dictionary-details');\n const { isAuthenticated } = useAuth();\n\n const editedDictionary = editedContent?.[dictionary.localId!];\n\n const isEdited =\n editedDictionary &&\n JSON.stringify(editedDictionary) !== JSON.stringify(dictionary);\n\n const isDistantDictionary =\n typeof (dictionary as unknown as DistantDictionary)?.id !== 'undefined';\n\n const handleSaveDictionaryConfirmation = async () => {\n if (!editedContent?.[dictionary.localId!]) return;\n\n const updatedDictionary = {\n ...dictionary,\n ...editedContent?.[dictionary.localId!],\n };\n\n writeDictionary(\n {\n dictionary: updatedDictionary,\n },\n {\n onSuccess: () => {\n setLocaleDictionary(editedContent?.[dictionary.localId!]);\n restoreEditedContent(dictionary.localId!);\n onSave?.();\n },\n }\n );\n };\n\n const handlePushDictionary = () => {\n const updatedDictionary = {\n ...dictionary,\n ...editedContent?.[dictionary.localId!],\n };\n\n pushDictionaries(\n { dictionaries: [updatedDictionary] },\n {\n onSuccess: (res) => {\n if (res) {\n setLocaleDictionary(updatedDictionary);\n restoreEditedContent(dictionary.localId!);\n onSave?.();\n }\n },\n }\n );\n };\n\n const handleDeleteDictionary = () => {\n if (!dictionary.id) return;\n\n deleteDictionary(\n {\n dictionaryId: dictionary.id,\n },\n {\n onSuccess: (res) => {\n if (res) {\n onDelete?.();\n }\n },\n }\n );\n };\n\n return (\n <>\n <Modal\n isOpen={isFormatAlertModalOpen}\n title={confirmation.title.value}\n size={ModalSize.MD}\n >\n <form className=\"size-full px-3\">\n <p className=\"py-4 text-neutral text-sm\">{confirmation.message}</p>\n\n <div className=\"mt-12 flex justify-end gap-2 max-md:flex-col\">\n <Form.Button\n label={confirmation.cancelButton.label.value}\n disabled={!isEdited || isLoading}\n color={ButtonColor.TEXT}\n className=\"max-md:w-full\"\n variant={ButtonVariant.OUTLINE}\n onClick={() => setIsFormatAlertModalOpen(false)}\n >\n {confirmation.cancelButton.text}\n </Form.Button>\n <Form.Button\n label={confirmation.confirmButton.label.value}\n disabled={!isEdited || isLoading}\n Icon={Save}\n color={ButtonColor.TEXT}\n className=\"max-md:w-full\"\n isLoading={isPushing}\n onClick={handleSaveDictionaryConfirmation}\n >\n {confirmation.confirmButton.text}\n </Form.Button>\n </div>\n </form>\n </Modal>\n <form\n className={cn('flex justify-end gap-2 max-md:flex-col', className)}\n {...props}\n >\n {mode.includes('remote') &&\n isDistantDictionary &&\n onDelete &&\n isAuthenticated && (\n <Form.Button\n label={deleteButton.label.value}\n Icon={Trash}\n color={ButtonColor.ERROR}\n variant={ButtonVariant.OUTLINE}\n className=\"max-md:w-full\"\n isLoading={isDeleting}\n onClick={handleDeleteDictionary}\n >\n {deleteButton.text}\n </Form.Button>\n )}\n {isEdited && (\n <Form.Button\n label={resetButton.label.value}\n disabled={!isEdited}\n Icon={RotateCcw}\n variant={ButtonVariant.OUTLINE}\n color={ButtonColor.TEXT}\n className=\"max-md:w-full\"\n onClick={() => restoreEditedContent(dictionary.localId!)}\n >\n {resetButton.text}\n </Form.Button>\n )}\n {mode.includes('local') && (\n <Form.Button\n label={downloadButton.label.value}\n disabled={!isEdited || isLoading}\n Icon={Download}\n color={ButtonColor.TEXT}\n variant={\n isAuthenticated ? ButtonVariant.OUTLINE : ButtonVariant.DEFAULT\n }\n className=\"max-md:w-full\"\n isLoading={isWriting}\n onClick={() => setIsFormatAlertModalOpen(true)}\n >\n {downloadButton.text}\n </Form.Button>\n )}\n {mode.includes('remote') && isAuthenticated && !isDistantDictionary && (\n <Form.Button\n label={publishButton.label.value}\n disabled={isLoading}\n Icon={ArrowUpFromLine}\n color={ButtonColor.TEXT}\n className=\"max-md:w-full\"\n isLoading={isPushing}\n onClick={handlePushDictionary}\n >\n {publishButton.text}\n </Form.Button>\n )}\n {mode.includes('remote') &&\n isAuthenticated &&\n isDistantDictionary &&\n isEdited && (\n <Form.Button\n label={saveButton.label.value}\n disabled={!isEdited || isLoading}\n Icon={Save}\n color=\"text\"\n className=\"max-md:w-full\"\n isLoading={isPushing}\n onClick={handlePushDictionary}\n >\n {saveButton.text}\n </Form.Button>\n )}\n </form>\n </>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"SaveForm.mjs","names":["SaveForm: FC<DictionaryDetailsProps>"],"sources":["../../../../../src/components/DictionaryFieldEditor/SaveForm/SaveForm.tsx"],"sourcesContent":["'use client';\n\nimport type { Dictionary as DistantDictionary } from '@intlayer/backend';\nimport {\n useDictionariesRecordActions,\n useEditedContent,\n} from '@intlayer/editor-react';\nimport type { Dictionary } from '@intlayer/types';\nimport {\n ArrowUpFromLine,\n Download,\n RotateCcw,\n Save,\n Trash,\n} from 'lucide-react';\nimport {\n type DetailedHTMLProps,\n type FC,\n type FormHTMLAttributes,\n useState,\n} from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { Modal, ModalSize } from '../../../components/Modal';\nimport {\n useAuth,\n useDeleteDictionary,\n usePushDictionaries,\n useWriteDictionary,\n} from '../../../hooks';\nimport { cn } from '../../../utils/cn';\nimport { ButtonColor, ButtonVariant } from '../../Button';\nimport { Form } from '../../Form';\n\ntype DictionaryDetailsProps = {\n dictionary: Dictionary;\n mode: ('local' | 'remote')[];\n onDelete?: () => void;\n onSave?: () => void;\n} & DetailedHTMLProps<FormHTMLAttributes<HTMLFormElement>, HTMLFormElement>;\n\nexport const SaveForm: FC<DictionaryDetailsProps> = ({\n dictionary,\n mode,\n className,\n onDelete,\n onSave,\n ...props\n}) => {\n const [isFormatAlertModalOpen, setIsFormatAlertModalOpen] = useState(false);\n const { setLocaleDictionary } = useDictionariesRecordActions();\n const { mutate: deleteDictionary, isPending: isDeleting } =\n useDeleteDictionary();\n const { mutate: writeDictionary, isPending: isWriting } =\n useWriteDictionary();\n const { mutate: pushDictionaries, isPending: isPushing } =\n usePushDictionaries();\n const isLoading = isWriting || isPushing;\n\n const { editedContent, restoreEditedContent } = useEditedContent();\n const {\n deleteButton,\n resetButton,\n saveButton,\n publishButton,\n downloadButton,\n confirmation,\n } = useIntlayer('save-dictionary-details');\n const { isAuthenticated } = useAuth();\n\n const editedDictionary = editedContent?.[dictionary.localId!];\n\n const isEdited =\n editedDictionary &&\n JSON.stringify(editedDictionary) !== JSON.stringify(dictionary);\n\n const isDistantDictionary =\n typeof (dictionary as unknown as DistantDictionary)?.id !== 'undefined';\n\n const handleSaveDictionaryConfirmation = async () => {\n if (!editedContent?.[dictionary.localId!]) return;\n\n const updatedDictionary = {\n ...dictionary,\n ...editedContent?.[dictionary.localId!],\n };\n\n writeDictionary(\n {\n dictionary: updatedDictionary,\n },\n {\n onSuccess: () => {\n setLocaleDictionary(editedContent?.[dictionary.localId!]);\n restoreEditedContent(dictionary.localId!);\n setIsFormatAlertModalOpen(false);\n onSave?.();\n },\n }\n );\n };\n\n const handlePushDictionary = () => {\n const updatedDictionary = {\n ...dictionary,\n ...editedContent?.[dictionary.localId!],\n };\n\n pushDictionaries(\n { dictionaries: [updatedDictionary] },\n {\n onSuccess: (res) => {\n if (res) {\n setLocaleDictionary(updatedDictionary);\n restoreEditedContent(dictionary.localId!);\n onSave?.();\n }\n },\n }\n );\n };\n\n const handleDeleteDictionary = () => {\n if (!dictionary.id) return;\n\n deleteDictionary(\n {\n dictionaryId: dictionary.id,\n },\n {\n onSuccess: (res) => {\n if (res) {\n onDelete?.();\n }\n },\n }\n );\n };\n\n return (\n <>\n <Modal\n isOpen={isFormatAlertModalOpen}\n title={confirmation.title.value}\n size={ModalSize.MD}\n onClose={() => setIsFormatAlertModalOpen(false)}\n >\n <form className=\"size-full px-3\">\n <p className=\"py-4 text-neutral text-sm\">{confirmation.message}</p>\n\n <div className=\"mt-12 flex justify-end gap-2 max-md:flex-col\">\n <Form.Button\n label={confirmation.cancelButton.label.value}\n disabled={!isEdited || isLoading}\n color={ButtonColor.TEXT}\n className=\"max-md:w-full\"\n variant={ButtonVariant.OUTLINE}\n onClick={() => setIsFormatAlertModalOpen(false)}\n >\n {confirmation.cancelButton.text}\n </Form.Button>\n <Form.Button\n label={confirmation.confirmButton.label.value}\n disabled={!isEdited || isLoading}\n Icon={Save}\n color={ButtonColor.TEXT}\n className=\"max-md:w-full\"\n isLoading={isPushing}\n onClick={handleSaveDictionaryConfirmation}\n >\n {confirmation.confirmButton.text}\n </Form.Button>\n </div>\n </form>\n </Modal>\n <form\n className={cn('flex justify-end gap-2 max-md:flex-col', className)}\n {...props}\n >\n {mode.includes('remote') &&\n isDistantDictionary &&\n onDelete &&\n isAuthenticated && (\n <Form.Button\n label={deleteButton.label.value}\n Icon={Trash}\n color={ButtonColor.ERROR}\n variant={ButtonVariant.OUTLINE}\n className=\"max-md:w-full\"\n isLoading={isDeleting}\n onClick={handleDeleteDictionary}\n >\n {deleteButton.text}\n </Form.Button>\n )}\n {isEdited && (\n <Form.Button\n label={resetButton.label.value}\n disabled={!isEdited}\n Icon={RotateCcw}\n variant={ButtonVariant.OUTLINE}\n color={ButtonColor.TEXT}\n className=\"max-md:w-full\"\n onClick={() => restoreEditedContent(dictionary.localId!)}\n >\n {resetButton.text}\n </Form.Button>\n )}\n {mode.includes('local') && (\n <Form.Button\n label={downloadButton.label.value}\n disabled={!isEdited || isLoading}\n Icon={Download}\n color={ButtonColor.TEXT}\n variant={\n isAuthenticated ? ButtonVariant.OUTLINE : ButtonVariant.DEFAULT\n }\n className=\"max-md:w-full\"\n isLoading={isWriting}\n onClick={() => setIsFormatAlertModalOpen(true)}\n >\n {downloadButton.text}\n </Form.Button>\n )}\n {mode.includes('remote') && isAuthenticated && !isDistantDictionary && (\n <Form.Button\n label={publishButton.label.value}\n disabled={isLoading}\n Icon={ArrowUpFromLine}\n color={ButtonColor.TEXT}\n className=\"max-md:w-full\"\n isLoading={isPushing}\n onClick={handlePushDictionary}\n >\n {publishButton.text}\n </Form.Button>\n )}\n {mode.includes('remote') &&\n isAuthenticated &&\n isDistantDictionary &&\n isEdited && (\n <Form.Button\n label={saveButton.label.value}\n disabled={!isEdited || isLoading}\n Icon={Save}\n color=\"text\"\n className=\"max-md:w-full\"\n isLoading={isPushing}\n onClick={handlePushDictionary}\n >\n {saveButton.text}\n </Form.Button>\n )}\n </form>\n </>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;AAwCA,MAAaA,YAAwC,EACnD,YACA,MACA,WACA,UACA,QACA,GAAG,YACC;CACJ,MAAM,CAAC,wBAAwB,6BAA6B,SAAS,MAAM;CAC3E,MAAM,EAAE,wBAAwB,8BAA8B;CAC9D,MAAM,EAAE,QAAQ,kBAAkB,WAAW,eAC3C,qBAAqB;CACvB,MAAM,EAAE,QAAQ,iBAAiB,WAAW,cAC1C,oBAAoB;CACtB,MAAM,EAAE,QAAQ,kBAAkB,WAAW,cAC3C,qBAAqB;CACvB,MAAM,YAAY,aAAa;CAE/B,MAAM,EAAE,eAAe,yBAAyB,kBAAkB;CAClE,MAAM,EACJ,cACA,aACA,YACA,eACA,gBACA,iBACE,YAAY,0BAA0B;CAC1C,MAAM,EAAE,oBAAoB,SAAS;CAErC,MAAM,mBAAmB,gBAAgB,WAAW;CAEpD,MAAM,WACJ,oBACA,KAAK,UAAU,iBAAiB,KAAK,KAAK,UAAU,WAAW;CAEjE,MAAM,sBACJ,OAAQ,YAA6C,OAAO;CAE9D,MAAM,mCAAmC,YAAY;AACnD,MAAI,CAAC,gBAAgB,WAAW,SAAW;AAO3C,kBACE,EACE,YAPsB;GACxB,GAAG;GACH,GAAG,gBAAgB,WAAW;GAC/B,EAKE,EACD,EACE,iBAAiB;AACf,uBAAoB,gBAAgB,WAAW,SAAU;AACzD,wBAAqB,WAAW,QAAS;AACzC,6BAA0B,MAAM;AAChC,aAAU;KAEb,CACF;;CAGH,MAAM,6BAA6B;EACjC,MAAM,oBAAoB;GACxB,GAAG;GACH,GAAG,gBAAgB,WAAW;GAC/B;AAED,mBACE,EAAE,cAAc,CAAC,kBAAkB,EAAE,EACrC,EACE,YAAY,QAAQ;AAClB,OAAI,KAAK;AACP,wBAAoB,kBAAkB;AACtC,yBAAqB,WAAW,QAAS;AACzC,cAAU;;KAGf,CACF;;CAGH,MAAM,+BAA+B;AACnC,MAAI,CAAC,WAAW,GAAI;AAEpB,mBACE,EACE,cAAc,WAAW,IAC1B,EACD,EACE,YAAY,QAAQ;AAClB,OAAI,IACF,aAAY;KAGjB,CACF;;AAGH,QACE,8CACE,oBAAC;EACC,QAAQ;EACR,OAAO,aAAa,MAAM;EAC1B,MAAM,UAAU;EAChB,eAAe,0BAA0B,MAAM;YAE/C,qBAAC;GAAK,WAAU;cACd,oBAAC;IAAE,WAAU;cAA6B,aAAa;KAAY,EAEnE,qBAAC;IAAI,WAAU;eACb,oBAAC,KAAK;KACJ,OAAO,aAAa,aAAa,MAAM;KACvC,UAAU,CAAC,YAAY;KACvB,OAAO,YAAY;KACnB,WAAU;KACV,SAAS,cAAc;KACvB,eAAe,0BAA0B,MAAM;eAE9C,aAAa,aAAa;MACf,EACd,oBAAC,KAAK;KACJ,OAAO,aAAa,cAAc,MAAM;KACxC,UAAU,CAAC,YAAY;KACvB,MAAM;KACN,OAAO,YAAY;KACnB,WAAU;KACV,WAAW;KACX,SAAS;eAER,aAAa,cAAc;MAChB;KACV;IACD;GACD,EACR,qBAAC;EACC,WAAW,GAAG,0CAA0C,UAAU;EAClE,GAAI;;GAEH,KAAK,SAAS,SAAS,IACtB,uBACA,YACA,mBACE,oBAAC,KAAK;IACJ,OAAO,aAAa,MAAM;IAC1B,MAAM;IACN,OAAO,YAAY;IACnB,SAAS,cAAc;IACvB,WAAU;IACV,WAAW;IACX,SAAS;cAER,aAAa;KACF;GAEjB,YACC,oBAAC,KAAK;IACJ,OAAO,YAAY,MAAM;IACzB,UAAU,CAAC;IACX,MAAM;IACN,SAAS,cAAc;IACvB,OAAO,YAAY;IACnB,WAAU;IACV,eAAe,qBAAqB,WAAW,QAAS;cAEvD,YAAY;KACD;GAEf,KAAK,SAAS,QAAQ,IACrB,oBAAC,KAAK;IACJ,OAAO,eAAe,MAAM;IAC5B,UAAU,CAAC,YAAY;IACvB,MAAM;IACN,OAAO,YAAY;IACnB,SACE,kBAAkB,cAAc,UAAU,cAAc;IAE1D,WAAU;IACV,WAAW;IACX,eAAe,0BAA0B,KAAK;cAE7C,eAAe;KACJ;GAEf,KAAK,SAAS,SAAS,IAAI,mBAAmB,CAAC,uBAC9C,oBAAC,KAAK;IACJ,OAAO,cAAc,MAAM;IAC3B,UAAU;IACV,MAAM;IACN,OAAO,YAAY;IACnB,WAAU;IACV,WAAW;IACX,SAAS;cAER,cAAc;KACH;GAEf,KAAK,SAAS,SAAS,IACtB,mBACA,uBACA,YACE,oBAAC,KAAK;IACJ,OAAO,WAAW,MAAM;IACxB,UAAU,CAAC,YAAY;IACvB,MAAM;IACN,OAAM;IACN,WAAU;IACV,WAAW;IACX,SAAS;cAER,WAAW;KACA;;GAEb,IACN"}
@@ -2,8 +2,9 @@ import { TextEditor, TextEditorContainer, traceKeys } from "./ContentEditorView/
2
2
  import { KeyPathBreadcrumb } from "./KeyPathBreadcrumb.mjs";
3
3
  import { ContentEditor } from "./ContentEditor.mjs";
4
4
  import { DictionaryCreationForm } from "./DictionaryCreationForm/DictionaryCreationForm.mjs";
5
+ import { SaveForm } from "./SaveForm/SaveForm.mjs";
5
6
  import { DictionaryFieldEditor } from "./DictionaryFieldEditor.mjs";
6
7
  import { VersionSwitcherProvider, useVersionSwitcher } from "./VersionSwitcherDropDown/VersionSwitcherContext.mjs";
7
8
  import { VersionSwitcher } from "./VersionSwitcherDropDown/VersionSwitcher.mjs";
8
9
 
9
- export { ContentEditor, DictionaryCreationForm, DictionaryFieldEditor, KeyPathBreadcrumb, TextEditor, TextEditorContainer, VersionSwitcher, VersionSwitcherProvider, traceKeys, useVersionSwitcher };
10
+ export { ContentEditor, DictionaryCreationForm, DictionaryFieldEditor, KeyPathBreadcrumb, SaveForm, TextEditor, TextEditorContainer, VersionSwitcher, VersionSwitcherProvider, traceKeys, useVersionSwitcher };
@@ -1,8 +1,8 @@
1
1
  'use client';
2
2
 
3
3
  import { cn } from "../../utils/cn.mjs";
4
- import { useFormField } from "./FormField.mjs";
5
4
  import { Label } from "../Label/index.mjs";
5
+ import { useFormField } from "./FormField.mjs";
6
6
  import { jsx } from "react/jsx-runtime";
7
7
 
8
8
  //#region src/components/Form/FormLabel.tsx
@@ -1,5 +1,5 @@
1
- import { FormElement } from "./FormElement.mjs";
2
1
  import { AutoSizedTextArea } from "../../TextArea/AutoSizeTextArea.mjs";
2
+ import { FormElement } from "./FormElement.mjs";
3
3
  import { jsx } from "react/jsx-runtime";
4
4
 
5
5
  //#region src/components/Form/elements/AutoSizeTextAreaElement.tsx
@@ -1,5 +1,5 @@
1
- import { FormElement } from "./FormElement.mjs";
2
1
  import { EditableFieldTextArea } from "../../EditableField/EditableFieldTextArea.mjs";
2
+ import { FormElement } from "./FormElement.mjs";
3
3
  import { jsx } from "react/jsx-runtime";
4
4
 
5
5
  //#region src/components/Form/elements/EditableFieldTextAreaElement.tsx
@@ -1,8 +1,8 @@
1
1
  'use client';
2
2
 
3
+ import { MultiSelect } from "../../Select/Multiselect.mjs";
3
4
  import { useFormField } from "../FormField.mjs";
4
5
  import { FormItemLayout } from "../layout/FormItemLayout.mjs";
5
- import { MultiSelect } from "../../Select/Multiselect.mjs";
6
6
  import { Form } from "../Form.mjs";
7
7
  import { jsx } from "react/jsx-runtime";
8
8
  import { useFormContext } from "react-hook-form";
@@ -1,8 +1,8 @@
1
1
  'use client';
2
2
 
3
+ import { Select } from "../../Select/Select.mjs";
3
4
  import { useFormField } from "../FormField.mjs";
4
5
  import { FormItemLayout } from "../layout/FormItemLayout.mjs";
5
- import { Select } from "../../Select/Select.mjs";
6
6
  import { Form } from "../Form.mjs";
7
7
  import { jsx } from "react/jsx-runtime";
8
8
  import { useFormContext } from "react-hook-form";
@@ -1,8 +1,8 @@
1
1
  'use client';
2
2
 
3
+ import { SwitchSelector } from "../../SwitchSelector/index.mjs";
3
4
  import { useFormField } from "../FormField.mjs";
4
5
  import { FormItemLayout } from "../layout/FormItemLayout.mjs";
5
- import { SwitchSelector } from "../../SwitchSelector/index.mjs";
6
6
  import { Form } from "../Form.mjs";
7
7
  import { jsx } from "react/jsx-runtime";
8
8
  import { useFormContext } from "react-hook-form";
@@ -1,5 +1,5 @@
1
- import { FormElement } from "./FormElement.mjs";
2
1
  import { TextArea } from "../../TextArea/TextArea.mjs";
2
+ import { FormElement } from "./FormElement.mjs";
3
3
  import { jsx } from "react/jsx-runtime";
4
4
 
5
5
  //#region src/components/Form/elements/TextAreaElement.tsx
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
- import { Select } from "../Select/Select.mjs";
4
3
  import { useCodeContext } from "./CodeContext.mjs";
4
+ import { Select } from "../Select/Select.mjs";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  import { useIntlayer } from "react-intlayer";
7
7
 
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
- import { Select } from "../Select/Select.mjs";
4
3
  import { useCodeContext } from "./CodeContext.mjs";
4
+ import { Select } from "../Select/Select.mjs";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  import { useIntlayer } from "react-intlayer";
7
7
 
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
- import { Select } from "../Select/Select.mjs";
4
3
  import { useCodeContext } from "./CodeContext.mjs";
4
+ import { Select } from "../Select/Select.mjs";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  import { useIntlayer } from "react-intlayer";
7
7
 
@@ -170,12 +170,12 @@ const KeyboardShortcut = ({ shortcut, onTriggered, display = true, className, si
170
170
  if (event.shiftKey) normalizedEventKeys.add("Shift");
171
171
  if (currentKey.startsWith("Arrow")) {
172
172
  normalizedEventKeys.add(currentKey);
173
- const arrowSymbol = getDisplayKey(currentKey);
174
- normalizedEventKeys.add(arrowSymbol);
173
+ normalizedEventKeys.add(getDisplayKey(currentKey));
175
174
  } else normalizedEventKeys.add(currentKey.toUpperCase());
176
175
  setPressedKeys(normalizedEventKeys);
177
176
  if (onTriggered && matchesShortcut(event, keys)) {
178
- if (isInputField) return;
177
+ const isEscapeShortcut = keys.includes("Escape");
178
+ if (isInputField && !isEscapeShortcut) return;
179
179
  event.preventDefault();
180
180
  onTriggered();
181
181
  }
@@ -191,7 +191,11 @@ const KeyboardShortcut = ({ shortcut, onTriggered, display = true, className, si
191
191
  window.removeEventListener("keyup", handleKeyUp);
192
192
  window.removeEventListener("blur", handleKeyUp);
193
193
  };
194
- }, [keys, onTriggered]);
194
+ }, [
195
+ keys,
196
+ onTriggered,
197
+ isMac
198
+ ]);
195
199
  if (!display) return null;
196
200
  /**
197
201
  * Check if a key is currently pressed
@@ -1 +1 @@
1
- {"version":3,"file":"KeyboardShortcut.mjs","names":["KeyboardShortcut: FC<KeyboardShortcutProps>"],"sources":["../../../../src/components/KeyboardShortcut/KeyboardShortcut.tsx"],"sourcesContent":["'use client';\n\nimport { type FC, useEffect, useState } from 'react';\nimport { useDevice } from '../../hooks/useDevice';\nimport { cn } from '../../utils/cn';\n\n/**\n * Enum for available keyboard keys\n */\nexport enum KeyList {\n '⌘' = '⌘',\n Ctrl = 'Ctrl',\n Alt = 'Alt',\n '⌥' = '⌥',\n Shift = 'Shift',\n Meta = 'Meta',\n F = 'F',\n K = 'K',\n L = 'L',\n P = 'P',\n S = 'S',\n A = 'A',\n B = 'B',\n C = 'C',\n D = 'D',\n E = 'E',\n G = 'G',\n H = 'H',\n I = 'I',\n J = 'J',\n M = 'M',\n N = 'N',\n O = 'O',\n Q = 'Q',\n R = 'R',\n T = 'T',\n U = 'U',\n V = 'V',\n W = 'W',\n X = 'X',\n Y = 'Y',\n Z = 'Z',\n Enter = 'Enter',\n Escape = 'Escape',\n Backspace = 'Backspace',\n Tab = 'Tab',\n Space = 'Space',\n ArrowUp = 'ArrowUp',\n ArrowDown = 'ArrowDown',\n ArrowLeft = 'ArrowLeft',\n ArrowRight = 'ArrowRight',\n '↑' = '↑',\n '↓' = '↓',\n '←' = '←',\n '→' = '→',\n}\n\n/**\n * Type-safe keyboard shortcut combinations\n * Note: Using string type to avoid union type complexity issues\n * Expected format: \"Key + Key\" (e.g., \"⌘ + F\", \"Ctrl + Shift + K\")\n */\nexport type KeyboardShortcutType = string;\n\nexport type KeyboardShortcutProps = {\n /** The keyboard shortcut combination (e.g., \"⌘ + F\" or \"Ctrl + K\") */\n shortcut: KeyboardShortcutType;\n /** Callback function triggered when the shortcut is pressed */\n onTriggered?: () => void;\n /** Whether to display the shortcut visually (default: true) */\n display?: boolean;\n /** Additional CSS classes */\n className?: string;\n /** Size of the keyboard shortcut display */\n size?: 'sm' | 'md' | 'lg';\n};\n\n/**\n * Parse keyboard shortcut string into individual keys\n */\nconst parseShortcut = (shortcut: string): string[] => {\n return shortcut.split(' + ').map((key) => key.trim());\n};\n\n/**\n * Normalize key name for event comparison\n */\nconst normalizeKey = (key: string): string => {\n const keyMap: Record<string, string> = {\n '⌘': 'Meta',\n Ctrl: 'Control',\n Control: 'Control',\n Alt: 'Alt',\n '⌥': 'Alt',\n Shift: 'Shift',\n Meta: 'Meta',\n '↑': 'ArrowUp',\n '↓': 'ArrowDown',\n '←': 'ArrowLeft',\n '→': 'ArrowRight',\n ArrowUp: 'ArrowUp',\n ArrowDown: 'ArrowDown',\n ArrowLeft: 'ArrowLeft',\n ArrowRight: 'ArrowRight',\n };\n\n return keyMap[key] || key;\n};\n\n/**\n * Check if the keyboard event matches the shortcut\n */\nconst matchesShortcut = (event: KeyboardEvent, keys: string[]): boolean => {\n const normalizedKeys = keys.map(normalizeKey);\n const hasModifiers = {\n Meta: normalizedKeys.includes('Meta'),\n Control: normalizedKeys.includes('Control'),\n Alt: normalizedKeys.includes('Alt'),\n Shift: normalizedKeys.includes('Shift'),\n };\n\n // Check if all required modifiers are pressed\n if (\n hasModifiers.Meta !== event.metaKey ||\n hasModifiers.Control !== event.ctrlKey ||\n hasModifiers.Alt !== event.altKey ||\n hasModifiers.Shift !== event.shiftKey\n ) {\n return false;\n }\n\n // Find the non-modifier key\n const nonModifierKey = keys.find(\n (key) =>\n !['⌘', 'Ctrl', 'Control', 'Alt', '⌥', 'Shift', 'Meta'].includes(\n normalizeKey(key)\n )\n );\n\n if (!nonModifierKey) return false;\n\n // Normalize the key for comparison\n const normalizedNonModifierKey = normalizeKey(nonModifierKey);\n\n // Compare the main key\n // For arrow keys, compare directly with event.key\n if (normalizedNonModifierKey.startsWith('Arrow')) {\n return event.key === normalizedNonModifierKey;\n }\n\n // For other keys, compare case-insensitive\n return event.key.toLowerCase() === normalizedNonModifierKey.toLowerCase();\n};\n\n/**\n * Get display key symbol for better visual representation\n */\nconst getDisplayKey = (key: string): string => {\n const displayMap: Record<string, string> = {\n ArrowUp: '↑',\n ArrowDown: '↓',\n ArrowLeft: '←',\n ArrowRight: '→',\n };\n\n return displayMap[key] || key;\n};\n\n/**\n * Get display shortcut based on OS (Mac uses ⌘ and ⌥, others use Ctrl and Alt)\n */\nconst getDisplayShortcut = (shortcut: string, isMac: boolean): string => {\n let result = shortcut;\n\n if (isMac) {\n result = result.replace(/Ctrl/g, '⌘');\n result = result.replace(/Alt/g, '⌥');\n } else {\n result = result.replace(/⌘/g, 'Ctrl');\n result = result.replace(/⌥/g, 'Alt');\n }\n\n // Replace arrow key names with symbols\n result = result.replace(/ArrowUp/g, '↑');\n result = result.replace(/ArrowDown/g, '↓');\n result = result.replace(/ArrowLeft/g, '←');\n result = result.replace(/ArrowRight/g, '→');\n\n return result;\n};\n\n/**\n * KeyboardShortcut Component\n *\n * A reusable component that displays keyboard shortcuts and listens for key combinations.\n * Automatically adapts to Mac (⌘, ⌥) and Windows/Linux (Ctrl, Alt) conventions.\n *\n * @example\n * ```tsx\n * <KeyboardShortcut\n * shortcut=\"⌘ + F\"\n * onTriggered={() => setShowSearch(true)}\n * />\n * ```\n */\nexport const KeyboardShortcut: FC<KeyboardShortcutProps> = ({\n shortcut,\n onTriggered,\n display = true,\n className,\n size = 'md',\n}) => {\n const { isMac } = useDevice();\n const displayShortcut = getDisplayShortcut(shortcut, isMac ?? false);\n const keys = parseShortcut(displayShortcut);\n const [pressedKeys, setPressedKeys] = useState<Set<string>>(new Set());\n\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n // Don't trigger shortcuts when typing in input fields\n const target = event.target as HTMLElement;\n const isInputField =\n target.tagName === 'INPUT' ||\n target.tagName === 'TEXTAREA' ||\n target.isContentEditable;\n\n // Update pressed keys state for visual feedback\n const currentKey = event.key;\n const normalizedEventKeys = new Set<string>();\n\n // Add modifier keys\n if (event.metaKey) normalizedEventKeys.add('⌘');\n if (event.ctrlKey) normalizedEventKeys.add('Ctrl');\n if (event.altKey) normalizedEventKeys.add(isMac ? '⌥' : 'Alt');\n if (event.shiftKey) normalizedEventKeys.add('Shift');\n\n // Add the main key\n if (currentKey.startsWith('Arrow')) {\n // For arrow keys, add both the key name and the symbol\n normalizedEventKeys.add(currentKey);\n const arrowSymbol = getDisplayKey(currentKey);\n normalizedEventKeys.add(arrowSymbol);\n } else {\n normalizedEventKeys.add(currentKey.toUpperCase());\n }\n\n setPressedKeys(normalizedEventKeys);\n\n // Trigger callback if shortcut matches\n if (onTriggered && matchesShortcut(event, keys)) {\n // Don't trigger shortcuts when typing in input fields\n if (isInputField) {\n return;\n }\n event.preventDefault();\n onTriggered();\n }\n };\n\n const handleKeyUp = () => {\n // Clear pressed keys when any key is released\n setPressedKeys(new Set());\n };\n\n window.addEventListener('keydown', handleKeyDown);\n window.addEventListener('keyup', handleKeyUp);\n window.addEventListener('blur', handleKeyUp); // Clear on window blur\n\n return () => {\n window.removeEventListener('keydown', handleKeyDown);\n window.removeEventListener('keyup', handleKeyUp);\n window.removeEventListener('blur', handleKeyUp);\n };\n }, [keys, onTriggered]);\n\n if (!display) return null;\n\n /**\n * Check if a key is currently pressed\n */\n const isKeyPressed = (key: string): boolean => {\n const upperKey = key.toUpperCase();\n const normalizedKey = normalizeKey(key);\n\n return (\n pressedKeys.has(key) ||\n pressedKeys.has(upperKey) ||\n pressedKeys.has(normalizedKey) ||\n // Check for modifier key matches\n (key === '⌘' && pressedKeys.has('Meta')) ||\n (key === 'Ctrl' && pressedKeys.has('Control')) ||\n (key === '⌥' && pressedKeys.has('Alt')) ||\n (key === 'Alt' && pressedKeys.has('Alt')) ||\n // Check for arrow key symbols\n (key === '←' && pressedKeys.has('ArrowLeft')) ||\n (key === '→' && pressedKeys.has('ArrowRight')) ||\n (key === '↑' && pressedKeys.has('ArrowUp')) ||\n (key === '↓' && pressedKeys.has('ArrowDown'))\n );\n };\n\n return (\n <kbd\n className={cn(\n 'inline-flex items-center justify-center gap-0.5 p-0.5',\n 'rounded-lg [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl',\n 'font-medium font-sans',\n 'border-1 border-neutral/20 text-neutral',\n size === 'sm' && 'text-xs',\n size === 'md' && 'text-sm',\n size === 'lg' && 'text-base',\n className\n )}\n >\n {keys.map((key, index) => {\n const keyId = `${key}-${index}-${shortcut}`;\n const displayKey = getDisplayKey(key);\n return (\n <span key={keyId} className=\"inline-flex items-center\">\n {index > 0 && <span className=\"text-neutral/50\">+</span>}\n <span\n className={cn(\n 'min-w-4 px-0.5',\n isKeyPressed(key) && 'scale-120 font-bold text-text'\n )}\n >\n {displayKey}\n </span>\n </span>\n );\n })}\n </kbd>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"KeyboardShortcut.mjs","names":["KeyboardShortcut: FC<KeyboardShortcutProps>"],"sources":["../../../../src/components/KeyboardShortcut/KeyboardShortcut.tsx"],"sourcesContent":["'use client';\n\nimport { type FC, useEffect, useState } from 'react';\nimport { useDevice } from '../../hooks/useDevice';\nimport { cn } from '../../utils/cn';\n\n/**\n * Enum for available keyboard keys\n */\nexport enum KeyList {\n '⌘' = '⌘',\n Ctrl = 'Ctrl',\n Alt = 'Alt',\n '⌥' = '⌥',\n Shift = 'Shift',\n Meta = 'Meta',\n F = 'F',\n K = 'K',\n L = 'L',\n P = 'P',\n S = 'S',\n A = 'A',\n B = 'B',\n C = 'C',\n D = 'D',\n E = 'E',\n G = 'G',\n H = 'H',\n I = 'I',\n J = 'J',\n M = 'M',\n N = 'N',\n O = 'O',\n Q = 'Q',\n R = 'R',\n T = 'T',\n U = 'U',\n V = 'V',\n W = 'W',\n X = 'X',\n Y = 'Y',\n Z = 'Z',\n Enter = 'Enter',\n Escape = 'Escape',\n Backspace = 'Backspace',\n Tab = 'Tab',\n Space = 'Space',\n ArrowUp = 'ArrowUp',\n ArrowDown = 'ArrowDown',\n ArrowLeft = 'ArrowLeft',\n ArrowRight = 'ArrowRight',\n '↑' = '↑',\n '↓' = '↓',\n '←' = '←',\n '→' = '→',\n}\n\n/**\n * Type-safe keyboard shortcut combinations\n * Note: Using string type to avoid union type complexity issues\n * Expected format: \"Key + Key\" (e.g., \"⌘ + F\", \"Ctrl + Shift + K\")\n */\nexport type KeyboardShortcutType = string;\n\nexport type KeyboardShortcutProps = {\n /** The keyboard shortcut combination (e.g., \"⌘ + F\" or \"Ctrl + K\") */\n shortcut: KeyboardShortcutType;\n /** Callback function triggered when the shortcut is pressed */\n onTriggered?: () => void;\n /** Whether to display the shortcut visually (default: true) */\n display?: boolean;\n /** Additional CSS classes */\n className?: string;\n /** Size of the keyboard shortcut display */\n size?: 'sm' | 'md' | 'lg';\n};\n\n/**\n * Parse keyboard shortcut string into individual keys\n */\nconst parseShortcut = (shortcut: string): string[] => {\n return shortcut.split(' + ').map((key) => key.trim());\n};\n\n/**\n * Normalize key name for event comparison\n */\nconst normalizeKey = (key: string): string => {\n const keyMap: Record<string, string> = {\n '⌘': 'Meta',\n Ctrl: 'Control',\n Control: 'Control',\n Alt: 'Alt',\n '⌥': 'Alt',\n Shift: 'Shift',\n Meta: 'Meta',\n '↑': 'ArrowUp',\n '↓': 'ArrowDown',\n '←': 'ArrowLeft',\n '→': 'ArrowRight',\n ArrowUp: 'ArrowUp',\n ArrowDown: 'ArrowDown',\n ArrowLeft: 'ArrowLeft',\n ArrowRight: 'ArrowRight',\n };\n\n return keyMap[key] || key;\n};\n\n/**\n * Check if the keyboard event matches the shortcut\n */\nconst matchesShortcut = (event: KeyboardEvent, keys: string[]): boolean => {\n const normalizedKeys = keys.map(normalizeKey);\n const hasModifiers = {\n Meta: normalizedKeys.includes('Meta'),\n Control: normalizedKeys.includes('Control'),\n Alt: normalizedKeys.includes('Alt'),\n Shift: normalizedKeys.includes('Shift'),\n };\n\n // Check if all required modifiers are pressed\n if (\n hasModifiers.Meta !== event.metaKey ||\n hasModifiers.Control !== event.ctrlKey ||\n hasModifiers.Alt !== event.altKey ||\n hasModifiers.Shift !== event.shiftKey\n ) {\n return false;\n }\n\n // Find the non-modifier key\n const nonModifierKey = keys.find(\n (key) =>\n !['⌘', 'Ctrl', 'Control', 'Alt', '⌥', 'Shift', 'Meta'].includes(\n normalizeKey(key)\n )\n );\n\n if (!nonModifierKey) return false;\n\n // Normalize the key for comparison\n const normalizedNonModifierKey = normalizeKey(nonModifierKey);\n\n // Compare the main key\n // For arrow keys, compare directly with event.key\n if (normalizedNonModifierKey.startsWith('Arrow')) {\n return event.key === normalizedNonModifierKey;\n }\n\n // For other keys, compare case-insensitive\n return event.key.toLowerCase() === normalizedNonModifierKey.toLowerCase();\n};\n\n/**\n * Get display key symbol for better visual representation\n */\nconst getDisplayKey = (key: string): string => {\n const displayMap: Record<string, string> = {\n ArrowUp: '↑',\n ArrowDown: '↓',\n ArrowLeft: '←',\n ArrowRight: '→',\n };\n\n return displayMap[key] || key;\n};\n\n/**\n * Get display shortcut based on OS (Mac uses ⌘ and ⌥, others use Ctrl and Alt)\n */\nconst getDisplayShortcut = (shortcut: string, isMac: boolean): string => {\n let result = shortcut;\n\n if (isMac) {\n result = result.replace(/Ctrl/g, '⌘');\n result = result.replace(/Alt/g, '⌥');\n } else {\n result = result.replace(/⌘/g, 'Ctrl');\n result = result.replace(/⌥/g, 'Alt');\n }\n\n // Replace arrow key names with symbols\n result = result.replace(/ArrowUp/g, '↑');\n result = result.replace(/ArrowDown/g, '↓');\n result = result.replace(/ArrowLeft/g, '←');\n result = result.replace(/ArrowRight/g, '→');\n\n return result;\n};\n\n/**\n * KeyboardShortcut Component\n *\n * A reusable component that displays keyboard shortcuts and listens for key combinations.\n * Automatically adapts to Mac (⌘, ⌥) and Windows/Linux (Ctrl, Alt) conventions.\n *\n * @example\n * ```tsx\n * <KeyboardShortcut\n * shortcut=\"⌘ + F\"\n * onTriggered={() => setShowSearch(true)}\n * />\n * ```\n */\nexport const KeyboardShortcut: FC<KeyboardShortcutProps> = ({\n shortcut,\n onTriggered,\n display = true,\n className,\n size = 'md',\n}) => {\n const { isMac } = useDevice();\n const displayShortcut = getDisplayShortcut(shortcut, isMac ?? false);\n const keys = parseShortcut(displayShortcut);\n const [pressedKeys, setPressedKeys] = useState<Set<string>>(new Set());\n\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n // 1. Identify input fields\n const target = event.target as HTMLElement;\n const isInputField =\n target.tagName === 'INPUT' ||\n target.tagName === 'TEXTAREA' ||\n target.isContentEditable;\n\n // ... (Your existing key visualization logic here) ...\n // Note: Copied your key tracking logic for context\n const currentKey = event.key;\n const normalizedEventKeys = new Set<string>();\n if (event.metaKey) normalizedEventKeys.add('⌘');\n if (event.ctrlKey) normalizedEventKeys.add('Ctrl');\n if (event.altKey) normalizedEventKeys.add(isMac ? '⌥' : 'Alt');\n if (event.shiftKey) normalizedEventKeys.add('Shift');\n\n if (currentKey.startsWith('Arrow')) {\n normalizedEventKeys.add(currentKey);\n normalizedEventKeys.add(getDisplayKey(currentKey));\n } else {\n normalizedEventKeys.add(currentKey.toUpperCase());\n }\n setPressedKeys(normalizedEventKeys);\n\n // 2. Trigger callback if shortcut matches\n if (onTriggered && matchesShortcut(event, keys)) {\n // FIX: Check if the required shortcut is \"Escape\"\n const isEscapeShortcut = keys.includes('Escape');\n\n // Only block if it is an input field AND the shortcut is NOT Escape\n if (isInputField && !isEscapeShortcut) {\n return;\n }\n\n event.preventDefault();\n onTriggered();\n }\n };\n\n const handleKeyUp = () => {\n setPressedKeys(new Set());\n };\n\n window.addEventListener('keydown', handleKeyDown);\n window.addEventListener('keyup', handleKeyUp);\n window.addEventListener('blur', handleKeyUp);\n\n return () => {\n window.removeEventListener('keydown', handleKeyDown);\n window.removeEventListener('keyup', handleKeyUp);\n window.removeEventListener('blur', handleKeyUp);\n };\n }, [keys, onTriggered, isMac]);\n if (!display) return null;\n\n /**\n * Check if a key is currently pressed\n */\n const isKeyPressed = (key: string): boolean => {\n const upperKey = key.toUpperCase();\n const normalizedKey = normalizeKey(key);\n\n return (\n pressedKeys.has(key) ||\n pressedKeys.has(upperKey) ||\n pressedKeys.has(normalizedKey) ||\n // Check for modifier key matches\n (key === '⌘' && pressedKeys.has('Meta')) ||\n (key === 'Ctrl' && pressedKeys.has('Control')) ||\n (key === '⌥' && pressedKeys.has('Alt')) ||\n (key === 'Alt' && pressedKeys.has('Alt')) ||\n // Check for arrow key symbols\n (key === '←' && pressedKeys.has('ArrowLeft')) ||\n (key === '→' && pressedKeys.has('ArrowRight')) ||\n (key === '↑' && pressedKeys.has('ArrowUp')) ||\n (key === '↓' && pressedKeys.has('ArrowDown'))\n );\n };\n\n return (\n <kbd\n className={cn(\n 'inline-flex items-center justify-center gap-0.5 p-0.5',\n 'rounded-lg [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl',\n 'font-medium font-sans',\n 'border-1 border-neutral/20 text-neutral',\n size === 'sm' && 'text-xs',\n size === 'md' && 'text-sm',\n size === 'lg' && 'text-base',\n className\n )}\n >\n {keys.map((key, index) => {\n const keyId = `${key}-${index}-${shortcut}`;\n const displayKey = getDisplayKey(key);\n return (\n <span key={keyId} className=\"inline-flex items-center\">\n {index > 0 && <span className=\"text-neutral/50\">+</span>}\n <span\n className={cn(\n 'min-w-4 px-0.5',\n isKeyPressed(key) && 'scale-120 font-bold text-text'\n )}\n >\n {displayKey}\n </span>\n </span>\n );\n })}\n </kbd>\n );\n};\n"],"mappings":"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"}
@@ -4,8 +4,8 @@ import { Container } from "../Container/index.mjs";
4
4
  import { Button, ButtonColor, ButtonSize, ButtonTextAlign, ButtonVariant } from "../Button/Button.mjs";
5
5
  import { Input } from "../Input/Input.mjs";
6
6
  import { usePersistedStore } from "../../hooks/usePersistedStore.mjs";
7
- import { SwitchSelector, SwitchSelectorColor, SwitchSelectorSize } from "../SwitchSelector/index.mjs";
8
7
  import { DropDown } from "../DropDown/index.mjs";
8
+ import { SwitchSelector, SwitchSelectorColor, SwitchSelectorSize } from "../SwitchSelector/index.mjs";
9
9
  import { useLocaleSwitcherContent } from "./LocaleSwitcherContentContext.mjs";
10
10
  import { Check, Globe, MoveVertical } from "lucide-react";
11
11
  import { useMemo, useRef, useState } from "react";
@@ -219,7 +219,7 @@ const MarkdownRenderer = ({ children, isDarkMode, locale, options }) => {
219
219
  isDarkMode,
220
220
  language: props.className?.replace("lang-", "") || "plaintext",
221
221
  fileName: props.fileName,
222
- showHeader: Boolean(props.fileName)
222
+ showHeader: Boolean(props.fileName || props.packageManager || props.codeFormat || props.contentDeclarationFormat)
223
223
  }),
224
224
  blockquote: ({ className, ...props }) => /* @__PURE__ */ jsx("blockquote", {
225
225
  className: cn("mt-5 gap-3 border-card border-l-4 pl-5 text-neutral", className),
@@ -1 +1 @@
1
- {"version":3,"file":"MarkDownRender.mjs","names":["MarkdownRenderer: FC<MarkdownRendererProps>"],"sources":["../../../../src/components/MarkDownRender/MarkDownRender.tsx"],"sourcesContent":["import type { LocalesValues } from '@intlayer/types';\nimport type { ComponentProps, ComponentPropsWithoutRef, FC } from 'react';\nimport type { BundledLanguage } from 'shiki/bundle/web';\nimport { cn } from '../../utils/cn';\nimport { H1, H2, H3, H4, H5, H6 } from '../Headers';\nimport type { CodeCompAttributes } from '../IDE/Code';\nimport { Code } from '../IDE/Code';\nimport { CodeProvider } from '../IDE/CodeContext';\nimport { Link } from '../Link';\nimport { Tab } from '../Tab';\nimport { TabProvider } from '../Tab/TabContext';\nimport { Table } from '../Table';\nimport { MarkdownProcessor, type MarkdownProcessorOptions } from './processor';\n\ntype MarkdownRendererProps = {\n children: string;\n isDarkMode?: boolean;\n locale?: LocalesValues;\n options?: MarkdownProcessorOptions;\n};\n\n/**\n * Removes frontmatter from markdown content\n * Frontmatter is the YAML metadata block at the beginning of markdown files\n * delimited by --- at the start and end\n */\nconst stripFrontmatter = (markdown: string): string => {\n const lines = markdown.split(/\\r?\\n/);\n\n // Check if the very first non-empty line is the metadata start delimiter\n const firstNonEmptyLine = lines.find((line) => line.trim() !== '');\n\n if (!firstNonEmptyLine || firstNonEmptyLine.trim() !== '---') {\n // No frontmatter, return original content\n return markdown;\n }\n\n let inMetadataBlock = false;\n let endOfMetadataIndex = -1;\n\n for (let i = 0; i < lines.length; i++) {\n const trimmedLine = lines[i].trim();\n\n // Toggle metadata block on encountering the delimiter\n if (trimmedLine === '---') {\n if (!inMetadataBlock) {\n // Begin metadata block\n inMetadataBlock = true;\n } else {\n // End of metadata block\n endOfMetadataIndex = i;\n break;\n }\n }\n }\n\n if (endOfMetadataIndex > -1) {\n // Return content after the frontmatter\n return lines.slice(endOfMetadataIndex + 1).join('\\n');\n }\n\n // If we couldn't find the end delimiter, return original content\n return markdown;\n};\n\n/**\n * MarkdownRenderer Component\n *\n * A comprehensive markdown renderer that transforms markdown text into rich,\n * interactive HTML with custom styling and Intlayer integration. Supports\n * code syntax highlighting, responsive tables, internationalized links,\n * and automatic frontmatter stripping.\n *\n * @component\n * @example\n * Basic usage:\n * ```tsx\n * const markdownContent = `\n * # Hello World\n * This is **bold** and *italic* text.\n * `;\n *\n * <MarkdownRenderer>{markdownContent}</MarkdownRenderer>\n * ```\n *\n * @example\n * With dark mode:\n * ```tsx\n * const codeExample = `\n * # API Documentation\n * \\`\\`\\`javascript\n * const response = await fetch('/api/data');\n * const data = await response.json();\n * \\`\\`\\`\n * `;\n *\n * <MarkdownRenderer isDarkMode={true}>\n * {codeExample}\n * </MarkdownRenderer>\n * ```\n *\n * @example\n * With internationalized links:\n * ```tsx\n * const content = `\n * Visit our [documentation](/docs) for more information.\n * External link: [Google](https://google.com)\n * `;\n *\n * <MarkdownRenderer locale=\"fr\">\n * {content}\n * </MarkdownRenderer>\n * ```\n *\n * @example\n * With custom overrides:\n * ```tsx\n * const customOptions = {\n * overrides: {\n * h1: ({ children }) => (\n * <h1 className=\"custom-title\">{children}</h1>\n * ),\n * },\n * };\n *\n * <MarkdownRenderer options={customOptions}>\n * {markdownContent}\n * </MarkdownRenderer>\n * ```\n *\n * @example\n * With frontmatter (automatically stripped):\n * ```tsx\n * const blogPost = `\n * ---\n * title: \"My Blog Post\"\n * date: \"2023-12-01\"\n * author: \"John Doe\"\n * ---\n * # My Blog Post\n * This content will be rendered without the frontmatter.\n * `;\n *\n * <MarkdownRenderer>{blogPost}</MarkdownRenderer>\n * ```\n *\n * Features:\n * - Automatic frontmatter detection and removal\n * - Syntax-highlighted code blocks with theme support\n * - Clickable headers with anchor links\n * - Internationalized link handling with locale awareness\n * - Responsive tables with hover effects\n * - Custom blockquote, list, and image styling\n * - External link detection and security attributes\n * - Inline code with distinctive styling\n * - Lazy-loaded images with GitHub raw URL support\n * - Horizontal rules with custom styling\n *\n * Supported Markdown Elements:\n * - Headers (h1-h4) with click-to-anchor functionality\n * - Code blocks with language-specific syntax highlighting\n * - Inline code with background styling\n * - Blockquotes with custom border and padding\n * - Ordered and unordered lists with custom spacing\n * - Links (internal and external) with security attributes\n * - Images with lazy loading and responsive sizing\n * - Tables with hover effects and proper styling\n * - Horizontal rules with custom appearance\n * - All standard markdown formatting (bold, italic, etc.)\n *\n * Code Block Support:\n * - Language detection from code fence info\n * - Syntax highlighting through Code component\n * - Dark/light mode theme switching\n * - Line numbers and copy functionality (via Code component)\n * - Support for popular languages (JS, TS, Python, etc.)\n *\n * Link Handling:\n * - Automatic external link detection (starts with 'http')\n * - Security attributes for external links (rel=\"noopener noreferrer\")\n * - Locale-aware internal link routing\n * - Custom Link component integration\n * - Underlined styling for better visibility\n *\n * Image Processing:\n * - Automatic lazy loading for performance\n * - GitHub raw URL transformation for repository images\n * - Responsive sizing with max-width constraints\n * - Rounded corners for visual appeal\n *\n * Accessibility:\n * - Semantic HTML structure with proper heading hierarchy\n * - ARIA attributes through component integration\n * - Screen reader friendly link descriptions\n * - Keyboard navigation support\n * - High contrast styling options\n *\n * Performance:\n * - Lazy loading for images\n * - Efficient re-rendering with React memo patterns\n * - Code syntax highlighting with minimal bundle impact\n * - Optimized markdown parsing with markdown-to-jsx\n *\n * @param props - Component props\n * @param props.children - Raw markdown content to render\n * @param props.isDarkMode - Enable dark mode styling for code blocks\n * @param props.locale - Current locale for link internationalization\n * @param props.options - Additional markdown-to-jsx options\n * @param props.options.overrides - Custom component overrides for markdown elements\n *\n * @returns Rendered markdown content with custom styling and functionality\n */\nexport const MarkdownRenderer: FC<MarkdownRendererProps> = ({\n children,\n isDarkMode,\n locale,\n options,\n}) => {\n const { overrides, ...restOptions } = options ?? {};\n\n // Strip frontmatter from the markdown content before rendering\n const cleanMarkdown = stripFrontmatter(children);\n\n return (\n <CodeProvider>\n <TabProvider>\n <MarkdownProcessor\n options={{\n overrides: {\n h1: (props: ComponentProps<typeof H1>) => (\n <H1 isClickable={true} {...props} />\n ),\n h2: (props: ComponentProps<typeof H2>) => (\n <H2 isClickable={true} className=\"mt-16\" {...props} />\n ),\n h3: (props: ComponentProps<typeof H3>) => (\n <H3 isClickable={true} className=\"mt-5\" {...props} />\n ),\n h4: (props: ComponentProps<typeof H4>) => (\n <H4 isClickable={true} className=\"mt-3\" {...props} />\n ),\n h5: (props: ComponentProps<typeof H5>) => (\n <H5 isClickable={true} className=\"mt-3\" {...props} />\n ),\n h6: (props: ComponentProps<typeof H6>) => (\n <H6 isClickable={true} className=\"mt-3\" {...props} />\n ),\n\n code: (\n props: Omit<ComponentPropsWithoutRef<'code'>, 'children'> &\n Partial<CodeCompAttributes> & { children: string }\n ) =>\n !props.className ? (\n <strong className=\"rounded bg-card/60 box-decoration-clone px-1 py-0.5\">\n {props.children}\n </strong>\n ) : (\n <Code\n {...props}\n isDarkMode={isDarkMode}\n language={\n (props.className?.replace('lang-', '') ||\n 'plaintext') as BundledLanguage\n }\n fileName={props.fileName}\n showHeader={Boolean(props.fileName)}\n />\n ),\n\n blockquote: ({\n className,\n ...props\n }: ComponentPropsWithoutRef<'blockquote'>) => (\n <blockquote\n className={cn(\n 'mt-5 gap-3 border-card border-l-4 pl-5 text-neutral',\n className\n )}\n {...props}\n />\n ),\n ul: ({ className, ...props }: ComponentPropsWithoutRef<'ul'>) => (\n <ul\n className={cn(\n 'mt-5 flex list-disc flex-col gap-3 pl-5',\n className\n )}\n {...props}\n />\n ),\n ol: ({ className, ...props }: ComponentPropsWithoutRef<'ol'>) => (\n <ol\n className={cn(\n 'mt-5 flex list-decimal flex-col gap-3 pl-5',\n className\n )}\n {...props}\n />\n ),\n img: ({\n className,\n ...props\n }: ComponentPropsWithoutRef<'img'>) => (\n <img\n {...props}\n loading=\"lazy\"\n className={cn(\n 'max-h-[80vh] max-w-full rounded-md',\n className\n )}\n src={`${props.src}?raw=true`}\n />\n ),\n a: (props: ComponentProps<typeof Link>) => (\n <Link\n color=\"neutral\"\n isExternalLink={props.href?.startsWith('http')}\n underlined={true}\n locale={locale}\n {...props}\n />\n ),\n pre: (props: ComponentPropsWithoutRef<'pre'>) => props.children,\n\n table: (props: ComponentProps<typeof Table>) => (\n <Table isRollable={true} {...props} />\n ),\n th: ({ className, ...props }: ComponentPropsWithoutRef<'th'>) => (\n <th\n className={cn(\n 'border-neutral border-b bg-neutral/10 p-4',\n className\n )}\n {...props}\n />\n ),\n tr: ({ className, ...props }: ComponentPropsWithoutRef<'tr'>) => (\n <tr\n className={cn('hover:/10 hover:bg-neutral/10', className)}\n {...props}\n />\n ),\n td: ({ className, ...props }: ComponentPropsWithoutRef<'td'>) => (\n <td\n className={cn(\n 'border-neutral-500/50 border-b p-4',\n className\n )}\n {...props}\n />\n ),\n hr: ({ className, ...props }: ComponentPropsWithoutRef<'hr'>) => (\n <hr\n className={cn('mx-6 mt-16 text-neutral', className)}\n {...props}\n />\n ),\n // Support both <Tab> and <Tabs> in markdown\n Tabs: (props: ComponentProps<typeof Tab>) => <Tab {...props} />,\n Tab: (props: ComponentProps<typeof Tab>) => <Tab {...props} />,\n TabItem: (props: ComponentProps<typeof Tab.Item>) => (\n <Tab.Item {...props} />\n ),\n Columns: ({\n className,\n ...props\n }: ComponentPropsWithoutRef<'div'>) => (\n <div\n className={cn('flex gap-4 max-md:flex-col', className)}\n {...props}\n />\n ),\n Column: ({\n className,\n ...props\n }: ComponentPropsWithoutRef<'div'>) => (\n <div className={cn('flex-1', className)} {...props} />\n ),\n ...overrides,\n },\n ...restOptions,\n }}\n >\n {cleanMarkdown ?? ''}\n </MarkdownProcessor>\n </TabProvider>\n </CodeProvider>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"MarkDownRender.mjs","names":["MarkdownRenderer: FC<MarkdownRendererProps>"],"sources":["../../../../src/components/MarkDownRender/MarkDownRender.tsx"],"sourcesContent":["import type { LocalesValues } from '@intlayer/types';\nimport type { ComponentProps, ComponentPropsWithoutRef, FC } from 'react';\nimport type { BundledLanguage } from 'shiki/bundle/web';\nimport { cn } from '../../utils/cn';\nimport { H1, H2, H3, H4, H5, H6 } from '../Headers';\nimport type { CodeCompAttributes } from '../IDE/Code';\nimport { Code } from '../IDE/Code';\nimport { CodeProvider } from '../IDE/CodeContext';\nimport { Link } from '../Link';\nimport { Tab } from '../Tab';\nimport { TabProvider } from '../Tab/TabContext';\nimport { Table } from '../Table';\nimport { MarkdownProcessor, type MarkdownProcessorOptions } from './processor';\n\ntype MarkdownRendererProps = {\n children: string;\n isDarkMode?: boolean;\n locale?: LocalesValues;\n options?: MarkdownProcessorOptions;\n};\n\n/**\n * Removes frontmatter from markdown content\n * Frontmatter is the YAML metadata block at the beginning of markdown files\n * delimited by --- at the start and end\n */\nconst stripFrontmatter = (markdown: string): string => {\n const lines = markdown.split(/\\r?\\n/);\n\n // Check if the very first non-empty line is the metadata start delimiter\n const firstNonEmptyLine = lines.find((line) => line.trim() !== '');\n\n if (!firstNonEmptyLine || firstNonEmptyLine.trim() !== '---') {\n // No frontmatter, return original content\n return markdown;\n }\n\n let inMetadataBlock = false;\n let endOfMetadataIndex = -1;\n\n for (let i = 0; i < lines.length; i++) {\n const trimmedLine = lines[i].trim();\n\n // Toggle metadata block on encountering the delimiter\n if (trimmedLine === '---') {\n if (!inMetadataBlock) {\n // Begin metadata block\n inMetadataBlock = true;\n } else {\n // End of metadata block\n endOfMetadataIndex = i;\n break;\n }\n }\n }\n\n if (endOfMetadataIndex > -1) {\n // Return content after the frontmatter\n return lines.slice(endOfMetadataIndex + 1).join('\\n');\n }\n\n // If we couldn't find the end delimiter, return original content\n return markdown;\n};\n\n/**\n * MarkdownRenderer Component\n *\n * A comprehensive markdown renderer that transforms markdown text into rich,\n * interactive HTML with custom styling and Intlayer integration. Supports\n * code syntax highlighting, responsive tables, internationalized links,\n * and automatic frontmatter stripping.\n *\n * @component\n * @example\n * Basic usage:\n * ```tsx\n * const markdownContent = `\n * # Hello World\n * This is **bold** and *italic* text.\n * `;\n *\n * <MarkdownRenderer>{markdownContent}</MarkdownRenderer>\n * ```\n *\n * @example\n * With dark mode:\n * ```tsx\n * const codeExample = `\n * # API Documentation\n * \\`\\`\\`javascript\n * const response = await fetch('/api/data');\n * const data = await response.json();\n * \\`\\`\\`\n * `;\n *\n * <MarkdownRenderer isDarkMode={true}>\n * {codeExample}\n * </MarkdownRenderer>\n * ```\n *\n * @example\n * With internationalized links:\n * ```tsx\n * const content = `\n * Visit our [documentation](/docs) for more information.\n * External link: [Google](https://google.com)\n * `;\n *\n * <MarkdownRenderer locale=\"fr\">\n * {content}\n * </MarkdownRenderer>\n * ```\n *\n * @example\n * With custom overrides:\n * ```tsx\n * const customOptions = {\n * overrides: {\n * h1: ({ children }) => (\n * <h1 className=\"custom-title\">{children}</h1>\n * ),\n * },\n * };\n *\n * <MarkdownRenderer options={customOptions}>\n * {markdownContent}\n * </MarkdownRenderer>\n * ```\n *\n * @example\n * With frontmatter (automatically stripped):\n * ```tsx\n * const blogPost = `\n * ---\n * title: \"My Blog Post\"\n * date: \"2023-12-01\"\n * author: \"John Doe\"\n * ---\n * # My Blog Post\n * This content will be rendered without the frontmatter.\n * `;\n *\n * <MarkdownRenderer>{blogPost}</MarkdownRenderer>\n * ```\n *\n * Features:\n * - Automatic frontmatter detection and removal\n * - Syntax-highlighted code blocks with theme support\n * - Clickable headers with anchor links\n * - Internationalized link handling with locale awareness\n * - Responsive tables with hover effects\n * - Custom blockquote, list, and image styling\n * - External link detection and security attributes\n * - Inline code with distinctive styling\n * - Lazy-loaded images with GitHub raw URL support\n * - Horizontal rules with custom styling\n *\n * Supported Markdown Elements:\n * - Headers (h1-h4) with click-to-anchor functionality\n * - Code blocks with language-specific syntax highlighting\n * - Inline code with background styling\n * - Blockquotes with custom border and padding\n * - Ordered and unordered lists with custom spacing\n * - Links (internal and external) with security attributes\n * - Images with lazy loading and responsive sizing\n * - Tables with hover effects and proper styling\n * - Horizontal rules with custom appearance\n * - All standard markdown formatting (bold, italic, etc.)\n *\n * Code Block Support:\n * - Language detection from code fence info\n * - Syntax highlighting through Code component\n * - Dark/light mode theme switching\n * - Line numbers and copy functionality (via Code component)\n * - Support for popular languages (JS, TS, Python, etc.)\n *\n * Link Handling:\n * - Automatic external link detection (starts with 'http')\n * - Security attributes for external links (rel=\"noopener noreferrer\")\n * - Locale-aware internal link routing\n * - Custom Link component integration\n * - Underlined styling for better visibility\n *\n * Image Processing:\n * - Automatic lazy loading for performance\n * - GitHub raw URL transformation for repository images\n * - Responsive sizing with max-width constraints\n * - Rounded corners for visual appeal\n *\n * Accessibility:\n * - Semantic HTML structure with proper heading hierarchy\n * - ARIA attributes through component integration\n * - Screen reader friendly link descriptions\n * - Keyboard navigation support\n * - High contrast styling options\n *\n * Performance:\n * - Lazy loading for images\n * - Efficient re-rendering with React memo patterns\n * - Code syntax highlighting with minimal bundle impact\n * - Optimized markdown parsing with markdown-to-jsx\n *\n * @param props - Component props\n * @param props.children - Raw markdown content to render\n * @param props.isDarkMode - Enable dark mode styling for code blocks\n * @param props.locale - Current locale for link internationalization\n * @param props.options - Additional markdown-to-jsx options\n * @param props.options.overrides - Custom component overrides for markdown elements\n *\n * @returns Rendered markdown content with custom styling and functionality\n */\nexport const MarkdownRenderer: FC<MarkdownRendererProps> = ({\n children,\n isDarkMode,\n locale,\n options,\n}) => {\n const { overrides, ...restOptions } = options ?? {};\n\n // Strip frontmatter from the markdown content before rendering\n const cleanMarkdown = stripFrontmatter(children);\n\n return (\n <CodeProvider>\n <TabProvider>\n <MarkdownProcessor\n options={{\n overrides: {\n h1: (props: ComponentProps<typeof H1>) => (\n <H1 isClickable={true} {...props} />\n ),\n h2: (props: ComponentProps<typeof H2>) => (\n <H2 isClickable={true} className=\"mt-16\" {...props} />\n ),\n h3: (props: ComponentProps<typeof H3>) => (\n <H3 isClickable={true} className=\"mt-5\" {...props} />\n ),\n h4: (props: ComponentProps<typeof H4>) => (\n <H4 isClickable={true} className=\"mt-3\" {...props} />\n ),\n h5: (props: ComponentProps<typeof H5>) => (\n <H5 isClickable={true} className=\"mt-3\" {...props} />\n ),\n h6: (props: ComponentProps<typeof H6>) => (\n <H6 isClickable={true} className=\"mt-3\" {...props} />\n ),\n\n code: (\n props: Omit<ComponentPropsWithoutRef<'code'>, 'children'> &\n Partial<CodeCompAttributes> & { children: string }\n ) =>\n !props.className ? (\n <strong className=\"rounded bg-card/60 box-decoration-clone px-1 py-0.5\">\n {props.children}\n </strong>\n ) : (\n <Code\n {...props}\n isDarkMode={isDarkMode}\n language={\n (props.className?.replace('lang-', '') ||\n 'plaintext') as BundledLanguage\n }\n fileName={props.fileName}\n showHeader={Boolean(\n props.fileName ||\n props.packageManager ||\n props.codeFormat ||\n props.contentDeclarationFormat\n )}\n />\n ),\n\n blockquote: ({\n className,\n ...props\n }: ComponentPropsWithoutRef<'blockquote'>) => (\n <blockquote\n className={cn(\n 'mt-5 gap-3 border-card border-l-4 pl-5 text-neutral',\n className\n )}\n {...props}\n />\n ),\n ul: ({ className, ...props }: ComponentPropsWithoutRef<'ul'>) => (\n <ul\n className={cn(\n 'mt-5 flex list-disc flex-col gap-3 pl-5',\n className\n )}\n {...props}\n />\n ),\n ol: ({ className, ...props }: ComponentPropsWithoutRef<'ol'>) => (\n <ol\n className={cn(\n 'mt-5 flex list-decimal flex-col gap-3 pl-5',\n className\n )}\n {...props}\n />\n ),\n img: ({\n className,\n ...props\n }: ComponentPropsWithoutRef<'img'>) => (\n <img\n {...props}\n loading=\"lazy\"\n className={cn(\n 'max-h-[80vh] max-w-full rounded-md',\n className\n )}\n src={`${props.src}?raw=true`}\n />\n ),\n a: (props: ComponentProps<typeof Link>) => (\n <Link\n color=\"neutral\"\n isExternalLink={props.href?.startsWith('http')}\n underlined={true}\n locale={locale}\n {...props}\n />\n ),\n pre: (props: ComponentPropsWithoutRef<'pre'>) => props.children,\n\n table: (props: ComponentProps<typeof Table>) => (\n <Table isRollable={true} {...props} />\n ),\n th: ({ className, ...props }: ComponentPropsWithoutRef<'th'>) => (\n <th\n className={cn(\n 'border-neutral border-b bg-neutral/10 p-4',\n className\n )}\n {...props}\n />\n ),\n tr: ({ className, ...props }: ComponentPropsWithoutRef<'tr'>) => (\n <tr\n className={cn('hover:/10 hover:bg-neutral/10', className)}\n {...props}\n />\n ),\n td: ({ className, ...props }: ComponentPropsWithoutRef<'td'>) => (\n <td\n className={cn(\n 'border-neutral-500/50 border-b p-4',\n className\n )}\n {...props}\n />\n ),\n hr: ({ className, ...props }: ComponentPropsWithoutRef<'hr'>) => (\n <hr\n className={cn('mx-6 mt-16 text-neutral', className)}\n {...props}\n />\n ),\n // Support both <Tab> and <Tabs> in markdown\n Tabs: (props: ComponentProps<typeof Tab>) => <Tab {...props} />,\n Tab: (props: ComponentProps<typeof Tab>) => <Tab {...props} />,\n TabItem: (props: ComponentProps<typeof Tab.Item>) => (\n <Tab.Item {...props} />\n ),\n Columns: ({\n className,\n ...props\n }: ComponentPropsWithoutRef<'div'>) => (\n <div\n className={cn('flex gap-4 max-md:flex-col', className)}\n {...props}\n />\n ),\n Column: ({\n className,\n ...props\n }: ComponentPropsWithoutRef<'div'>) => (\n <div className={cn('flex-1', className)} {...props} />\n ),\n ...overrides,\n },\n ...restOptions,\n }}\n >\n {cleanMarkdown ?? ''}\n </MarkdownProcessor>\n </TabProvider>\n </CodeProvider>\n );\n};\n"],"mappings":"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"}
@@ -34,7 +34,7 @@ let ModalSize = /* @__PURE__ */ function(ModalSize$1) {
34
34
  * Class name variants for different modal sizes using class-variance-authority
35
35
  * Defines responsive sizing and scrollable content for modal containers
36
36
  */
37
- const modalVariants = cva("flex cursor-default flex-col overflow-hidden p-3 shadow-sm", {
37
+ const modalVariants = cva("flex cursor-default flex-col p-3 shadow-sm", {
38
38
  variants: { size: {
39
39
  sm: "h-auto max-h-[30vh] w-[95vw] max-w-xl",
40
40
  md: "h-auto max-h-[50vh] w-[95vw] max-w-xl",
@@ -190,7 +190,7 @@ const Modal = ({ children, isOpen, container, disableScroll = true, onClose, has
190
190
  size: ButtonSize.ICON_MD
191
191
  })]
192
192
  }), /* @__PURE__ */ jsx("div", {
193
- className: "flex w-full flex-1 flex-col overflow-hidden",
193
+ className: "flex w-full flex-1 flex-col",
194
194
  children
195
195
  })]
196
196
  })
@@ -1 +1 @@
1
- {"version":3,"file":"Modal.mjs","names":["m","Modal: FC<ModalProps>"],"sources":["../../../../src/components/Modal/Modal.tsx"],"sourcesContent":["'use client';\n\nimport { cva } from 'class-variance-authority';\nimport { motion as m } from 'framer-motion';\nimport { X } from 'lucide-react';\nimport { type FC, type ReactNode, useEffect } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useGetElementOrWindow, useScrollBlockage } from '../../hooks/index';\nimport { cn } from '../../utils/cn';\nimport { Button, ButtonColor, ButtonSize, ButtonVariant } from '../Button';\nimport { Container, type ContainerProps } from '../Container';\nimport { H3 } from '../Headers';\n\n/**\n * Enumeration of available modal sizes\n */\nexport enum ModalSize {\n /** Small modal: max height 30vh, max width xl */\n SM = 'sm',\n /** Medium modal: max height 50vh, max width xl */\n MD = 'md',\n /** Large modal: max height 70vh, max width 2xl */\n LG = 'lg',\n /** Extra large modal: max height 95vh, max width 6xl */\n XL = 'xl',\n /** Unset size: max height 95vh, full width responsive */\n UNSET = 'unset',\n}\n\n/**\n * Props for the Modal component\n */\ntype ModalProps = {\n /** Content to be displayed inside the modal */\n children: ReactNode;\n /** Controls whether the modal is visible */\n isOpen: boolean;\n /** Callback function called when the modal should be closed */\n onClose?: () => void;\n /** Container element to render the modal into (defaults to document.body) */\n container?: HTMLElement;\n /** Whether to disable scrolling on the background content */\n disableScroll?: boolean;\n /** Whether to display a close button in the modal header */\n hasCloseButton?: boolean;\n /** Optional title displayed at the top of the modal */\n title?: string;\n /** Size variant for the modal */\n size?: ModalSize | `${ModalSize}`;\n} & Pick<\n ContainerProps,\n | 'className'\n | 'transparency'\n | 'border'\n | 'background'\n | 'roundedSize'\n | 'borderColor'\n | 'padding'\n | 'separator'\n | 'gap'\n>;\n\n/**\n * Class name variants for different modal sizes using class-variance-authority\n * Defines responsive sizing and scrollable content for modal containers\n */\nconst modalVariants = cva(\n 'flex cursor-default flex-col overflow-hidden p-3 shadow-sm',\n {\n variants: {\n size: {\n /** Small modal: height auto, max-height 30vh, width 95vw, max-width xl */\n sm: 'h-auto max-h-[30vh] w-[95vw] max-w-xl',\n /** Medium modal: height auto, max-height 50vh, width 95vw, max-width xl */\n md: 'h-auto max-h-[50vh] w-[95vw] max-w-xl',\n /** Large modal: height auto, max-height 70vh, width 95vw, max-width 2xl */\n lg: 'h-auto max-h-[70vh] w-[95vw] max-w-2xl',\n /** Extra large modal: height auto, max-height 95vh, width 95vw, max-width 6xl */\n xl: 'h-auto max-h-[95vh] w-[95vw] max-w-6xl',\n /** Unset modal: height auto, max-height 95vh, width 95vw, no max-width */\n unset: 'h-auto max-h-[95vh] w-[95vw]',\n },\n },\n defaultVariants: {\n size: 'unset',\n },\n }\n);\n\n/**\n * Motion-enabled modal component with Framer Motion support\n * Extends Container component with motion capabilities for animations\n */\nconst MotionModal = m.create(Container);\n\n/**\n * Modal Component\n *\n * A highly customizable modal dialog component with portal rendering, Framer Motion animations,\n * and comprehensive accessibility features. Supports multiple size variants and scroll management.\n *\n * Features:\n * - Portal rendering to any container element (defaults to document.body)\n * - Smooth animations with Framer Motion\n * - Size variants: SM, MD, LG, XL, UNSET with responsive sizing\n * - Optional title and close button\n * - Background scroll prevention\n * - Click-outside-to-close functionality\n * - Full accessibility support with ARIA attributes\n * - Keyboard navigation support (ESC to close)\n * - Extensible styling with Container props\n *\n * @example\n * Basic usage:\n * ```jsx\n * <Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)}>\n * <p>Modal content goes here</p>\n * </Modal>\n * ```\n *\n * @example\n * With title and close button:\n * ```jsx\n * <Modal\n * isOpen={isOpen}\n * onClose={onClose}\n * title=\"Confirm Action\"\n * hasCloseButton\n * size={ModalSize.LG}\n * >\n * <div>\n * <p>Are you sure you want to continue?</p>\n * <Button onClick={onConfirm}>Confirm</Button>\n * </div>\n * </Modal>\n * ```\n *\n * @example\n * Custom container and styling:\n * ```jsx\n * <Modal\n * isOpen={isOpen}\n * onClose={onClose}\n * container={customContainer}\n * background=\"card\"\n * padding=\"lg\"\n * border=\"default\"\n * >\n * Content with custom styling\n * </Modal>\n * ```\n *\n * Accessibility Notes:\n * - Modal receives focus when opened\n * - Background content is hidden from screen readers when modal is open\n * - ESC key closes modal (handled by browser for role=\"dialog\")\n * - Click outside modal closes it\n * - Close button has descriptive label for screen readers\n *\n * @param props - Modal component props\n * @returns JSX element rendered via createPortal\n */\nexport const Modal: FC<ModalProps> = ({\n children,\n isOpen,\n container,\n disableScroll = true,\n onClose,\n hasCloseButton = false,\n title,\n size = ModalSize.MD,\n className,\n ...props\n}) => {\n const containerElement = useGetElementOrWindow(container);\n\n useScrollBlockage({ key: 'modal', disableScroll: isOpen && disableScroll });\n\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && isOpen && onClose) {\n onClose();\n }\n };\n\n document.addEventListener('keydown', handleEscape);\n\n return () => {\n document.removeEventListener('keydown', handleEscape);\n };\n }, [isOpen, onClose]);\n\n if (!containerElement) return <></>;\n\n const hasTitle = typeof title === 'string';\n\n return createPortal(\n <m.div\n className=\"invisible fixed top-0 left-0 z-50 flex size-full cursor-pointer items-center justify-center overflow-auto bg-background/40 backdrop-blur\"\n animate={isOpen ? 'visible' : 'invisible'}\n variants={{\n visible: {\n opacity: 1,\n visibility: 'visible',\n transition: { duration: 0.1, when: 'beforeChildren' },\n },\n invisible: {\n opacity: 0,\n visibility: 'hidden',\n transition: { duration: 0.1, when: 'afterChildren' },\n },\n }}\n onClick={(e) => {\n e.stopPropagation();\n onClose?.();\n }}\n aria-hidden={!isOpen}\n >\n <MotionModal\n onClick={(e) => e.stopPropagation()}\n initial={{ scale: isOpen ? 0.5 : 1 }}\n animate={{ scale: isOpen ? 1 : 0.5 }}\n transition={{ duration: 0.3 }}\n className={modalVariants({\n size,\n className,\n })}\n role=\"dialog\"\n aria-modal\n roundedSize=\"2xl\"\n {...props}\n >\n <div\n className={cn(\n 'cursor-default',\n hasCloseButton && hasTitle\n ? `flex items-start`\n : hasCloseButton\n ? `flex justify-end`\n : hasTitle\n ? `items-center`\n : `hidden`\n )}\n >\n {hasTitle && (\n <H3 className=\"mt-2 mb-4 ml-4 flex items-center justify-center font-bold text-lg\">\n {title}\n </H3>\n )}\n {hasCloseButton && (\n <Button\n variant={ButtonVariant.HOVERABLE}\n color={ButtonColor.TEXT}\n label=\"Close modal\"\n className=\"ml-auto\"\n onClick={(e) => {\n e.stopPropagation();\n onClose?.();\n }}\n Icon={X}\n size={ButtonSize.ICON_MD}\n />\n )}\n </div>\n <div className=\"flex w-full flex-1 flex-col overflow-hidden\">\n {children}\n </div>\n </MotionModal>\n </m.div>,\n containerElement\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAgBA,IAAY,kDAAL;;AAEL;;AAEA;;AAEA;;AAEA;;AAEA;;;;;;;AAwCF,MAAM,gBAAgB,IACpB,8DACA;CACE,UAAU,EACR,MAAM;EAEJ,IAAI;EAEJ,IAAI;EAEJ,IAAI;EAEJ,IAAI;EAEJ,OAAO;EACR,EACF;CACD,iBAAiB,EACf,MAAM,SACP;CACF,CACF;;;;;AAMD,MAAM,cAAcA,OAAE,OAAO,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqEvC,MAAaC,SAAyB,EACpC,UACA,QACA,WACA,gBAAgB,MAChB,SACA,iBAAiB,OACjB,OACA,OAAO,UAAU,IACjB,WACA,GAAG,YACC;CACJ,MAAM,mBAAmB,sBAAsB,UAAU;AAEzD,mBAAkB;EAAE,KAAK;EAAS,eAAe,UAAU;EAAe,CAAC;AAE3E,iBAAgB;EACd,MAAM,gBAAgB,UAAyB;AAC7C,OAAI,MAAM,QAAQ,YAAY,UAAU,QACtC,UAAS;;AAIb,WAAS,iBAAiB,WAAW,aAAa;AAElD,eAAa;AACX,YAAS,oBAAoB,WAAW,aAAa;;IAEtD,CAAC,QAAQ,QAAQ,CAAC;AAErB,KAAI,CAAC,iBAAkB,QAAO,mCAAK;CAEnC,MAAM,WAAW,OAAO,UAAU;AAElC,QAAO,aACL,oBAACD,OAAE;EACD,WAAU;EACV,SAAS,SAAS,YAAY;EAC9B,UAAU;GACR,SAAS;IACP,SAAS;IACT,YAAY;IACZ,YAAY;KAAE,UAAU;KAAK,MAAM;KAAkB;IACtD;GACD,WAAW;IACT,SAAS;IACT,YAAY;IACZ,YAAY;KAAE,UAAU;KAAK,MAAM;KAAiB;IACrD;GACF;EACD,UAAU,MAAM;AACd,KAAE,iBAAiB;AACnB,cAAW;;EAEb,eAAa,CAAC;YAEd,qBAAC;GACC,UAAU,MAAM,EAAE,iBAAiB;GACnC,SAAS,EAAE,OAAO,SAAS,KAAM,GAAG;GACpC,SAAS,EAAE,OAAO,SAAS,IAAI,IAAK;GACpC,YAAY,EAAE,UAAU,IAAK;GAC7B,WAAW,cAAc;IACvB;IACA;IACD,CAAC;GACF,MAAK;GACL;GACA,aAAY;GACZ,GAAI;cAEJ,qBAAC;IACC,WAAW,GACT,kBACA,kBAAkB,WACd,qBACA,iBACE,qBACA,WACE,iBACA,SACT;eAEA,YACC,oBAAC;KAAG,WAAU;eACX;MACE,EAEN,kBACC,oBAAC;KACC,SAAS,cAAc;KACvB,OAAO,YAAY;KACnB,OAAM;KACN,WAAU;KACV,UAAU,MAAM;AACd,QAAE,iBAAiB;AACnB,iBAAW;;KAEb,MAAM;KACN,MAAM,WAAW;MACjB;KAEA,EACN,oBAAC;IAAI,WAAU;IACZ;KACG;IACM;GACR,EACR,iBACD"}
1
+ {"version":3,"file":"Modal.mjs","names":["m","Modal: FC<ModalProps>"],"sources":["../../../../src/components/Modal/Modal.tsx"],"sourcesContent":["'use client';\n\nimport { cva } from 'class-variance-authority';\nimport { motion as m } from 'framer-motion';\nimport { X } from 'lucide-react';\nimport { type FC, type ReactNode, useEffect } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useGetElementOrWindow, useScrollBlockage } from '../../hooks/index';\nimport { cn } from '../../utils/cn';\nimport { Button, ButtonColor, ButtonSize, ButtonVariant } from '../Button';\nimport { Container, type ContainerProps } from '../Container';\nimport { H3 } from '../Headers';\n\n/**\n * Enumeration of available modal sizes\n */\nexport enum ModalSize {\n /** Small modal: max height 30vh, max width xl */\n SM = 'sm',\n /** Medium modal: max height 50vh, max width xl */\n MD = 'md',\n /** Large modal: max height 70vh, max width 2xl */\n LG = 'lg',\n /** Extra large modal: max height 95vh, max width 6xl */\n XL = 'xl',\n /** Unset size: max height 95vh, full width responsive */\n UNSET = 'unset',\n}\n\n/**\n * Props for the Modal component\n */\ntype ModalProps = {\n /** Content to be displayed inside the modal */\n children: ReactNode;\n /** Controls whether the modal is visible */\n isOpen: boolean;\n /** Callback function called when the modal should be closed */\n onClose?: () => void;\n /** Container element to render the modal into (defaults to document.body) */\n container?: HTMLElement;\n /** Whether to disable scrolling on the background content */\n disableScroll?: boolean;\n /** Whether to display a close button in the modal header */\n hasCloseButton?: boolean;\n /** Optional title displayed at the top of the modal */\n title?: string;\n /** Size variant for the modal */\n size?: ModalSize | `${ModalSize}`;\n} & Pick<\n ContainerProps,\n | 'className'\n | 'transparency'\n | 'border'\n | 'background'\n | 'roundedSize'\n | 'borderColor'\n | 'padding'\n | 'separator'\n | 'gap'\n>;\n\n/**\n * Class name variants for different modal sizes using class-variance-authority\n * Defines responsive sizing and scrollable content for modal containers\n */\nconst modalVariants = cva('flex cursor-default flex-col p-3 shadow-sm', {\n variants: {\n size: {\n /** Small modal: height auto, max-height 30vh, width 95vw, max-width xl */\n sm: 'h-auto max-h-[30vh] w-[95vw] max-w-xl',\n /** Medium modal: height auto, max-height 50vh, width 95vw, max-width xl */\n md: 'h-auto max-h-[50vh] w-[95vw] max-w-xl',\n /** Large modal: height auto, max-height 70vh, width 95vw, max-width 2xl */\n lg: 'h-auto max-h-[70vh] w-[95vw] max-w-2xl',\n /** Extra large modal: height auto, max-height 95vh, width 95vw, max-width 6xl */\n xl: 'h-auto max-h-[95vh] w-[95vw] max-w-6xl',\n /** Unset modal: height auto, max-height 95vh, width 95vw, no max-width */\n unset: 'h-auto max-h-[95vh] w-[95vw]',\n },\n },\n defaultVariants: {\n size: 'unset',\n },\n});\n\n/**\n * Motion-enabled modal component with Framer Motion support\n * Extends Container component with motion capabilities for animations\n */\nconst MotionModal = m.create(Container);\n\n/**\n * Modal Component\n *\n * A highly customizable modal dialog component with portal rendering, Framer Motion animations,\n * and comprehensive accessibility features. Supports multiple size variants and scroll management.\n *\n * Features:\n * - Portal rendering to any container element (defaults to document.body)\n * - Smooth animations with Framer Motion\n * - Size variants: SM, MD, LG, XL, UNSET with responsive sizing\n * - Optional title and close button\n * - Background scroll prevention\n * - Click-outside-to-close functionality\n * - Full accessibility support with ARIA attributes\n * - Keyboard navigation support (ESC to close)\n * - Extensible styling with Container props\n *\n * @example\n * Basic usage:\n * ```jsx\n * <Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)}>\n * <p>Modal content goes here</p>\n * </Modal>\n * ```\n *\n * @example\n * With title and close button:\n * ```jsx\n * <Modal\n * isOpen={isOpen}\n * onClose={onClose}\n * title=\"Confirm Action\"\n * hasCloseButton\n * size={ModalSize.LG}\n * >\n * <div>\n * <p>Are you sure you want to continue?</p>\n * <Button onClick={onConfirm}>Confirm</Button>\n * </div>\n * </Modal>\n * ```\n *\n * @example\n * Custom container and styling:\n * ```jsx\n * <Modal\n * isOpen={isOpen}\n * onClose={onClose}\n * container={customContainer}\n * background=\"card\"\n * padding=\"lg\"\n * border=\"default\"\n * >\n * Content with custom styling\n * </Modal>\n * ```\n *\n * Accessibility Notes:\n * - Modal receives focus when opened\n * - Background content is hidden from screen readers when modal is open\n * - ESC key closes modal (handled by browser for role=\"dialog\")\n * - Click outside modal closes it\n * - Close button has descriptive label for screen readers\n *\n * @param props - Modal component props\n * @returns JSX element rendered via createPortal\n */\nexport const Modal: FC<ModalProps> = ({\n children,\n isOpen,\n container,\n disableScroll = true,\n onClose,\n hasCloseButton = false,\n title,\n size = ModalSize.MD,\n className,\n ...props\n}) => {\n const containerElement = useGetElementOrWindow(container);\n\n useScrollBlockage({ key: 'modal', disableScroll: isOpen && disableScroll });\n\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && isOpen && onClose) {\n onClose();\n }\n };\n\n document.addEventListener('keydown', handleEscape);\n\n return () => {\n document.removeEventListener('keydown', handleEscape);\n };\n }, [isOpen, onClose]);\n\n if (!containerElement) return <></>;\n\n const hasTitle = typeof title === 'string';\n\n return createPortal(\n <m.div\n className=\"invisible fixed top-0 left-0 z-50 flex size-full cursor-pointer items-center justify-center overflow-auto bg-background/40 backdrop-blur\"\n animate={isOpen ? 'visible' : 'invisible'}\n variants={{\n visible: {\n opacity: 1,\n visibility: 'visible',\n transition: { duration: 0.1, when: 'beforeChildren' },\n },\n invisible: {\n opacity: 0,\n visibility: 'hidden',\n transition: { duration: 0.1, when: 'afterChildren' },\n },\n }}\n onClick={(e) => {\n e.stopPropagation();\n onClose?.();\n }}\n aria-hidden={!isOpen}\n >\n <MotionModal\n onClick={(e) => e.stopPropagation()}\n initial={{ scale: isOpen ? 0.5 : 1 }}\n animate={{ scale: isOpen ? 1 : 0.5 }}\n transition={{ duration: 0.3 }}\n className={modalVariants({\n size,\n className,\n })}\n role=\"dialog\"\n aria-modal\n roundedSize=\"2xl\"\n {...props}\n >\n <div\n className={cn(\n 'cursor-default',\n hasCloseButton && hasTitle\n ? `flex items-start`\n : hasCloseButton\n ? `flex justify-end`\n : hasTitle\n ? `items-center`\n : `hidden`\n )}\n >\n {hasTitle && (\n <H3 className=\"mt-2 mb-4 ml-4 flex items-center justify-center font-bold text-lg\">\n {title}\n </H3>\n )}\n {hasCloseButton && (\n <Button\n variant={ButtonVariant.HOVERABLE}\n color={ButtonColor.TEXT}\n label=\"Close modal\"\n className=\"ml-auto\"\n onClick={(e) => {\n e.stopPropagation();\n onClose?.();\n }}\n Icon={X}\n size={ButtonSize.ICON_MD}\n />\n )}\n </div>\n <div className=\"flex w-full flex-1 flex-col\">{children}</div>\n </MotionModal>\n </m.div>,\n containerElement\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAgBA,IAAY,kDAAL;;AAEL;;AAEA;;AAEA;;AAEA;;AAEA;;;;;;;AAwCF,MAAM,gBAAgB,IAAI,8CAA8C;CACtE,UAAU,EACR,MAAM;EAEJ,IAAI;EAEJ,IAAI;EAEJ,IAAI;EAEJ,IAAI;EAEJ,OAAO;EACR,EACF;CACD,iBAAiB,EACf,MAAM,SACP;CACF,CAAC;;;;;AAMF,MAAM,cAAcA,OAAE,OAAO,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqEvC,MAAaC,SAAyB,EACpC,UACA,QACA,WACA,gBAAgB,MAChB,SACA,iBAAiB,OACjB,OACA,OAAO,UAAU,IACjB,WACA,GAAG,YACC;CACJ,MAAM,mBAAmB,sBAAsB,UAAU;AAEzD,mBAAkB;EAAE,KAAK;EAAS,eAAe,UAAU;EAAe,CAAC;AAE3E,iBAAgB;EACd,MAAM,gBAAgB,UAAyB;AAC7C,OAAI,MAAM,QAAQ,YAAY,UAAU,QACtC,UAAS;;AAIb,WAAS,iBAAiB,WAAW,aAAa;AAElD,eAAa;AACX,YAAS,oBAAoB,WAAW,aAAa;;IAEtD,CAAC,QAAQ,QAAQ,CAAC;AAErB,KAAI,CAAC,iBAAkB,QAAO,mCAAK;CAEnC,MAAM,WAAW,OAAO,UAAU;AAElC,QAAO,aACL,oBAACD,OAAE;EACD,WAAU;EACV,SAAS,SAAS,YAAY;EAC9B,UAAU;GACR,SAAS;IACP,SAAS;IACT,YAAY;IACZ,YAAY;KAAE,UAAU;KAAK,MAAM;KAAkB;IACtD;GACD,WAAW;IACT,SAAS;IACT,YAAY;IACZ,YAAY;KAAE,UAAU;KAAK,MAAM;KAAiB;IACrD;GACF;EACD,UAAU,MAAM;AACd,KAAE,iBAAiB;AACnB,cAAW;;EAEb,eAAa,CAAC;YAEd,qBAAC;GACC,UAAU,MAAM,EAAE,iBAAiB;GACnC,SAAS,EAAE,OAAO,SAAS,KAAM,GAAG;GACpC,SAAS,EAAE,OAAO,SAAS,IAAI,IAAK;GACpC,YAAY,EAAE,UAAU,IAAK;GAC7B,WAAW,cAAc;IACvB;IACA;IACD,CAAC;GACF,MAAK;GACL;GACA,aAAY;GACZ,GAAI;cAEJ,qBAAC;IACC,WAAW,GACT,kBACA,kBAAkB,WACd,qBACA,iBACE,qBACA,WACE,iBACA,SACT;eAEA,YACC,oBAAC;KAAG,WAAU;eACX;MACE,EAEN,kBACC,oBAAC;KACC,SAAS,cAAc;KACvB,OAAO,YAAY;KACnB,OAAM;KACN,WAAU;KACV,UAAU,MAAM;AACd,QAAE,iBAAiB;AACnB,iBAAW;;KAEb,MAAM;KACN,MAAM,WAAW;MACjB;KAEA,EACN,oBAAC;IAAI,WAAU;IAA+B;KAAe;IACjD;GACR,EACR,iBACD"}