@intlayer/design-system 9.4.1 → 9.4.3
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.
- package/README.md +1 -1
- package/dist/esm/api/index.mjs +2 -2
- package/dist/esm/api/useUser/index.mjs +1 -1
- package/dist/esm/components/Avatar/index.mjs +1 -1
- package/dist/esm/components/Avatar/index.mjs.map +1 -1
- package/dist/esm/components/Badge/index.mjs +3 -3
- package/dist/esm/components/Badge/index.mjs.map +1 -1
- package/dist/esm/components/Breadcrumb/index.mjs +5 -5
- package/dist/esm/components/Breadcrumb/index.mjs.map +1 -1
- package/dist/esm/components/Browser/Browser.mjs +4 -4
- package/dist/esm/components/Browser/Browser.mjs.map +1 -1
- package/dist/esm/components/Button/Button.mjs +1 -0
- package/dist/esm/components/Button/Button.mjs.map +1 -1
- package/dist/esm/components/Carousel/index.mjs +5 -5
- package/dist/esm/components/Carousel/index.mjs.map +1 -1
- package/dist/esm/components/CollapsibleTable/CollapsibleTable.mjs +5 -5
- package/dist/esm/components/CollapsibleTable/CollapsibleTable.mjs.map +1 -1
- package/dist/esm/components/Container/index.mjs +2 -2
- package/dist/esm/components/Container/index.mjs.map +1 -1
- package/dist/esm/components/ContentEditor/ContentEditorTextArea.mjs +1 -1
- package/dist/esm/components/DictionaryEditor/ItemLayout.mjs +1 -1
- package/dist/esm/components/DictionaryEditor/ItemLayout.mjs.map +1 -1
- package/dist/esm/components/DictionaryFieldEditor/ContentEditorView/TextEditor.mjs +7 -7
- package/dist/esm/components/DictionaryFieldEditor/ContentEditorView/TextEditor.mjs.map +1 -1
- package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/DictionaryCreationForm.mjs +1 -1
- package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/DictionaryDetailsForm.mjs +9 -9
- package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/DictionaryDetailsForm.mjs.map +1 -1
- package/dist/esm/components/DictionaryFieldEditor/DictionaryFieldEditor.mjs +4 -4
- package/dist/esm/components/DictionaryFieldEditor/DictionaryFieldEditor.mjs.map +1 -1
- package/dist/esm/components/DictionaryFieldEditor/NavigationView/NavigationViewNode.mjs +1 -1
- package/dist/esm/components/DictionaryFieldEditor/{ContentEditor.mjs → NodeEditor.mjs} +9 -9
- package/dist/esm/components/DictionaryFieldEditor/NodeEditor.mjs.map +1 -0
- package/dist/esm/components/DictionaryFieldEditor/SaveForm/SaveForm.mjs +3 -3
- package/dist/esm/components/DictionaryFieldEditor/SaveForm/SaveForm.mjs.map +1 -1
- package/dist/esm/components/DictionaryFieldEditor/StructureView/StructureView.mjs +2 -2
- package/dist/esm/components/DictionaryFieldEditor/StructureView/StructureView.mjs.map +1 -1
- package/dist/esm/components/DictionaryFieldEditor/VersionSwitcherDropDown/VersionSwitcher.mjs +2 -2
- package/dist/esm/components/DictionaryFieldEditor/VersionSwitcherDropDown/VersionSwitcher.mjs.map +1 -1
- package/dist/esm/components/DictionaryFieldEditor/index.mjs +3 -3
- package/dist/esm/components/Footer/index.mjs +5 -3
- package/dist/esm/components/Footer/index.mjs.map +1 -1
- package/dist/esm/components/HeightResizer/index.mjs +1 -1
- package/dist/esm/components/HeightResizer/index.mjs.map +1 -1
- package/dist/esm/components/IDE/Code.mjs +1 -1
- package/dist/esm/components/IDE/Code.mjs.map +1 -1
- package/dist/esm/components/IDE/CodeBlockHighlight.mjs +1 -1
- package/dist/esm/components/IDE/CodeBlockHighlight.mjs.map +1 -1
- package/dist/esm/components/IDE/CodeBlockServer.mjs +1 -1
- package/dist/esm/components/IDE/CodeBlockServer.mjs.map +1 -1
- package/dist/esm/components/IDE/CodeBlockShiki.mjs +6 -48
- package/dist/esm/components/IDE/CodeBlockShiki.mjs.map +1 -1
- package/dist/esm/components/IDE/FileList.mjs +1 -1
- package/dist/esm/components/IDE/FileList.mjs.map +1 -1
- package/dist/esm/components/IDE/FileTree.mjs +1 -1
- package/dist/esm/components/IDE/FileTree.mjs.map +1 -1
- package/dist/esm/components/IDE/IDE.mjs +10 -7
- package/dist/esm/components/IDE/IDE.mjs.map +1 -1
- package/dist/esm/components/IDE/MarkDownRender.mjs +4 -3
- package/dist/esm/components/IDE/MarkDownRender.mjs.map +1 -1
- package/dist/esm/components/IDE/shikiBundle.mjs +61 -0
- package/dist/esm/components/IDE/shikiBundle.mjs.map +1 -0
- package/dist/esm/components/IDE/shikiLanguages.mjs +2 -39
- package/dist/esm/components/IDE/shikiLanguages.mjs.map +1 -1
- package/dist/esm/components/IDE/shikiThemes.mjs.map +1 -1
- package/dist/esm/components/Input/Checkbox.mjs +4 -4
- package/dist/esm/components/Input/Checkbox.mjs.map +1 -1
- package/dist/esm/components/Input/Input.mjs +2 -2
- package/dist/esm/components/Input/Input.mjs.map +1 -1
- package/dist/esm/components/Input/OTPInput.mjs +1 -1
- package/dist/esm/components/Input/OTPInput.mjs.map +1 -1
- package/dist/esm/components/KeyboardShortcut/KeyboardShortcut.mjs +2 -2
- package/dist/esm/components/KeyboardShortcut/KeyboardShortcut.mjs.map +1 -1
- package/dist/esm/components/LanguageBackground/LanguageSection.mjs +6 -8
- package/dist/esm/components/LanguageBackground/LanguageSection.mjs.map +1 -1
- package/dist/esm/components/LanguageBackground/index.mjs +2 -2
- package/dist/esm/components/LocaleSwitcherContentDropDown/LocaleSwitcherContent.mjs +2 -2
- package/dist/esm/components/LocaleSwitcherContentDropDown/LocaleSwitcherContent.mjs.map +1 -1
- package/dist/esm/components/MarkDownRender/MarkDownIframe.mjs +8 -3
- package/dist/esm/components/MarkDownRender/MarkDownIframe.mjs.map +1 -1
- package/dist/esm/components/MarkDownRender/MarkDownRender.mjs +9 -8
- package/dist/esm/components/MarkDownRender/MarkDownRender.mjs.map +1 -1
- package/dist/esm/components/MarkdownEditor/MarkdownEditor.mjs +2 -2
- package/dist/esm/components/MarkdownEditor/MarkdownEditor.mjs.map +1 -1
- package/dist/esm/components/MarkdownEditor/extensions.mjs +1 -1
- package/dist/esm/components/MarkdownEditor/extensions.mjs.map +1 -1
- package/dist/esm/components/MarkdownEditor/generative/AISelector.mjs +3 -3
- package/dist/esm/components/MarkdownEditor/generative/AISelector.mjs.map +1 -1
- package/dist/esm/components/MarkdownEditor/generative/AISelectorCommands.mjs +2 -2
- package/dist/esm/components/MarkdownEditor/generative/AISelectorCommands.mjs.map +1 -1
- package/dist/esm/components/MarkdownEditor/generative/GenerativeMenuSwitch.mjs +2 -2
- package/dist/esm/components/MarkdownEditor/generative/GenerativeMenuSwitch.mjs.map +1 -1
- package/dist/esm/components/MarkdownEditor/novel/extensions/code-block-shiki.mjs +197 -0
- package/dist/esm/components/MarkdownEditor/novel/extensions/code-block-shiki.mjs.map +1 -0
- package/dist/esm/components/MarkdownEditor/novel/extensions/index.mjs +2 -1
- package/dist/esm/components/MarkdownEditor/novel/extensions/index.mjs.map +1 -1
- package/dist/esm/components/MarkdownEditor/novel/index.mjs +2 -1
- package/dist/esm/components/Modal/Modal.mjs +0 -1
- package/dist/esm/components/Modal/Modal.mjs.map +1 -1
- package/dist/esm/components/Navbar/DesktopNavbar.mjs +3 -3
- package/dist/esm/components/Navbar/DesktopNavbar.mjs.map +1 -1
- package/dist/esm/components/Navbar/MobileNavbar.mjs +1 -1
- package/dist/esm/components/Navbar/MobileNavbar.mjs.map +1 -1
- package/dist/esm/components/Pattern/GridDistortionPattern.mjs +2 -2
- package/dist/esm/components/Pattern/GridDistortionPattern.mjs.map +1 -1
- package/dist/esm/components/Pattern/GridPattern.mjs +1 -1
- package/dist/esm/components/Pattern/GridPattern.mjs.map +1 -1
- package/dist/esm/components/RightDrawer/RightDrawer.mjs +2 -2
- package/dist/esm/components/RightDrawer/RightDrawer.mjs.map +1 -1
- package/dist/esm/components/Select/Multiselect.mjs +2 -2
- package/dist/esm/components/Select/Multiselect.mjs.map +1 -1
- package/dist/esm/components/Select/Select.mjs +2 -2
- package/dist/esm/components/Select/Select.mjs.map +1 -1
- package/dist/esm/components/Status/index.mjs +28 -0
- package/dist/esm/components/Status/index.mjs.map +1 -0
- package/dist/esm/components/Steps/index.mjs +3 -3
- package/dist/esm/components/Steps/index.mjs.map +1 -1
- package/dist/esm/components/SwitchSelector/SwitchSelector.mjs +12 -11
- package/dist/esm/components/SwitchSelector/SwitchSelector.mjs.map +1 -1
- package/dist/esm/components/SwitchSelector/VerticalSwitchSelector.mjs +6 -6
- package/dist/esm/components/SwitchSelector/VerticalSwitchSelector.mjs.map +1 -1
- package/dist/esm/components/Tab/Tab.mjs +1 -1
- package/dist/esm/components/Tab/Tab.mjs.map +1 -1
- package/dist/esm/components/TabSelector/TabSelector.mjs +5 -5
- package/dist/esm/components/TabSelector/TabSelector.mjs.map +1 -1
- package/dist/esm/components/Table/SmartTable.mjs +2 -2
- package/dist/esm/components/Table/SmartTable.mjs.map +1 -1
- package/dist/esm/components/Table/Table.mjs +1 -1
- package/dist/esm/components/Table/Table.mjs.map +1 -1
- package/dist/esm/components/Table/TableElements.mjs +2 -2
- package/dist/esm/components/Table/TableElements.mjs.map +1 -1
- package/dist/esm/components/Tag/index.mjs +1 -1
- package/dist/esm/components/Tag/index.mjs.map +1 -1
- package/dist/esm/components/TechLogo/TechLogo.mjs +2 -0
- package/dist/esm/components/TechLogo/TechLogo.mjs.map +1 -1
- package/dist/esm/components/TechLogo/logos/HTMX.mjs +31 -0
- package/dist/esm/components/TechLogo/logos/HTMX.mjs.map +1 -0
- package/dist/esm/components/TechLogo/logos/Remix.mjs +22 -0
- package/dist/esm/components/TechLogo/logos/Remix.mjs.map +1 -0
- package/dist/esm/components/TechLogo/techLogos.mjs +5 -1
- package/dist/esm/components/TechLogo/techLogos.mjs.map +1 -1
- package/dist/esm/components/Terminal/Terminal.mjs +4 -4
- package/dist/esm/components/Terminal/Terminal.mjs.map +1 -1
- package/dist/esm/components/TextArea/TextArea.mjs.map +1 -1
- package/dist/esm/components/Toaster/Toast.mjs +4 -4
- package/dist/esm/components/Toaster/Toast.mjs.map +1 -1
- package/dist/esm/components/index.mjs +8 -7
- package/dist/esm/hooks/usePersistedStore.mjs +6 -18
- package/dist/esm/hooks/usePersistedStore.mjs.map +1 -1
- package/dist/esm/providers/ReactQueryProvider.mjs +10 -2
- package/dist/esm/providers/ReactQueryProvider.mjs.map +1 -1
- package/dist/esm/providers/ThemeProvider.mjs +27 -15
- package/dist/esm/providers/ThemeProvider.mjs.map +1 -1
- package/dist/esm/routes.mjs +24 -6
- package/dist/esm/routes.mjs.map +1 -1
- package/dist/esm/structured-data/buildItemListJsonLd.mjs +27 -0
- package/dist/esm/structured-data/buildItemListJsonLd.mjs.map +1 -0
- package/dist/esm/structured-data/index.mjs +2 -1
- package/dist/types/components/Badge/index.d.ts.map +1 -1
- package/dist/types/components/Button/Button.d.ts +2 -2
- package/dist/types/components/Button/Button.d.ts.map +1 -1
- package/dist/types/components/Container/index.d.ts.map +1 -1
- package/dist/types/components/DictionaryFieldEditor/ContentEditorView/TextEditor.d.ts.map +1 -1
- package/dist/types/components/DictionaryFieldEditor/NodeEditor.d.ts +10 -0
- package/dist/types/components/DictionaryFieldEditor/NodeEditor.d.ts.map +1 -0
- package/dist/types/components/DictionaryFieldEditor/index.d.ts +2 -2
- package/dist/types/components/Footer/index.d.ts.map +1 -1
- package/dist/types/components/Form/FormBase.d.ts +1 -1
- package/dist/types/components/IDE/CodeBlockServer.d.ts +1 -1
- package/dist/types/components/IDE/CodeBlockServer.d.ts.map +1 -1
- package/dist/types/components/IDE/CodeBlockShiki.d.ts.map +1 -1
- package/dist/types/components/IDE/MarkDownRender.d.ts +1 -0
- package/dist/types/components/IDE/MarkDownRender.d.ts.map +1 -1
- package/dist/types/components/IDE/shikiBundle.d.ts +74 -0
- package/dist/types/components/IDE/shikiBundle.d.ts.map +1 -0
- package/dist/types/components/IDE/shikiLanguages.d.ts +2 -39
- package/dist/types/components/IDE/shikiLanguages.d.ts.map +1 -1
- package/dist/types/components/IDE/shikiThemes.d.ts +3 -1
- package/dist/types/components/IDE/shikiThemes.d.ts.map +1 -1
- package/dist/types/components/LanguageBackground/LanguageSection.d.ts +5 -1
- package/dist/types/components/LanguageBackground/LanguageSection.d.ts.map +1 -1
- package/dist/types/components/LanguageBackground/index.d.ts +2 -2
- package/dist/types/components/MarkDownRender/MarkDownIframe.d.ts.map +1 -1
- package/dist/types/components/MarkDownRender/MarkDownRender.d.ts.map +1 -1
- package/dist/types/components/MarkdownEditor/novel/extensions/code-block-shiki.d.ts +15 -0
- package/dist/types/components/MarkdownEditor/novel/extensions/code-block-shiki.d.ts.map +1 -0
- package/dist/types/components/MarkdownEditor/novel/extensions/index.d.ts +2 -1
- package/dist/types/components/MarkdownEditor/novel/extensions/index.d.ts.map +1 -1
- package/dist/types/components/MarkdownEditor/novel/index.d.ts +2 -1
- package/dist/types/components/Status/index.d.ts +8 -0
- package/dist/types/components/Status/index.d.ts.map +1 -0
- package/dist/types/components/SwitchSelector/SwitchSelector.d.ts +3 -3
- package/dist/types/components/SwitchSelector/SwitchSelector.d.ts.map +1 -1
- package/dist/types/components/Table/TableElements.d.ts.map +1 -1
- package/dist/types/components/TechLogo/TechLogo.d.ts.map +1 -1
- package/dist/types/components/TechLogo/logos/HTMX.d.ts +6 -0
- package/dist/types/components/TechLogo/logos/HTMX.d.ts.map +1 -0
- package/dist/types/components/TechLogo/logos/Remix.d.ts +6 -0
- package/dist/types/components/TechLogo/logos/Remix.d.ts.map +1 -0
- package/dist/types/components/TechLogo/techLogos.d.ts +4 -2
- package/dist/types/components/TechLogo/types.d.ts +1 -1
- package/dist/types/components/TextArea/TextArea.d.ts +1 -1
- package/dist/types/components/index.d.ts +3 -2
- package/dist/types/providers/ThemeProvider.d.ts.map +1 -1
- package/dist/types/routes.d.ts +21 -3
- package/dist/types/routes.d.ts.map +1 -1
- package/dist/types/structured-data/buildItemListJsonLd.d.ts +36 -0
- package/dist/types/structured-data/buildItemListJsonLd.d.ts.map +1 -0
- package/dist/types/structured-data/index.d.ts +2 -1
- package/package.json +68 -54
- package/src/components/MarkdownEditor/markdown-editor.css +17 -17
- package/styles/globals.css +71 -0
- package/dist/esm/components/DictionaryFieldEditor/ContentEditor.mjs.map +0 -1
- package/dist/types/components/DictionaryFieldEditor/ContentEditor.d.ts +0 -10
- package/dist/types/components/DictionaryFieldEditor/ContentEditor.d.ts.map +0 -1
- package/tailwind.css +0 -269
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DictionaryFieldEditor.mjs","names":[],"sources":["../../../../src/components/DictionaryFieldEditor/DictionaryFieldEditor.tsx"],"sourcesContent":["'use client';\n\nimport { Container } from '@components/Container';\nimport { CopyToClipboard } from '@components/CopyToClipboard';\nimport {\n useConfiguration,\n useDictionariesRecordActions,\n useFocusUnmergedDictionary,\n} from '@intlayer/editor-react';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { ArrowLeft } from 'lucide-react';\nimport { type FC, type ReactNode, useEffect, useState } from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { Button } from '../Button';\nimport { LocaleSwitcherContentProvider } from '../LocaleSwitcherContentDropDown';\nimport { TabSelector } from '../TabSelector';\nimport {
|
|
1
|
+
{"version":3,"file":"DictionaryFieldEditor.mjs","names":[],"sources":["../../../../src/components/DictionaryFieldEditor/DictionaryFieldEditor.tsx"],"sourcesContent":["'use client';\n\nimport { Container } from '@components/Container';\nimport { CopyToClipboard } from '@components/CopyToClipboard';\nimport {\n useConfiguration,\n useDictionariesRecordActions,\n useFocusUnmergedDictionary,\n} from '@intlayer/editor-react';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { ArrowLeft } from 'lucide-react';\nimport { type FC, type ReactNode, useEffect, useState } from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { Button } from '../Button';\nimport { LocaleSwitcherContentProvider } from '../LocaleSwitcherContentDropDown';\nimport { TabSelector } from '../TabSelector';\nimport { DictionaryDetailsForm } from './DictionaryDetails/DictionaryDetailsForm';\nimport { JSONEditor } from './JSONEditor';\nimport { NodeEditor } from './NodeEditor';\nimport { SaveForm } from './SaveForm/SaveForm';\nimport { StructureEditor } from './StructureEditor';\n\ntype DictionaryFieldEditorProps = {\n dictionary: Dictionary;\n onClickDictionaryList?: () => void;\n onDelete?: () => void;\n onSave?: () => void;\n isDarkMode?: boolean;\n mode: ('local' | 'remote')[];\n showReturnButton?: boolean;\n rightContent?: ReactNode;\n};\n\nexport const DictionaryFieldEditor: FC<DictionaryFieldEditorProps> = ({\n dictionary,\n onClickDictionaryList,\n isDarkMode,\n mode,\n onDelete,\n onSave,\n showReturnButton = true,\n rightContent,\n}) => {\n const config = useConfiguration();\n const { returnToDictionaryList } = useIntlayer('dictionary-field-editor');\n const { focusedContent, setFocusedContent } = useFocusUnmergedDictionary();\n const { setLocaleDictionary } = useDictionariesRecordActions();\n const [activeTab, setActiveTab] = useState<string>('content');\n\n useEffect(() => {\n setFocusedContent({\n ...(focusedContent ?? {}),\n dictionaryKey: dictionary.key,\n dictionaryLocalId: dictionary.localId,\n });\n setLocaleDictionary(dictionary);\n }, []);\n\n return (\n <LocaleSwitcherContentProvider\n availableLocales={config?.internationalization?.locales ?? []}\n >\n <div className=\"relative flex h-full min-h-0 w-full flex-1 flex-col md:overflow-hidden\">\n {showReturnButton && (\n <Button\n onClick={onClickDictionaryList}\n variant=\"hoverable\"\n className=\"z-10 mr-auto mb-6 ml-5 shrink-0\"\n color=\"text\"\n Icon={ArrowLeft}\n label={returnToDictionaryList.label.value}\n >\n {returnToDictionaryList.text}\n </Button>\n )}\n\n <div className=\"mb-22 flex min-h-0 flex-1 flex-col overflow-hidden\">\n {/* Tab headers */}\n <div className=\"sticky top-0 z-10 flex shrink-0 gap-3 rounded-xl bg-background/20 p-3 pb-4\">\n <TabSelector\n selectedChoice={activeTab}\n tabs={[\n ...(mode.includes('remote')\n ? [\n <button\n key=\"details\"\n className=\"cursor-pointer whitespace-nowrap rounded-md px-4 py-1 font-medium text-sm transition-colors focus:outline-none\"\n data-active={activeTab === 'details'}\n onClick={() => setActiveTab('details')}\n type=\"button\"\n >\n Details\n </button>,\n ]\n : []),\n <button\n key=\"structure\"\n className=\"cursor-pointer whitespace-nowrap rounded-md px-4 py-1 font-medium text-sm transition-colors focus:outline-none\"\n data-active={activeTab === 'structure'}\n onClick={() => setActiveTab('structure')}\n type=\"button\"\n >\n Structure\n </button>,\n <button\n key=\"content\"\n className=\"cursor-pointer whitespace-nowrap rounded-md px-4 py-1 font-medium text-sm transition-colors focus:outline-none\"\n data-active={activeTab === 'content'}\n onClick={() => setActiveTab('content')}\n type=\"button\"\n >\n Content\n </button>,\n <button\n key=\"json\"\n className=\"cursor-pointer whitespace-nowrap rounded-md px-4 py-1 font-medium text-sm transition-colors focus:outline-none\"\n data-active={activeTab === 'json'}\n onClick={() => setActiveTab('json')}\n type=\"button\"\n >\n JSON\n </button>,\n ]}\n hoverable\n color=\"text\"\n />\n <div className=\"flex w-full items-center justify-end\">\n {rightContent}\n </div>\n </div>\n {/* Tab content — only active panel is mounted */}\n <div className=\"min-h-0 flex-1 overflow-y-auto p-6\">\n <div className=\"flex w-full min-w-0 flex-col items-stretch gap-6\">\n {mode.includes('remote') && activeTab === 'details' && (\n <DictionaryDetailsForm dictionary={dictionary} mode={mode} />\n )}\n {activeTab === 'structure' && (\n <StructureEditor dictionary={dictionary} />\n )}\n {activeTab === 'content' && (\n <NodeEditor dictionary={dictionary} />\n )}\n {activeTab === 'json' && (\n <JSONEditor dictionary={dictionary} isDarkMode={isDarkMode} />\n )}\n </div>\n </div>\n </div>\n\n <div className=\"absolute bottom-3 z-20 w-full p-2\">\n <Container\n color=\"card\"\n roundedSize=\"2xl\"\n padding=\"sm\"\n className=\"w-full shrink-0 flex-row flex-wrap items-center justify-end gap-10 bg-background/20 md:bottom-0\"\n >\n <CopyToClipboard\n text={dictionary.id!}\n className=\"text-nowrap text-muted-foreground text-sm\"\n size={9}\n >\n {dictionary.id}\n </CopyToClipboard>\n <SaveForm\n dictionary={dictionary}\n mode={mode}\n onDelete={() => {\n setFocusedContent(null);\n onDelete?.();\n }}\n onSave={onSave}\n />\n </Container>\n </div>\n </div>\n </LocaleSwitcherContentProvider>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAiCA,MAAa,yBAAyD,EACpE,YACA,uBACA,YACA,MACA,UACA,QACA,mBAAmB,MACnB,mBACI;CACJ,MAAM,SAAS,iBAAiB;CAChC,MAAM,EAAE,2BAA2B,YAAY,yBAAyB;CACxE,MAAM,EAAE,gBAAgB,sBAAsB,2BAA2B;CACzE,MAAM,EAAE,wBAAwB,6BAA6B;CAC7D,MAAM,CAAC,WAAW,gBAAgB,SAAiB,SAAS;CAE5D,gBAAgB;EACd,kBAAkB;GAChB,GAAI,kBAAkB,CAAC;GACvB,eAAe,WAAW;GAC1B,mBAAmB,WAAW;EAChC,CAAC;EACD,oBAAoB,UAAU;CAChC,GAAG,CAAC,CAAC;CAEL,OACE,oBAAC,+BAAD;EACE,kBAAkB,QAAQ,sBAAsB,WAAW,CAAC;YAE5D,qBAAC,OAAD;GAAK,WAAU;aAAf;IACG,oBACC,oBAAC,QAAD;KACE,SAAS;KACT,SAAQ;KACR,WAAU;KACV,OAAM;KACN,MAAM;KACN,OAAO,uBAAuB,MAAM;eAEnC,uBAAuB;IAClB;IAGV,qBAAC,OAAD;KAAK,WAAU;eAAf,CAEE,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,aAAD;OACE,gBAAgB;OAChB,MAAM;QACJ,GAAI,KAAK,SAAS,QAAQ,IACtB,CACE,oBAAC,UAAD;SAEE,WAAU;SACV,eAAa,cAAc;SAC3B,eAAe,aAAa,SAAS;SACrC,MAAK;mBACN;QAEO,GAPF,SAOE,CACV,IACA,CAAC;QACL,oBAAC,UAAD;SAEE,WAAU;SACV,eAAa,cAAc;SAC3B,eAAe,aAAa,WAAW;SACvC,MAAK;mBACN;QAEO,GAPF,WAOE;QACR,oBAAC,UAAD;SAEE,WAAU;SACV,eAAa,cAAc;SAC3B,eAAe,aAAa,SAAS;SACrC,MAAK;mBACN;QAEO,GAPF,SAOE;QACR,oBAAC,UAAD;SAEE,WAAU;SACV,eAAa,cAAc;SAC3B,eAAe,aAAa,MAAM;SAClC,MAAK;mBACN;QAEO,GAPF,MAOE;OACV;OACA;OACA,OAAM;MACP,IACD,oBAAC,OAAD;OAAK,WAAU;iBACZ;MACE,EACF;SAEL,oBAAC,OAAD;MAAK,WAAU;gBACb,qBAAC,OAAD;OAAK,WAAU;iBAAf;QACG,KAAK,SAAS,QAAQ,KAAK,cAAc,aACxC,oBAAC,uBAAD;SAAmC;SAAkB;QAAO;QAE7D,cAAc,eACb,oBAAC,iBAAD,EAA6B,WAAa;QAE3C,cAAc,aACb,oBAAC,YAAD,EAAwB,WAAa;QAEtC,cAAc,UACb,oBAAC,YAAD;SAAwB;SAAwB;QAAa;OAE5D;;KACF,EACF;;IAEL,oBAAC,OAAD;KAAK,WAAU;eACb,qBAAC,WAAD;MACE,OAAM;MACN,aAAY;MACZ,SAAQ;MACR,WAAU;gBAJZ,CAME,oBAAC,iBAAD;OACE,MAAM,WAAW;OACjB,WAAU;OACV,MAAM;iBAEL,WAAW;MACG,IACjB,oBAAC,UAAD;OACc;OACN;OACN,gBAAgB;QACd,kBAAkB,IAAI;QACtB,WAAW;OACb;OACQ;MACT,EACQ;;IACR;GACF;;CACwB;AAEnC"}
|
|
@@ -5,8 +5,8 @@ import { getIsEditableSection } from "../getIsEditableSection.mjs";
|
|
|
5
5
|
import { useState } from "react";
|
|
6
6
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import { ChevronRight, Plus } from "lucide-react";
|
|
8
|
-
import { useEditedContentActions, useEditorLocale, useFocusUnmergedDictionary } from "@intlayer/editor-react";
|
|
9
8
|
import { useIntlayer } from "react-intlayer";
|
|
9
|
+
import { useEditedContentActions, useEditorLocale, useFocusUnmergedDictionary } from "@intlayer/editor-react";
|
|
10
10
|
import { getContentNodeByKeyPath, getEmptyNode, getNodeType } from "@intlayer/core/dictionaryManipulator";
|
|
11
11
|
import * as NodeTypes from "@intlayer/types/nodeType";
|
|
12
12
|
import { isSameKeyPath } from "@intlayer/core/utils";
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useGetDictionaries } from "../../api/hooks/dictionary.mjs";
|
|
4
3
|
import { Container } from "../Container/index.mjs";
|
|
4
|
+
import { useGetDictionaries } from "../../api/hooks/dictionary.mjs";
|
|
5
5
|
import { LocaleSwitcherContent } from "../LocaleSwitcherContentDropDown/LocaleSwitcherContent.mjs";
|
|
6
|
-
import { Pagination } from "../Pagination/Pagination.mjs";
|
|
7
6
|
import { TextEditorContainer } from "./ContentEditorView/TextEditor.mjs";
|
|
7
|
+
import { Pagination } from "../Pagination/Pagination.mjs";
|
|
8
8
|
import { getIsEditableSection } from "./getIsEditableSection.mjs";
|
|
9
9
|
import { KeyPathBreadcrumb } from "./KeyPathBreadcrumb.mjs";
|
|
10
10
|
import { NavigationViewNode } from "./NavigationView/NavigationViewNode.mjs";
|
|
11
11
|
import { useCallback, useDeferredValue, useEffect, useMemo, useState, useTransition } from "react";
|
|
12
12
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
|
-
import { useEditedContent, useEditorLocale, useFocusUnmergedDictionary } from "@intlayer/editor-react";
|
|
14
13
|
import { useIntlayer } from "react-intlayer";
|
|
14
|
+
import { useEditedContent, useEditorLocale, useFocusUnmergedDictionary } from "@intlayer/editor-react";
|
|
15
15
|
import { getContentNodeByKeyPath } from "@intlayer/core/dictionaryManipulator";
|
|
16
16
|
|
|
17
|
-
//#region src/components/DictionaryFieldEditor/
|
|
17
|
+
//#region src/components/DictionaryFieldEditor/NodeEditor.tsx
|
|
18
18
|
/** Render a variant (named string or structured object) for display. */
|
|
19
19
|
const formatVariant = (variant) => variant !== null && typeof variant === "object" ? JSON.stringify(variant) : variant ?? "";
|
|
20
|
-
const
|
|
20
|
+
const NodeEditor = ({ dictionary }) => {
|
|
21
21
|
const { itemPagination, variantSwitcher } = useIntlayer("content-editor");
|
|
22
22
|
const { editedContent } = useEditedContent();
|
|
23
23
|
const { focusedContent, setFocusedContentKeyPath: _setFocusedContentKeyPath } = useFocusUnmergedDictionary();
|
|
@@ -86,12 +86,12 @@ const ContentEditor = ({ dictionary }) => {
|
|
|
86
86
|
return /* @__PURE__ */ jsx("button", {
|
|
87
87
|
type: "button",
|
|
88
88
|
onClick: () => !isActive && switchSibling(sibling),
|
|
89
|
-
className: `rounded-lg px-3 py-1 text-xs transition-colors ${isActive ? "bg-text font-semibold text-
|
|
89
|
+
className: `rounded-lg px-3 py-1 text-xs transition-colors ${isActive ? "bg-text font-semibold text-foreground-opposite" : "cursor-pointer border border-border hover:bg-text/10"}`,
|
|
90
90
|
children: formatVariant(sibling.variant)
|
|
91
91
|
}, sibling.localId);
|
|
92
92
|
}),
|
|
93
93
|
currentVariant !== void 0 && !variantDicts.some((d) => d.localId === dictionary.localId) && /* @__PURE__ */ jsx("span", {
|
|
94
|
-
className: "rounded-lg bg-text px-3 py-1 font-semibold text-
|
|
94
|
+
className: "rounded-lg bg-text px-3 py-1 font-semibold text-foreground-opposite text-xs",
|
|
95
95
|
children: formatVariant(currentVariant)
|
|
96
96
|
})
|
|
97
97
|
]
|
|
@@ -134,5 +134,5 @@ const ContentEditor = ({ dictionary }) => {
|
|
|
134
134
|
};
|
|
135
135
|
|
|
136
136
|
//#endregion
|
|
137
|
-
export {
|
|
138
|
-
//# sourceMappingURL=
|
|
137
|
+
export { NodeEditor };
|
|
138
|
+
//# sourceMappingURL=NodeEditor.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NodeEditor.mjs","names":[],"sources":["../../../../src/components/DictionaryFieldEditor/NodeEditor.tsx"],"sourcesContent":["'use client';\n\nimport { useGetDictionaries } from '@api/index';\nimport { getContentNodeByKeyPath } from '@intlayer/core/dictionaryManipulator';\nimport {\n useEditedContent,\n useEditorLocale,\n useFocusUnmergedDictionary,\n} from '@intlayer/editor-react';\nimport type { Dictionary, LocalDictionaryId } from '@intlayer/types/dictionary';\nimport {\n type FC,\n useCallback,\n useDeferredValue,\n useEffect,\n useMemo,\n useState,\n useTransition,\n} from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { Container } from '../Container';\nimport { LocaleSwitcherContent } from '../LocaleSwitcherContentDropDown';\nimport { Pagination } from '../Pagination';\nimport { TextEditorContainer } from './ContentEditorView/TextEditor';\nimport { getIsEditableSection } from './getIsEditableSection';\nimport { KeyPathBreadcrumb } from './KeyPathBreadcrumb';\nimport { NavigationViewNode } from './NavigationView/NavigationViewNode';\n\nexport type NodeEditorProps = {\n dictionary: Dictionary;\n};\n\n/** Render a variant (named string or structured object) for display. */\nconst formatVariant = (\n variant: string | Record<string, string | number> | undefined\n): string =>\n variant !== null && typeof variant === 'object'\n ? JSON.stringify(variant)\n : (variant ?? '');\n\nexport const NodeEditor: FC<NodeEditorProps> = ({ dictionary }) => {\n const { itemPagination, variantSwitcher } = useIntlayer('content-editor');\n const { editedContent } = useEditedContent();\n const {\n focusedContent,\n setFocusedContentKeyPath: _setFocusedContentKeyPath,\n } = useFocusUnmergedDictionary();\n const [, startTransition] = useTransition();\n const setFocusedContentKeyPath: typeof _setFocusedContentKeyPath = (\n keyPath\n ) => startTransition(() => _setFocusedContentKeyPath(keyPath));\n\n const [activeDictionary, setActiveDictionary] =\n useState<Dictionary>(dictionary);\n\n // Re-sync the locally tracked dictionary whenever the incoming prop changes\n // identity — not only when its `localId` changes. After a save, the query\n // refetches and hands back the same `localId` with fresh content; keying the\n // effect on `localId` alone left `activeDictionary` (the render source) stale\n // until a full page reload. The prop reference is stable across unrelated\n // re-renders (query data / store entry), so sibling switches made via\n // `switchSibling` are preserved.\n useEffect(() => {\n setActiveDictionary(dictionary);\n }, [dictionary]);\n\n const hasQualifier =\n dictionary.item !== undefined || dictionary.variant !== undefined;\n\n const { data: siblingsData } = useGetDictionaries(\n { keys: [dictionary.key] },\n { enabled: hasQualifier && !!dictionary.key }\n );\n\n const allSiblings = useMemo<Dictionary[]>(\n () => (siblingsData?.data ?? []) as unknown as Dictionary[],\n [siblingsData]\n );\n\n const itemDicts = useMemo<Dictionary[]>(() => {\n if (dictionary.item === undefined) return [];\n return allSiblings\n .filter((d) => d.item !== undefined)\n .sort((a, b) => (a.item ?? 0) - (b.item ?? 0));\n }, [dictionary, allSiblings]);\n\n const variantDicts = useMemo<Dictionary[]>(() => {\n if (dictionary.variant === undefined) return [];\n return allSiblings.filter((d) => d.variant !== undefined);\n }, [dictionary, allSiblings]);\n\n const switchSibling = useCallback(\n (sibling: Dictionary) => {\n setActiveDictionary(sibling);\n setFocusedContentKeyPath([]);\n },\n [setFocusedContentKeyPath]\n );\n\n const { content, key, localId } = activeDictionary;\n const focusedKeyPath = focusedContent?.keyPath;\n const section =\n typeof editedContent?.[localId as LocalDictionaryId]?.content ===\n 'undefined'\n ? content\n : editedContent?.[localId as LocalDictionaryId]?.content;\n\n const currentLocale = useEditorLocale();\n const focusedSection = getContentNodeByKeyPath(\n section,\n focusedKeyPath ?? [],\n currentLocale\n );\n\n const deferredKeyPath = useDeferredValue(focusedKeyPath);\n const deferredSection = useDeferredValue(focusedSection);\n const isStale = deferredSection !== focusedSection;\n\n const isEditableBaseSection = getIsEditableSection(section);\n const isEditableFocusedSection = getIsEditableSection(deferredSection);\n\n useEffect(() => {\n if (typeof focusedSection === 'undefined') {\n setFocusedContentKeyPath(focusedContent?.keyPath?.slice(0, -1) ?? []);\n }\n }, []);\n\n const currentItemIndex = itemDicts.findIndex(\n (dictionary) => dictionary.localId === activeDictionary.localId\n );\n const currentVariant = activeDictionary.variant;\n\n return (\n <div>\n {/* Qualifier navigation controls */}\n {hasQualifier && (\n <div className=\"mb-4 flex flex-wrap items-center gap-4\">\n {itemDicts.length > 0 && (\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium text-muted text-xs\">\n {itemPagination.label}\n </span>\n <Pagination\n currentPage={currentItemIndex >= 0 ? currentItemIndex + 1 : 1}\n totalPages={itemDicts.length}\n onPageChange={(page) => {\n const target = itemDicts[page - 1];\n if (target) switchSibling(target);\n }}\n size=\"sm\"\n />\n </div>\n )}\n\n {variantDicts.length > 0 && (\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"font-medium text-muted text-xs\">\n {variantSwitcher.label}:\n </span>\n {variantDicts.map((sibling) => {\n const isActive = sibling.localId === activeDictionary.localId;\n return (\n <button\n key={sibling.localId}\n type=\"button\"\n onClick={() => !isActive && switchSibling(sibling)}\n className={`rounded-lg px-3 py-1 text-xs transition-colors ${\n isActive\n ? 'bg-text font-semibold text-foreground-opposite'\n : 'cursor-pointer border border-border hover:bg-text/10'\n }`}\n >\n {formatVariant(sibling.variant)}\n </button>\n );\n })}\n {currentVariant !== undefined &&\n !variantDicts.some((d) => d.localId === dictionary.localId) && (\n <span className=\"rounded-lg bg-text px-3 py-1 font-semibold text-foreground-opposite text-xs\">\n {formatVariant(currentVariant)}\n </span>\n )}\n </div>\n )}\n </div>\n )}\n\n <div className=\"mb-6 flex items-center justify-between gap-2\">\n <KeyPathBreadcrumb\n dictionaryKey={key}\n keyPath={focusedKeyPath ?? []}\n onClickKeyPath={setFocusedContentKeyPath}\n />\n <div className=\"flex items-center gap-2\">\n <LocaleSwitcherContent />\n </div>\n </div>\n <div className=\"flex flex-1 gap-2 overflow-visible max-md:flex-col\">\n {typeof section === 'object' &&\n section &&\n !isEditableBaseSection &&\n Object.keys(section).length > 0 && (\n <Container\n border\n background=\"none\"\n className=\"top-10 flex h-full flex-col items-start gap-0.5 overflow-auto p-2 md:sticky md:max-w-[50%]\"\n roundedSize=\"2xl\"\n transparency=\"xs\"\n >\n <NavigationViewNode\n keyPath={[]}\n section={section}\n dictionary={activeDictionary}\n />\n </Container>\n )}\n {(isEditableFocusedSection || (deferredKeyPath ?? []).length > 0) && (\n <div\n className={\n isStale\n ? 'pointer-events-none flex-1 opacity-50 transition-opacity'\n : 'flex-1 transition-opacity'\n }\n >\n <TextEditorContainer\n keyPath={deferredKeyPath ?? []}\n section={deferredSection}\n dictionary={activeDictionary}\n />\n </div>\n )}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { cn } from "../../../utils/cn.mjs";
|
|
4
|
+
import { Button } from "../../Button/Button.mjs";
|
|
4
5
|
import { useSession } from "../../../api/useAuth/useSession.mjs";
|
|
5
6
|
import { useAuth } from "../../../api/useAuth/useAuth.mjs";
|
|
6
7
|
import { useDeleteDictionary, usePushDictionaries } from "../../../api/hooks/dictionary.mjs";
|
|
7
8
|
import { useWriteDictionary } from "../../../api/hooks/editor.mjs";
|
|
8
|
-
import { Button } from "../../Button/Button.mjs";
|
|
9
9
|
import { Modal } from "../../Modal/Modal.mjs";
|
|
10
10
|
import { useState } from "react";
|
|
11
11
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
12
12
|
import { ArrowUpFromLine, Download, RotateCcw, Save, Trash } from "lucide-react";
|
|
13
|
-
import { useDictionariesRecordActions, useEditedContent } from "@intlayer/editor-react";
|
|
14
13
|
import { useIntlayer } from "react-intlayer";
|
|
14
|
+
import { useDictionariesRecordActions, useEditedContent } from "@intlayer/editor-react";
|
|
15
15
|
|
|
16
16
|
//#region src/components/DictionaryFieldEditor/SaveForm/SaveForm.tsx
|
|
17
17
|
const SaveForm = ({ dictionary, mode, className, onDelete, onSave, ...props }) => {
|
|
@@ -71,7 +71,7 @@ const SaveForm = ({ dictionary, mode, className, onDelete, onSave, ...props }) =
|
|
|
71
71
|
children: /* @__PURE__ */ jsxs("form", {
|
|
72
72
|
className: "size-full",
|
|
73
73
|
children: [/* @__PURE__ */ jsx("p", {
|
|
74
|
-
className: "py-4 text-
|
|
74
|
+
className: "py-4 text-muted-foreground text-sm",
|
|
75
75
|
children: confirmation.message
|
|
76
76
|
}), /* @__PURE__ */ jsxs("div", {
|
|
77
77
|
className: "mt-12 flex justify-end gap-2 max-md:flex-col",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SaveForm.mjs","names":["FormButton"],"sources":["../../../../../src/components/DictionaryFieldEditor/SaveForm/SaveForm.tsx"],"sourcesContent":["'use client';\n\nimport {\n useAuth,\n useDeleteDictionary,\n usePushDictionaries,\n useSession,\n useWriteDictionary,\n} from '@api/index';\nimport { FormButton } from '@components/Form';\nimport { Modal } from '@components/Modal';\nimport type { Dictionary as DistantDictionary } from '@intlayer/backend';\nimport {\n useDictionariesRecordActions,\n useEditedContent,\n} from '@intlayer/editor-react';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { cn } from '@utils/cn';\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';\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 const { session } = useSession();\n\n const hasDictionaryWritePermission =\n (session?.permissions?.includes('dictionary:admin') ||\n session?.permissions?.includes('dictionary:write')) ??\n false;\n\n const hasDictionaryDeletePermission = hasDictionaryWritePermission;\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=\"md\"\n onClose={() => setIsFormatAlertModalOpen(false)}\n padding=\"md\"\n >\n <form className=\"size-full\">\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 <FormButton\n label={confirmation.cancelButton.label.value}\n disabled={!isEdited || isLoading}\n color=\"text\"\n className=\"max-md:w-full\"\n variant=\"outline\"\n onClick={() => setIsFormatAlertModalOpen(false)}\n >\n {confirmation.cancelButton.text}\n </FormButton>\n <FormButton\n label={confirmation.confirmButton.label.value}\n disabled={!isEdited || isLoading || !hasDictionaryWritePermission}\n Icon={Save}\n color=\"text\"\n className=\"max-md:w-full\"\n isLoading={isPushing}\n onClick={handleSaveDictionaryConfirmation}\n >\n {confirmation.confirmButton.text}\n </FormButton>\n </div>\n </form>\n </Modal>\n <form className={cn('flex justify-end gap-2', className)} {...props}>\n {mode.includes('remote') &&\n isDistantDictionary &&\n onDelete &&\n isAuthenticated && (\n <FormButton\n label={deleteButton.label.value}\n Icon={Trash}\n color=\"error\"\n variant=\"outline\"\n className=\"max-md:w-full\"\n isLoading={isDeleting}\n onClick={handleDeleteDictionary}\n disabled={!hasDictionaryDeletePermission}\n >\n {deleteButton.text}\n </FormButton>\n )}\n {isEdited && (\n <FormButton\n label={resetButton.label.value}\n disabled={!isEdited}\n Icon={RotateCcw}\n variant=\"outline\"\n color=\"text\"\n className=\"max-md:w-full\"\n onClick={() => restoreEditedContent(dictionary.localId!)}\n >\n {resetButton.text}\n </FormButton>\n )}\n {mode.includes('local') && (\n <FormButton\n label={downloadButton.label.value}\n disabled={!isEdited || isLoading}\n Icon={Download}\n color=\"text\"\n variant={isAuthenticated ? 'outline' : 'default'}\n className=\"max-md:w-full\"\n isLoading={isWriting}\n onClick={() => setIsFormatAlertModalOpen(true)}\n >\n {downloadButton.text}\n </FormButton>\n )}\n {mode.includes('remote') && isAuthenticated && !isDistantDictionary && (\n <FormButton\n label={publishButton.label.value}\n disabled={isLoading || !hasDictionaryWritePermission}\n Icon={ArrowUpFromLine}\n color=\"text\"\n className=\"max-md:w-full\"\n isLoading={isPushing}\n onClick={handlePushDictionary}\n >\n {publishButton.text}\n </FormButton>\n )}\n {mode.includes('remote') &&\n isAuthenticated &&\n isDistantDictionary &&\n isEdited && (\n <FormButton\n label={saveButton.label.value}\n disabled={!isEdited || isLoading || !hasDictionaryWritePermission}\n Icon={Save}\n color=\"text\"\n className=\"max-md:w-full\"\n isLoading={isPushing}\n onClick={handlePushDictionary}\n >\n {saveButton.text}\n </FormButton>\n )}\n </form>\n </>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SaveForm.mjs","names":["FormButton"],"sources":["../../../../../src/components/DictionaryFieldEditor/SaveForm/SaveForm.tsx"],"sourcesContent":["'use client';\n\nimport {\n useAuth,\n useDeleteDictionary,\n usePushDictionaries,\n useSession,\n useWriteDictionary,\n} from '@api/index';\nimport { FormButton } from '@components/Form';\nimport { Modal } from '@components/Modal';\nimport type { Dictionary as DistantDictionary } from '@intlayer/backend';\nimport {\n useDictionariesRecordActions,\n useEditedContent,\n} from '@intlayer/editor-react';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { cn } from '@utils/cn';\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';\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 const { session } = useSession();\n\n const hasDictionaryWritePermission =\n (session?.permissions?.includes('dictionary:admin') ||\n session?.permissions?.includes('dictionary:write')) ??\n false;\n\n const hasDictionaryDeletePermission = hasDictionaryWritePermission;\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=\"md\"\n onClose={() => setIsFormatAlertModalOpen(false)}\n padding=\"md\"\n >\n <form className=\"size-full\">\n <p className=\"py-4 text-muted-foreground text-sm\">\n {confirmation.message}\n </p>\n\n <div className=\"mt-12 flex justify-end gap-2 max-md:flex-col\">\n <FormButton\n label={confirmation.cancelButton.label.value}\n disabled={!isEdited || isLoading}\n color=\"text\"\n className=\"max-md:w-full\"\n variant=\"outline\"\n onClick={() => setIsFormatAlertModalOpen(false)}\n >\n {confirmation.cancelButton.text}\n </FormButton>\n <FormButton\n label={confirmation.confirmButton.label.value}\n disabled={!isEdited || isLoading || !hasDictionaryWritePermission}\n Icon={Save}\n color=\"text\"\n className=\"max-md:w-full\"\n isLoading={isPushing}\n onClick={handleSaveDictionaryConfirmation}\n >\n {confirmation.confirmButton.text}\n </FormButton>\n </div>\n </form>\n </Modal>\n <form className={cn('flex justify-end gap-2', className)} {...props}>\n {mode.includes('remote') &&\n isDistantDictionary &&\n onDelete &&\n isAuthenticated && (\n <FormButton\n label={deleteButton.label.value}\n Icon={Trash}\n color=\"error\"\n variant=\"outline\"\n className=\"max-md:w-full\"\n isLoading={isDeleting}\n onClick={handleDeleteDictionary}\n disabled={!hasDictionaryDeletePermission}\n >\n {deleteButton.text}\n </FormButton>\n )}\n {isEdited && (\n <FormButton\n label={resetButton.label.value}\n disabled={!isEdited}\n Icon={RotateCcw}\n variant=\"outline\"\n color=\"text\"\n className=\"max-md:w-full\"\n onClick={() => restoreEditedContent(dictionary.localId!)}\n >\n {resetButton.text}\n </FormButton>\n )}\n {mode.includes('local') && (\n <FormButton\n label={downloadButton.label.value}\n disabled={!isEdited || isLoading}\n Icon={Download}\n color=\"text\"\n variant={isAuthenticated ? 'outline' : 'default'}\n className=\"max-md:w-full\"\n isLoading={isWriting}\n onClick={() => setIsFormatAlertModalOpen(true)}\n >\n {downloadButton.text}\n </FormButton>\n )}\n {mode.includes('remote') && isAuthenticated && !isDistantDictionary && (\n <FormButton\n label={publishButton.label.value}\n disabled={isLoading || !hasDictionaryWritePermission}\n Icon={ArrowUpFromLine}\n color=\"text\"\n className=\"max-md:w-full\"\n isLoading={isPushing}\n onClick={handlePushDictionary}\n >\n {publishButton.text}\n </FormButton>\n )}\n {mode.includes('remote') &&\n isAuthenticated &&\n isDistantDictionary &&\n isEdited && (\n <FormButton\n label={saveButton.label.value}\n disabled={!isEdited || isLoading || !hasDictionaryWritePermission}\n Icon={Save}\n color=\"text\"\n className=\"max-md:w-full\"\n isLoading={isPushing}\n onClick={handlePushDictionary}\n >\n {saveButton.text}\n </FormButton>\n )}\n </form>\n </>\n );\n};\n"],"mappings":"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"}
|
|
@@ -6,8 +6,8 @@ import { EditableFieldInput } from "../../EditableField/EditableFieldInput.mjs";
|
|
|
6
6
|
import { NodeTypeSelector } from "../NodeTypeSelector.mjs";
|
|
7
7
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
import { Plus, Trash } from "lucide-react";
|
|
9
|
-
import { useConfiguration, useEditedContentActions, useFocusUnmergedDictionary } from "@intlayer/editor-react";
|
|
10
9
|
import { useIntlayer } from "react-intlayer";
|
|
10
|
+
import { useConfiguration, useEditedContentActions, useFocusUnmergedDictionary } from "@intlayer/editor-react";
|
|
11
11
|
import { getDefaultNode, getNodeChildren, getNodeType } from "@intlayer/core/dictionaryManipulator";
|
|
12
12
|
import * as NodeTypes from "@intlayer/types/nodeType";
|
|
13
13
|
import { isSameKeyPath } from "@intlayer/core/utils";
|
|
@@ -124,7 +124,7 @@ const NodeView = ({ sectionKey, section, keyPath, dictionaryLocalId }) => {
|
|
|
124
124
|
}
|
|
125
125
|
})]
|
|
126
126
|
}), /* @__PURE__ */ jsxs("span", {
|
|
127
|
-
className: "ml-3 text-
|
|
127
|
+
className: "ml-3 text-muted-foreground text-sm",
|
|
128
128
|
children: [
|
|
129
129
|
"( ",
|
|
130
130
|
camelCaseToSentence(sectionKey),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StructureView.mjs","names":[],"sources":["../../../../../src/components/DictionaryFieldEditor/StructureView/StructureView.tsx"],"sourcesContent":["'use client';\n\nimport { Button } from '@components/Button';\nimport { Container } from '@components/Container';\nimport { EditableFieldInput } from '@components/EditableField';\nimport { camelCaseToSentence } from '@intlayer/config/client';\nimport {\n getDefaultNode,\n getNodeChildren,\n getNodeType,\n} from '@intlayer/core/dictionaryManipulator';\nimport { isSameKeyPath } from '@intlayer/core/utils';\nimport {\n useConfiguration,\n useEditedContentActions,\n useFocusUnmergedDictionary,\n} from '@intlayer/editor-react';\nimport type { LocalDictionaryId, TypedNode } from '@intlayer/types/dictionary';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport type { ContentNode } from 'intlayer';\nimport { Plus, Trash } from 'lucide-react';\nimport type { FC } from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { NodeTypeSelector } from '../NodeTypeSelector';\n\ntype NodeTypeViewProps = {\n dictionaryLocalId: LocalDictionaryId;\n keyPath: KeyPath[];\n section: ContentNode;\n onNodeTypeChange: (content?: ContentNode) => void;\n};\n\nconst NodeTypeView: FC<NodeTypeViewProps> = ({\n section,\n dictionaryLocalId,\n keyPath,\n onNodeTypeChange: onNodeTypeChangeProp,\n}) => {\n const locales = useConfiguration()?.internationalization?.locales ?? [];\n const nodeType = getNodeType(section);\n const children = getNodeChildren(section);\n\n const onNodeTypeChange = (content?: ContentNode) => {\n const transformedContent = getDefaultNode(\n nodeType,\n locales,\n content\n ) as ContentNode;\n\n onNodeTypeChangeProp(transformedContent);\n };\n\n if (\n nodeType === NodeTypes.TRANSLATION ||\n nodeType === NodeTypes.CONDITION ||\n nodeType === NodeTypes.GENDER ||\n nodeType === NodeTypes.SELECT ||\n nodeType === NodeTypes.ENUMERATION ||\n nodeType === NodeTypes.PLURAL\n ) {\n const firstKey = Object.keys(\n (section as unknown as TypedNode)[nodeType as keyof typeof section]\n )[0];\n const childrenKeyPath = [\n ...keyPath,\n { type: nodeType, key: firstKey },\n ] as KeyPath[];\n\n return (\n <div className=\"flex w-full flex-col gap-1\">\n <NodeTypeSelector\n section={section}\n onValueChange={(nodeType) =>\n onNodeTypeChangeProp(\n getDefaultNode(nodeType, locales) as ContentNode\n )\n }\n />\n\n <NodeTypeView\n section={children}\n keyPath={childrenKeyPath}\n dictionaryLocalId={dictionaryLocalId}\n onNodeTypeChange={onNodeTypeChange}\n />\n </div>\n );\n }\n\n if (nodeType === NodeTypes.ARRAY) {\n const childrenKeyPath = [...keyPath, { type: nodeType, key: 0 } as KeyPath];\n return (\n <div className=\"flex w-full flex-col gap-1\">\n <NodeTypeSelector\n section={section}\n onValueChange={(nodeType) =>\n onNodeTypeChangeProp(\n getDefaultNode(nodeType, locales) as ContentNode\n )\n }\n />\n\n <NodeTypeView\n section={children}\n keyPath={childrenKeyPath}\n dictionaryLocalId={dictionaryLocalId}\n onNodeTypeChange={onNodeTypeChange}\n />\n </div>\n );\n }\n\n if (nodeType === NodeTypes.OBJECT) {\n return (\n <>\n <NodeTypeSelector\n section={section}\n onValueChange={(nodeType) =>\n onNodeTypeChangeProp(\n getDefaultNode(nodeType, locales) as ContentNode\n )\n }\n />\n <div className=\"mt-6 ml-10\">\n <StructureView\n keyPath={keyPath}\n section={section}\n dictionaryLocalId={dictionaryLocalId}\n />\n </div>\n </>\n );\n }\n\n return (\n <NodeTypeSelector\n section={section}\n onValueChange={(nodeType) =>\n onNodeTypeChangeProp(getDefaultNode(nodeType, locales) as ContentNode)\n }\n />\n );\n};\n\ntype NodeWrapperProps = {\n sectionKey?: string;\n dictionaryLocalId: LocalDictionaryId;\n keyPath: KeyPath[];\n section: ContentNode;\n};\n\nexport const NodeView: FC<NodeWrapperProps> = ({\n sectionKey,\n section,\n keyPath,\n dictionaryLocalId,\n}) => {\n const { focusedContent, setFocusedContentKeyPath } =\n useFocusUnmergedDictionary();\n const { renameEditedContent, addEditedContent } = useEditedContentActions();\n\n const { titleInput, deleteButton } = useIntlayer('structure-view');\n\n const handleRenameNodeKey = (keyName: string) => {\n renameEditedContent(dictionaryLocalId, keyName, keyPath);\n const prevKeyPath: KeyPath[] = keyPath.slice(0, -1);\n const lastKeyPath: KeyPath = keyPath[keyPath.length - 1]!;\n const newKeyPath: KeyPath[] = [\n ...prevKeyPath,\n { ...lastKeyPath, key: keyName } as KeyPath,\n ];\n setFocusedContentKeyPath(newKeyPath);\n };\n\n return (\n <Container\n transparency=\"xl\"\n roundedSize=\"2xl\"\n className=\"w-full min-w-80 gap-2 overflow-auto px-5 py-2\"\n border\n borderColor=\"text\"\n background=\"none\"\n aria-selected={isSameKeyPath(keyPath, focusedContent?.keyPath ?? [])}\n onClick={() => setFocusedContentKeyPath(keyPath)}\n >\n <div className=\"flex w-full flex-col items-start justify-between gap-3\">\n {typeof sectionKey === 'string' && (\n <div className=\"w-full\">\n <div className=\"flex w-full items-center justify-between gap-10\">\n <EditableFieldInput\n name=\"key\"\n aria-label=\"Key\"\n placeholder={titleInput.placeholder.value}\n defaultValue={sectionKey}\n onSave={(value) => handleRenameNodeKey(value)}\n variant=\"invisible\"\n />\n <Button\n label={deleteButton.label.value}\n variant=\"hoverable\"\n size=\"icon-sm\"\n color=\"text\"\n className=\"translate-x-2\"\n Icon={Trash}\n onClick={() => {\n addEditedContent(dictionaryLocalId, undefined, keyPath);\n\n const parentKeyPath: KeyPath[] = keyPath.slice(0, -1);\n setFocusedContentKeyPath(parentKeyPath);\n }}\n />\n </div>\n\n <span className=\"ml-3 text-neutral text-sm\">\n ( {camelCaseToSentence(sectionKey)} )\n </span>\n </div>\n )}\n <NodeTypeView\n keyPath={keyPath}\n dictionaryLocalId={dictionaryLocalId}\n section={section}\n onNodeTypeChange={(content) => {\n addEditedContent(dictionaryLocalId, content, keyPath);\n }}\n />\n </div>\n </Container>\n );\n};\n\ntype ObjectViewProps = {\n dictionaryLocalId: LocalDictionaryId;\n keyPath: KeyPath[];\n section: ContentNode;\n};\n\nexport const ObjectView: FC<ObjectViewProps> = ({\n section,\n keyPath,\n dictionaryLocalId,\n}) => {\n const { addNodeButton } = useIntlayer('structure-view');\n const { setFocusedContentKeyPath } = useFocusUnmergedDictionary();\n const { addEditedContent } = useEditedContentActions();\n\n if (!section || typeof section !== 'object') {\n return <div>Not an object</div>;\n }\n\n return (\n <div className=\"flex flex-col gap-2 overflow-y-auto\">\n <ul className=\"mr-auto flex flex-col gap-4\">\n {Object.keys(section).map((key) => (\n <li\n key={`${JSON.stringify(keyPath)}-object-${key}`}\n className=\"flex w-full\"\n >\n <NodeView\n sectionKey={key}\n section={section?.[key as keyof typeof section]}\n keyPath={[...keyPath, { type: NodeTypes.OBJECT, key }]}\n dictionaryLocalId={dictionaryLocalId}\n />\n </li>\n ))}\n </ul>\n <Button\n label={addNodeButton.label.value}\n variant=\"hoverable\"\n size=\"md\"\n color=\"text\"\n Icon={Plus}\n className=\"flex-1\"\n onClick={() => {\n const newKey = 'newKey';\n const newKeyPath = [\n ...keyPath,\n { type: NodeTypes.OBJECT, key: newKey },\n ] as KeyPath[];\n addEditedContent(dictionaryLocalId, '', newKeyPath);\n setFocusedContentKeyPath(newKeyPath);\n }}\n >\n {addNodeButton.text}\n </Button>\n </div>\n );\n};\n\ntype StructureViewProps = {\n dictionaryLocalId: LocalDictionaryId;\n keyPath: KeyPath[];\n section: ContentNode;\n};\n\nexport const StructureView: FC<StructureViewProps> = ({\n section,\n keyPath,\n dictionaryLocalId,\n}) => {\n if (\n !section ||\n typeof section !== 'object' ||\n typeof section.nodeType === 'string'\n ) {\n return (\n <NodeView\n sectionKey=\"content\"\n section={section}\n keyPath={keyPath}\n dictionaryLocalId={dictionaryLocalId}\n />\n );\n }\n\n return (\n <ObjectView\n section={section}\n keyPath={keyPath}\n dictionaryLocalId={dictionaryLocalId}\n />\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"StructureView.mjs","names":[],"sources":["../../../../../src/components/DictionaryFieldEditor/StructureView/StructureView.tsx"],"sourcesContent":["'use client';\n\nimport { Button } from '@components/Button';\nimport { Container } from '@components/Container';\nimport { EditableFieldInput } from '@components/EditableField';\nimport { camelCaseToSentence } from '@intlayer/config/client';\nimport {\n getDefaultNode,\n getNodeChildren,\n getNodeType,\n} from '@intlayer/core/dictionaryManipulator';\nimport { isSameKeyPath } from '@intlayer/core/utils';\nimport {\n useConfiguration,\n useEditedContentActions,\n useFocusUnmergedDictionary,\n} from '@intlayer/editor-react';\nimport type { LocalDictionaryId, TypedNode } from '@intlayer/types/dictionary';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport type { ContentNode } from 'intlayer';\nimport { Plus, Trash } from 'lucide-react';\nimport type { FC } from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { NodeTypeSelector } from '../NodeTypeSelector';\n\ntype NodeTypeViewProps = {\n dictionaryLocalId: LocalDictionaryId;\n keyPath: KeyPath[];\n section: ContentNode;\n onNodeTypeChange: (content?: ContentNode) => void;\n};\n\nconst NodeTypeView: FC<NodeTypeViewProps> = ({\n section,\n dictionaryLocalId,\n keyPath,\n onNodeTypeChange: onNodeTypeChangeProp,\n}) => {\n const locales = useConfiguration()?.internationalization?.locales ?? [];\n const nodeType = getNodeType(section);\n const children = getNodeChildren(section);\n\n const onNodeTypeChange = (content?: ContentNode) => {\n const transformedContent = getDefaultNode(\n nodeType,\n locales,\n content\n ) as ContentNode;\n\n onNodeTypeChangeProp(transformedContent);\n };\n\n if (\n nodeType === NodeTypes.TRANSLATION ||\n nodeType === NodeTypes.CONDITION ||\n nodeType === NodeTypes.GENDER ||\n nodeType === NodeTypes.SELECT ||\n nodeType === NodeTypes.ENUMERATION ||\n nodeType === NodeTypes.PLURAL\n ) {\n const firstKey = Object.keys(\n (section as unknown as TypedNode)[nodeType as keyof typeof section]\n )[0];\n const childrenKeyPath = [\n ...keyPath,\n { type: nodeType, key: firstKey },\n ] as KeyPath[];\n\n return (\n <div className=\"flex w-full flex-col gap-1\">\n <NodeTypeSelector\n section={section}\n onValueChange={(nodeType) =>\n onNodeTypeChangeProp(\n getDefaultNode(nodeType, locales) as ContentNode\n )\n }\n />\n\n <NodeTypeView\n section={children}\n keyPath={childrenKeyPath}\n dictionaryLocalId={dictionaryLocalId}\n onNodeTypeChange={onNodeTypeChange}\n />\n </div>\n );\n }\n\n if (nodeType === NodeTypes.ARRAY) {\n const childrenKeyPath = [...keyPath, { type: nodeType, key: 0 } as KeyPath];\n return (\n <div className=\"flex w-full flex-col gap-1\">\n <NodeTypeSelector\n section={section}\n onValueChange={(nodeType) =>\n onNodeTypeChangeProp(\n getDefaultNode(nodeType, locales) as ContentNode\n )\n }\n />\n\n <NodeTypeView\n section={children}\n keyPath={childrenKeyPath}\n dictionaryLocalId={dictionaryLocalId}\n onNodeTypeChange={onNodeTypeChange}\n />\n </div>\n );\n }\n\n if (nodeType === NodeTypes.OBJECT) {\n return (\n <>\n <NodeTypeSelector\n section={section}\n onValueChange={(nodeType) =>\n onNodeTypeChangeProp(\n getDefaultNode(nodeType, locales) as ContentNode\n )\n }\n />\n <div className=\"mt-6 ml-10\">\n <StructureView\n keyPath={keyPath}\n section={section}\n dictionaryLocalId={dictionaryLocalId}\n />\n </div>\n </>\n );\n }\n\n return (\n <NodeTypeSelector\n section={section}\n onValueChange={(nodeType) =>\n onNodeTypeChangeProp(getDefaultNode(nodeType, locales) as ContentNode)\n }\n />\n );\n};\n\ntype NodeWrapperProps = {\n sectionKey?: string;\n dictionaryLocalId: LocalDictionaryId;\n keyPath: KeyPath[];\n section: ContentNode;\n};\n\nexport const NodeView: FC<NodeWrapperProps> = ({\n sectionKey,\n section,\n keyPath,\n dictionaryLocalId,\n}) => {\n const { focusedContent, setFocusedContentKeyPath } =\n useFocusUnmergedDictionary();\n const { renameEditedContent, addEditedContent } = useEditedContentActions();\n\n const { titleInput, deleteButton } = useIntlayer('structure-view');\n\n const handleRenameNodeKey = (keyName: string) => {\n renameEditedContent(dictionaryLocalId, keyName, keyPath);\n const prevKeyPath: KeyPath[] = keyPath.slice(0, -1);\n const lastKeyPath: KeyPath = keyPath[keyPath.length - 1]!;\n const newKeyPath: KeyPath[] = [\n ...prevKeyPath,\n { ...lastKeyPath, key: keyName } as KeyPath,\n ];\n setFocusedContentKeyPath(newKeyPath);\n };\n\n return (\n <Container\n transparency=\"xl\"\n roundedSize=\"2xl\"\n className=\"w-full min-w-80 gap-2 overflow-auto px-5 py-2\"\n border\n borderColor=\"text\"\n background=\"none\"\n aria-selected={isSameKeyPath(keyPath, focusedContent?.keyPath ?? [])}\n onClick={() => setFocusedContentKeyPath(keyPath)}\n >\n <div className=\"flex w-full flex-col items-start justify-between gap-3\">\n {typeof sectionKey === 'string' && (\n <div className=\"w-full\">\n <div className=\"flex w-full items-center justify-between gap-10\">\n <EditableFieldInput\n name=\"key\"\n aria-label=\"Key\"\n placeholder={titleInput.placeholder.value}\n defaultValue={sectionKey}\n onSave={(value) => handleRenameNodeKey(value)}\n variant=\"invisible\"\n />\n <Button\n label={deleteButton.label.value}\n variant=\"hoverable\"\n size=\"icon-sm\"\n color=\"text\"\n className=\"translate-x-2\"\n Icon={Trash}\n onClick={() => {\n addEditedContent(dictionaryLocalId, undefined, keyPath);\n\n const parentKeyPath: KeyPath[] = keyPath.slice(0, -1);\n setFocusedContentKeyPath(parentKeyPath);\n }}\n />\n </div>\n\n <span className=\"ml-3 text-muted-foreground text-sm\">\n ( {camelCaseToSentence(sectionKey)} )\n </span>\n </div>\n )}\n <NodeTypeView\n keyPath={keyPath}\n dictionaryLocalId={dictionaryLocalId}\n section={section}\n onNodeTypeChange={(content) => {\n addEditedContent(dictionaryLocalId, content, keyPath);\n }}\n />\n </div>\n </Container>\n );\n};\n\ntype ObjectViewProps = {\n dictionaryLocalId: LocalDictionaryId;\n keyPath: KeyPath[];\n section: ContentNode;\n};\n\nexport const ObjectView: FC<ObjectViewProps> = ({\n section,\n keyPath,\n dictionaryLocalId,\n}) => {\n const { addNodeButton } = useIntlayer('structure-view');\n const { setFocusedContentKeyPath } = useFocusUnmergedDictionary();\n const { addEditedContent } = useEditedContentActions();\n\n if (!section || typeof section !== 'object') {\n return <div>Not an object</div>;\n }\n\n return (\n <div className=\"flex flex-col gap-2 overflow-y-auto\">\n <ul className=\"mr-auto flex flex-col gap-4\">\n {Object.keys(section).map((key) => (\n <li\n key={`${JSON.stringify(keyPath)}-object-${key}`}\n className=\"flex w-full\"\n >\n <NodeView\n sectionKey={key}\n section={section?.[key as keyof typeof section]}\n keyPath={[...keyPath, { type: NodeTypes.OBJECT, key }]}\n dictionaryLocalId={dictionaryLocalId}\n />\n </li>\n ))}\n </ul>\n <Button\n label={addNodeButton.label.value}\n variant=\"hoverable\"\n size=\"md\"\n color=\"text\"\n Icon={Plus}\n className=\"flex-1\"\n onClick={() => {\n const newKey = 'newKey';\n const newKeyPath = [\n ...keyPath,\n { type: NodeTypes.OBJECT, key: newKey },\n ] as KeyPath[];\n addEditedContent(dictionaryLocalId, '', newKeyPath);\n setFocusedContentKeyPath(newKeyPath);\n }}\n >\n {addNodeButton.text}\n </Button>\n </div>\n );\n};\n\ntype StructureViewProps = {\n dictionaryLocalId: LocalDictionaryId;\n keyPath: KeyPath[];\n section: ContentNode;\n};\n\nexport const StructureView: FC<StructureViewProps> = ({\n section,\n keyPath,\n dictionaryLocalId,\n}) => {\n if (\n !section ||\n typeof section !== 'object' ||\n typeof section.nodeType === 'string'\n ) {\n return (\n <NodeView\n sectionKey=\"content\"\n section={section}\n keyPath={keyPath}\n dictionaryLocalId={dictionaryLocalId}\n />\n );\n }\n\n return (\n <ObjectView\n section={section}\n keyPath={keyPath}\n dictionaryLocalId={dictionaryLocalId}\n />\n );\n};\n"],"mappings":"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"}
|
package/dist/esm/components/DictionaryFieldEditor/VersionSwitcherDropDown/VersionSwitcher.mjs
CHANGED
|
@@ -15,7 +15,7 @@ const VersionSwitcher = ({ panelProps }) => {
|
|
|
15
15
|
const { selectedVersion, versions, setSelectedVersion } = useVersionSwitcher();
|
|
16
16
|
if (versions.length === 0) return /* @__PURE__ */ jsx(Fragment, {});
|
|
17
17
|
return /* @__PURE__ */ jsx("div", {
|
|
18
|
-
className: "rounded-xl border border-text text-
|
|
18
|
+
className: "rounded-xl border border-text text-foreground transition-colors",
|
|
19
19
|
"aria-label": versionListLabel.value,
|
|
20
20
|
children: /* @__PURE__ */ jsxs(DropDown, {
|
|
21
21
|
identifier: DROPDOWN_IDENTIFIER,
|
|
@@ -57,7 +57,7 @@ const VersionSwitcher = ({ panelProps }) => {
|
|
|
57
57
|
textAlign: "left",
|
|
58
58
|
size: "sm",
|
|
59
59
|
children: /* @__PURE__ */ jsx("div", {
|
|
60
|
-
className: "flex flex-1 flex-row items-center justify-between gap-3 px-2 py-1 text-
|
|
60
|
+
className: "flex flex-1 flex-row items-center justify-between gap-3 px-2 py-1 text-muted-foreground text-sm",
|
|
61
61
|
children: version
|
|
62
62
|
})
|
|
63
63
|
})
|
package/dist/esm/components/DictionaryFieldEditor/VersionSwitcherDropDown/VersionSwitcher.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VersionSwitcher.mjs","names":[],"sources":["../../../../../src/components/DictionaryFieldEditor/VersionSwitcherDropDown/VersionSwitcher.tsx"],"sourcesContent":["'use client';\n\nimport { Button } from '@components/Button';\nimport { Container } from '@components/Container';\nimport { DropDown, type PanelProps } from '@components/DropDown';\nimport { MoveVertical } from 'lucide-react';\nimport type { FC } from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { useVersionSwitcher } from './VersionSwitcherContext';\n\nexport type VersionSwitcherProps = {\n panelProps?: Omit<PanelProps, 'identifier'>;\n};\n\nconst DROPDOWN_IDENTIFIER = 'version-switcher';\n\nexport const VersionSwitcher: FC<VersionSwitcherProps> = ({ panelProps }) => {\n const { switchTo, versionSwitcherLabel, versionListLabel } =\n useIntlayer('version-switcher');\n const { selectedVersion, versions, setSelectedVersion } =\n useVersionSwitcher();\n\n if (versions.length === 0) {\n return <></>;\n }\n\n return (\n <div\n className=\"rounded-xl border border-text text-
|
|
1
|
+
{"version":3,"file":"VersionSwitcher.mjs","names":[],"sources":["../../../../../src/components/DictionaryFieldEditor/VersionSwitcherDropDown/VersionSwitcher.tsx"],"sourcesContent":["'use client';\n\nimport { Button } from '@components/Button';\nimport { Container } from '@components/Container';\nimport { DropDown, type PanelProps } from '@components/DropDown';\nimport { MoveVertical } from 'lucide-react';\nimport type { FC } from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { useVersionSwitcher } from './VersionSwitcherContext';\n\nexport type VersionSwitcherProps = {\n panelProps?: Omit<PanelProps, 'identifier'>;\n};\n\nconst DROPDOWN_IDENTIFIER = 'version-switcher';\n\nexport const VersionSwitcher: FC<VersionSwitcherProps> = ({ panelProps }) => {\n const { switchTo, versionSwitcherLabel, versionListLabel } =\n useIntlayer('version-switcher');\n const { selectedVersion, versions, setSelectedVersion } =\n useVersionSwitcher();\n\n if (versions.length === 0) {\n return <></>;\n }\n\n return (\n <div\n className=\"rounded-xl border border-text text-foreground transition-colors\"\n aria-label={versionListLabel.value}\n >\n <DropDown identifier={DROPDOWN_IDENTIFIER}>\n <DropDown.Trigger identifier={DROPDOWN_IDENTIFIER}>\n <div className=\"flex w-full items-center justify-between\">\n <div className=\"px-2\">{selectedVersion ?? 'LTS'}</div>\n <MoveVertical className=\"w-5 self-center\" />\n </div>\n </DropDown.Trigger>\n\n <DropDown.Panel\n identifier={DROPDOWN_IDENTIFIER}\n isOverable\n isFocusable\n className=\"right-0 left-auto\"\n {...panelProps}\n >\n <Container\n className=\"max-h-[80vh] min-w-28\"\n separator=\"y\"\n role=\"listbox\"\n transparency=\"xs\"\n border\n roundedSize=\"2xl\"\n borderColor=\"text\"\n aria-label={versionSwitcherLabel.value}\n >\n <ol className=\"divide-y divide-dashed divide-text/20 overflow-y-auto p-1\">\n {versions.reverse().map((version) => (\n <li className=\"px-1.5 py-1\" key={version}>\n <Button\n onClick={() => setSelectedVersion(version)}\n label={`${switchTo} v${version}`}\n isActive={selectedVersion === version}\n variant=\"hoverable\"\n color=\"text\"\n isFullWidth\n textAlign=\"left\"\n size=\"sm\"\n >\n <div className=\"flex flex-1 flex-row items-center justify-between gap-3 px-2 py-1 text-muted-foreground text-sm\">\n {version}\n </div>\n </Button>\n </li>\n ))}\n </ol>\n </Container>\n </DropDown.Panel>\n </DropDown>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;AAcA,MAAM,sBAAsB;AAE5B,MAAa,mBAA6C,EAAE,iBAAiB;CAC3E,MAAM,EAAE,UAAU,sBAAsB,qBACtC,YAAY,kBAAkB;CAChC,MAAM,EAAE,iBAAiB,UAAU,uBACjC,mBAAmB;CAErB,IAAI,SAAS,WAAW,GACtB,OAAO,+BAAI;CAGb,OACE,oBAAC,OAAD;EACE,WAAU;EACV,cAAY,iBAAiB;YAE7B,qBAAC,UAAD;GAAU,YAAY;aAAtB,CACE,oBAAC,SAAS,SAAV;IAAkB,YAAY;cAC5B,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,oBAAC,OAAD;MAAK,WAAU;gBAAQ,mBAAmB;KAAW,IACrD,oBAAC,cAAD,EAAc,WAAU,kBAAmB,EACxC;;GACW,IAElB,oBAAC,SAAS,OAAV;IACE,YAAY;IACZ;IACA;IACA,WAAU;IACV,GAAI;cAEJ,oBAAC,WAAD;KACE,WAAU;KACV,WAAU;KACV,MAAK;KACL,cAAa;KACb;KACA,aAAY;KACZ,aAAY;KACZ,cAAY,qBAAqB;eAEjC,oBAAC,MAAD;MAAI,WAAU;gBACX,SAAS,QAAQ,CAAC,CAAC,KAAK,YACvB,oBAAC,MAAD;OAAI,WAAU;iBACZ,oBAAC,QAAD;QACE,eAAe,mBAAmB,OAAO;QACzC,OAAO,GAAG,SAAS,IAAI;QACvB,UAAU,oBAAoB;QAC9B,SAAQ;QACR,OAAM;QACN;QACA,WAAU;QACV,MAAK;kBAEL,oBAAC,OAAD;SAAK,WAAU;mBACZ;QACE;OACC;MACN,GAf6B,OAe7B,CACL;KACC;IACK;GACG,EACR;;CACP;AAET"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { TextEditor, TextEditorContainer, traceKeys } from "./ContentEditorView/TextEditor.mjs";
|
|
2
|
-
import { KeyPathBreadcrumb } from "./KeyPathBreadcrumb.mjs";
|
|
3
|
-
import { ContentEditor } from "./ContentEditor.mjs";
|
|
4
2
|
import { DictionaryCreationForm } from "./DictionaryCreationForm/DictionaryCreationForm.mjs";
|
|
3
|
+
import { KeyPathBreadcrumb } from "./KeyPathBreadcrumb.mjs";
|
|
4
|
+
import { NodeEditor } from "./NodeEditor.mjs";
|
|
5
5
|
import { SaveForm } from "./SaveForm/SaveForm.mjs";
|
|
6
6
|
import { DictionaryFieldEditor } from "./DictionaryFieldEditor.mjs";
|
|
7
7
|
import { VersionSwitcherProvider, useVersionSwitcher } from "./VersionSwitcherDropDown/VersionSwitcherContext.mjs";
|
|
8
8
|
import { VersionSwitcher } from "./VersionSwitcherDropDown/VersionSwitcher.mjs";
|
|
9
9
|
|
|
10
|
-
export {
|
|
10
|
+
export { DictionaryCreationForm, DictionaryFieldEditor, KeyPathBreadcrumb, NodeEditor, SaveForm, TextEditor, TextEditorContainer, VersionSwitcher, VersionSwitcherProvider, traceKeys, useVersionSwitcher };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Status } from "../Status/index.mjs";
|
|
1
2
|
import { LogoWithTextBelow } from "../Logo/LogoWithTextBelow.mjs";
|
|
2
3
|
import { SocialNetworks } from "../SocialNetworks/index.mjs";
|
|
3
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -89,19 +90,20 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
89
90
|
* @returns A rendered footer with branding, social links, and optional navigation links
|
|
90
91
|
*/
|
|
91
92
|
const Footer = ({ links, footerText }) => /* @__PURE__ */ jsxs("footer", {
|
|
92
|
-
className: "flex flex-auto flex-row flex-wrap items-center justify-around gap-10 p-6",
|
|
93
|
+
className: "flex flex-auto flex-row flex-wrap items-center justify-around gap-10 border-neutral border-t p-6",
|
|
93
94
|
children: [/* @__PURE__ */ jsxs("aside", {
|
|
94
95
|
className: "flex flex-col items-center justify-between gap-3 md:w-1/4",
|
|
95
96
|
children: [
|
|
96
97
|
/* @__PURE__ */ jsx(LogoWithTextBelow, { className: "size-full max-w-30" }),
|
|
97
98
|
/* @__PURE__ */ jsx("span", {
|
|
98
|
-
className: "text-center text-
|
|
99
|
+
className: "text-center text-muted-foreground text-xs",
|
|
99
100
|
children: "© 2026 Intlayer, Inc."
|
|
100
101
|
}),
|
|
101
102
|
footerText && /* @__PURE__ */ jsx("span", {
|
|
102
|
-
className: "text-center text-
|
|
103
|
+
className: "text-center text-muted-foreground text-xs",
|
|
103
104
|
children: footerText
|
|
104
105
|
}),
|
|
106
|
+
/* @__PURE__ */ jsx(Status, {}),
|
|
105
107
|
/* @__PURE__ */ jsx("div", {
|
|
106
108
|
className: "flex flex-row gap-3",
|
|
107
109
|
children: /* @__PURE__ */ jsx(SocialNetworks, {})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/Footer/index.tsx"],"sourcesContent":["import type { FC, ReactNode } from 'react';\nimport { LogoWithTextBelow } from '../Logo';\nimport { SocialNetworks } from '../SocialNetworks';\n\n/**\n * Represents a single footer link with all necessary attributes\n */\nexport type FooterLink = {\n /** URL or path for the link */\n href: string;\n /** Display text or JSX element for the link */\n text: ReactNode;\n /** Optional click handler for custom link behavior */\n onClick?: () => void;\n /** Accessible label for screen readers (required for accessibility) */\n label: string;\n};\n\n/**\n * Represents a group of related links with a title\n */\nexport type LinkGroup = {\n /** Title for the group of links */\n title: ReactNode;\n /** Array of footer links in this group */\n links: FooterLink[];\n};\n\n/**\n * Props for the Footer component\n */\ntype FooterProps = {\n /** Optional array of link groups to display in the footer */\n links?: LinkGroup[];\n /** Optional extra content to display at the bottom of the footer (e.g. ICP registration number) */\n footerText?: ReactNode;\n};\n\n/**\n * Footer Component\n *\n * A comprehensive footer component that displays the Intlayer logo, copyright information,\n * social networks, and organized groups of navigation links. Designed for responsive\n * layouts and optimal user experience across all devices.\n *\n * @component\n * @example\n * Basic usage without links:\n * ```tsx\n * <Footer />\n * ```\n *\n * @example\n * With organized link groups:\n * ```tsx\n * const footerLinks = [\n * {\n * title: 'Product',\n * links: [\n * { href: '/features', text: 'Features', label: 'Product features' },\n * { href: '/pricing', text: 'Pricing', label: 'Pricing information' },\n * { href: '/docs', text: 'Documentation', label: 'Product documentation' }\n * ]\n * },\n * {\n * title: 'Company',\n * links: [\n * { href: '/about', text: 'About Us', label: 'About the company' },\n * { href: '/careers', text: 'Careers', label: 'Job opportunities' },\n * { href: '/contact', text: 'Contact', label: 'Contact information' }\n * ]\n * }\n * ];\n *\n * <Footer links={footerLinks} />\n * ```\n *\n * @example\n * With custom click handlers:\n * ```tsx\n * const footerLinks = [\n * {\n * title: 'Actions',\n * links: [\n * {\n * href: '#',\n * text: 'Newsletter',\n * label: 'Subscribe to newsletter',\n * onClick: () => openNewsletterModal()\n * }\n * ]\n * }\n * ];\n *\n * <Footer links={footerLinks} />\n * ```\n *\n * Features:\n * - Responsive design that adapts to different screen sizes\n * - Intlayer branding with logo and copyright notice\n * - Integrated social network links\n * - Flexible link organization with titled groups\n * - Accessibility-compliant with proper ARIA labels\n * - Support for custom click handlers alongside href navigation\n * - Clean, modern design with proper spacing and typography\n *\n * Layout Structure:\n * - Left side (or top on mobile): Logo, copyright, and social links\n * - Right side (or bottom on mobile): Organized link groups\n * - Responsive breakpoints for optimal mobile experience\n *\n * Accessibility:\n * - Semantic HTML structure using <footer> tag\n * - All links include required aria-label attributes\n * - Proper heading hierarchy and keyboard navigation\n * - Screen reader friendly content organization\n * - Focus management for interactive elements\n *\n * @param props - Component properties\n * @param props.links - Optional array of link groups to display\n *\n * @returns A rendered footer with branding, social links, and optional navigation links\n */\nexport const Footer: FC<FooterProps> = ({ links, footerText }) => (\n <footer className=\"flex flex-auto flex-row flex-wrap items-center justify-around gap-10 p-6\">\n <aside className=\"flex flex-col items-center justify-between gap-3 md:w-1/4\">\n <LogoWithTextBelow className=\"size-full max-w-30\" />\n <span className=\"text-center text-
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/Footer/index.tsx"],"sourcesContent":["import { Status } from '@components/Status';\nimport type { FC, ReactNode } from 'react';\nimport { LogoWithTextBelow } from '../Logo';\nimport { SocialNetworks } from '../SocialNetworks';\n\n/**\n * Represents a single footer link with all necessary attributes\n */\nexport type FooterLink = {\n /** URL or path for the link */\n href: string;\n /** Display text or JSX element for the link */\n text: ReactNode;\n /** Optional click handler for custom link behavior */\n onClick?: () => void;\n /** Accessible label for screen readers (required for accessibility) */\n label: string;\n};\n\n/**\n * Represents a group of related links with a title\n */\nexport type LinkGroup = {\n /** Title for the group of links */\n title: ReactNode;\n /** Array of footer links in this group */\n links: FooterLink[];\n};\n\n/**\n * Props for the Footer component\n */\ntype FooterProps = {\n /** Optional array of link groups to display in the footer */\n links?: LinkGroup[];\n /** Optional extra content to display at the bottom of the footer (e.g. ICP registration number) */\n footerText?: ReactNode;\n};\n\n/**\n * Footer Component\n *\n * A comprehensive footer component that displays the Intlayer logo, copyright information,\n * social networks, and organized groups of navigation links. Designed for responsive\n * layouts and optimal user experience across all devices.\n *\n * @component\n * @example\n * Basic usage without links:\n * ```tsx\n * <Footer />\n * ```\n *\n * @example\n * With organized link groups:\n * ```tsx\n * const footerLinks = [\n * {\n * title: 'Product',\n * links: [\n * { href: '/features', text: 'Features', label: 'Product features' },\n * { href: '/pricing', text: 'Pricing', label: 'Pricing information' },\n * { href: '/docs', text: 'Documentation', label: 'Product documentation' }\n * ]\n * },\n * {\n * title: 'Company',\n * links: [\n * { href: '/about', text: 'About Us', label: 'About the company' },\n * { href: '/careers', text: 'Careers', label: 'Job opportunities' },\n * { href: '/contact', text: 'Contact', label: 'Contact information' }\n * ]\n * }\n * ];\n *\n * <Footer links={footerLinks} />\n * ```\n *\n * @example\n * With custom click handlers:\n * ```tsx\n * const footerLinks = [\n * {\n * title: 'Actions',\n * links: [\n * {\n * href: '#',\n * text: 'Newsletter',\n * label: 'Subscribe to newsletter',\n * onClick: () => openNewsletterModal()\n * }\n * ]\n * }\n * ];\n *\n * <Footer links={footerLinks} />\n * ```\n *\n * Features:\n * - Responsive design that adapts to different screen sizes\n * - Intlayer branding with logo and copyright notice\n * - Integrated social network links\n * - Flexible link organization with titled groups\n * - Accessibility-compliant with proper ARIA labels\n * - Support for custom click handlers alongside href navigation\n * - Clean, modern design with proper spacing and typography\n *\n * Layout Structure:\n * - Left side (or top on mobile): Logo, copyright, and social links\n * - Right side (or bottom on mobile): Organized link groups\n * - Responsive breakpoints for optimal mobile experience\n *\n * Accessibility:\n * - Semantic HTML structure using <footer> tag\n * - All links include required aria-label attributes\n * - Proper heading hierarchy and keyboard navigation\n * - Screen reader friendly content organization\n * - Focus management for interactive elements\n *\n * @param props - Component properties\n * @param props.links - Optional array of link groups to display\n *\n * @returns A rendered footer with branding, social links, and optional navigation links\n */\nexport const Footer: FC<FooterProps> = ({ links, footerText }) => (\n <footer className=\"flex flex-auto flex-row flex-wrap items-center justify-around gap-10 border-neutral border-t p-6\">\n <aside className=\"flex flex-col items-center justify-between gap-3 md:w-1/4\">\n <LogoWithTextBelow className=\"size-full max-w-30\" />\n <span className=\"text-center text-muted-foreground text-xs\">\n © 2026 Intlayer, Inc.\n </span>\n {footerText && (\n <span className=\"text-center text-muted-foreground text-xs\">\n {footerText}\n </span>\n )}\n <Status />\n <div className=\"flex flex-row gap-3\">\n <SocialNetworks />\n </div>\n </aside>\n <div className=\"m-auto flex w-full flex-row flex-wrap justify-around gap-x-3 gap-y-6 md:w-2/3\">\n {(links ?? []).map(({ title, links }) => (\n <div\n className=\"flex flex-col gap-2\"\n key={links.map((link) => link.href).join(',')}\n >\n <strong>{title}</strong>\n <div className=\"flex flex-col gap-3 text-sm\">\n {links.map((link) => (\n <a key={link.href} href={link.href} aria-label={link.label}>\n {link.text}\n </a>\n ))}\n </div>\n </div>\n ))}\n </div>\n </footer>\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4HA,MAAa,UAA2B,EAAE,OAAO,iBAC/C,qBAAC,UAAD;CAAQ,WAAU;WAAlB,CACE,qBAAC,SAAD;EAAO,WAAU;YAAjB;GACE,oBAAC,mBAAD,EAAmB,WAAU,qBAAsB;GACnD,oBAAC,QAAD;IAAM,WAAU;cAA4C;GAEtD;GACL,cACC,oBAAC,QAAD;IAAM,WAAU;cACb;GACG;GAER,oBAAC,QAAD,CAAS;GACT,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,gBAAD,CAAiB;GACd;EACA;KACP,oBAAC,OAAD;EAAK,WAAU;aACX,SAAS,CAAC,EAAC,CAAE,KAAK,EAAE,OAAO,YAC3B,qBAAC,OAAD;GACE,WAAU;aADZ,CAIE,oBAAC,UAAD,YAAS,MAAc,IACvB,oBAAC,OAAD;IAAK,WAAU;cACZ,MAAM,KAAK,SACV,oBAAC,KAAD;KAAmB,MAAM,KAAK;KAAM,cAAY,KAAK;eAClD,KAAK;IACL,GAFK,KAAK,IAEV,CACJ;GACE,EACF;KAVE,MAAM,KAAK,SAAS,KAAK,IAAI,CAAC,CAAC,KAAK,GAAG,CAUzC,CACN;CACE,EACC"}
|
|
@@ -165,7 +165,7 @@ const HeightResizer = ({ initialHeight, maxHeight, minHeight = 0, isDisabled = f
|
|
|
165
165
|
isDisabled
|
|
166
166
|
]);
|
|
167
167
|
return /* @__PURE__ */ jsx("div", {
|
|
168
|
-
className: cn("relative h-full w-full border-dashed transition", !isDisabled && "max-h-[80%] cursor-ns-resize border-
|
|
168
|
+
className: cn("relative h-full w-full border-dashed transition", !isDisabled && "max-h-[80%] cursor-ns-resize border-t-[1px]", !isDisabled && "before:absolute before:top-0 before:left-1/2 before:z-10 before:block before:h-1 before:w-10 before:-translate-x-1/2 before:-translate-y-1/2 before:transform before:cursor-ns-resize before:rounded-full before:bg-muted-foreground before:transition before:content-[\"\"]", !isDisabled && "active:border-neutral-400 active:before:bg-neutral-400 dark:active:border-neutral-600 active:dark:before:bg-neutral-600", className),
|
|
169
169
|
style: {
|
|
170
170
|
height: isDisabled ? "100%" : `${height}px`,
|
|
171
171
|
maxHeight: isDisabled ? "100%" : maxHeight ? `${maxHeight}px` : void 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/HeightResizer/index.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport type React from 'react';\nimport {\n type DetailedHTMLProps,\n type FC,\n type HTMLAttributes,\n type PropsWithChildren,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\nconst HANDLE_DOUBLE_CLICK_ZONE_PX = 16;\n\n/**\n * Props for the HeightResizer component\n *\n * @interface HeightResizerProps\n */\ntype HeightResizerProps = {\n /**\n * Initial height in pixels for the resizable container\n * Sets the default size when the component first loads\n * @example 200\n */\n initialHeight: number;\n\n /**\n * Maximum height in pixels that the user can resize to (optional)\n * When undefined, no maximum limit is enforced\n * @example 500\n */\n maxHeight?: number;\n\n /**\n * Minimum height in pixels that the user can resize to (optional)\n * Prevents the container from being resized below this threshold\n * @default 0\n * @example 50\n */\n minHeight?: number;\n /**\n * Disable the resizer. When true, it behaves as a normal static container without drag handle or resizing capability.\n * @default false\n */\n isDisabled?: boolean;\n} & DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;\n\n/**\n * HeightResizer Component\n *\n * A resizable container component that allows users to dynamically adjust the height\n * by dragging a visual handle at the top. Provides smooth resizing with optional\n * minimum and maximum height constraints.\n *\n * ## Key Features\n * - **Interactive Resizing**: Drag handle to resize container vertically\n * - **Touch Support**: Full support for touch devices and mobile interactions\n * - **Height Constraints**: Optional minimum and maximum height limits\n * - **Visual Feedback**: Handle with hover and active states for clear interaction\n * - **Accessibility**: ARIA slider role with value announcements for screen readers\n * - **Smooth Animation**: CSS transitions for polished user experience\n *\n * ## Use Cases\n * - Code editors with resizable panels\n * - Chat interfaces with adjustable message areas\n * - Dashboard widgets with user-customizable sizes\n * - Documentation viewers with resizable content panes\n * - Settings panels with expandable sections\n *\n * ## Interaction Model\n * The component uses a drag interaction model where users click and drag the visual\n * handle (rounded bar) at the top of the container. The resize calculation is based\n * on the difference between the current cursor position and the container's top edge.\n *\n * ## Accessibility Features\n * - **ARIA Slider**: Proper slider role for assistive technologies\n * - **Value Announcements**: Current, min, and max values announced to screen readers\n * - **Keyboard Navigation**: Focusable with standard slider keyboard support\n * - **Visual Indicators**: Clear visual handle for drag interaction\n *\n * @component\n * @example\n * ```tsx\n * // Basic usage\n * <HeightResizer initialHeight={200}>\n * <div>Your resizable content here</div>\n * </HeightResizer>\n *\n * // With height constraints\n * <HeightResizer\n * initialHeight={300}\n * minHeight={100}\n * maxHeight={600}\n * >\n * <div>Content with size limits</div>\n * </HeightResizer>\n *\n * // In a code editor context\n * <HeightResizer\n * initialHeight={400}\n * minHeight={150}\n * className=\"border rounded-lg\"\n * >\n * <CodeEditor />\n * </HeightResizer>\n * ```\n *\n * @param props - HeightResizer component props\n * @param props.initialHeight - Starting height in pixels\n * @param props.minHeight - Optional minimum height constraint\n * @param props.maxHeight - Optional maximum height constraint\n * @param props.children - Content to display in the resizable container\n * @param props.className - Additional CSS classes for styling\n * @returns Interactive resizable container component\n */\nexport const HeightResizer: FC<PropsWithChildren<HeightResizerProps>> = ({\n initialHeight,\n maxHeight,\n minHeight = 0,\n isDisabled = false,\n children,\n className,\n ...props\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const [height, setHeight] = useState(initialHeight);\n const [isResizing, setIsResizing] = useState(false);\n const lastExpandedHeightRef = useRef(initialHeight);\n\n /**\n * Handler to initiate the resizing process\n * Prevents default browser behavior and sets the resizing state\n *\n * @param mouseDownEvent - Mouse or touch event from the drag handle\n */\n const startResizing = useCallback(\n (\n mouseDownEvent:\n | React.MouseEvent<HTMLDivElement>\n | React.TouchEvent<HTMLDivElement>\n ) => {\n if (isDisabled) return;\n setIsResizing(true);\n mouseDownEvent.preventDefault();\n },\n [isDisabled]\n );\n\n /**\n * Handler to stop the resizing process\n * Resets the resizing state when user releases the drag handle\n */\n const stopResizing = useCallback(() => {\n setIsResizing(false);\n }, []);\n\n /**\n * Core resize logic that calculates new height based on cursor position\n * Handles both mouse and touch events with boundary checking\n *\n * @param mouseMoveEvent - Mouse or touch move event during drag\n */\n const resize = useCallback(\n (mouseMoveEvent: MouseEvent | TouchEvent) => {\n const container = containerRef.current;\n if (isResizing && container) {\n const { height: containerHeight, top: containerTop } =\n container.getBoundingClientRect();\n\n let clientY = 0;\n if (mouseMoveEvent instanceof MouseEvent) {\n clientY = mouseMoveEvent.clientY;\n } else if (mouseMoveEvent instanceof TouchEvent) {\n clientY = mouseMoveEvent.touches[0].clientY;\n }\n\n const resizeDifference = clientY - containerTop;\n const newHeight = containerHeight - resizeDifference;\n\n // Apply height constraints\n let correctedHeight = Math.max(newHeight, minHeight);\n if (maxHeight !== undefined) {\n correctedHeight = Math.min(correctedHeight, maxHeight);\n }\n\n setHeight(correctedHeight);\n }\n },\n [isResizing, minHeight, maxHeight]\n );\n\n /**\n * Effect to manage global event listeners for resize interactions\n * Handles both mouse and touch events with proper cleanup\n */\n useEffect(() => {\n if (isDisabled) return;\n\n window.addEventListener('mousemove', resize, { passive: true });\n window.addEventListener('mouseup', stopResizing);\n window.addEventListener('touchmove', resize, { passive: true });\n window.addEventListener('touchend', stopResizing);\n\n return () => {\n window.removeEventListener('mousemove', resize);\n window.removeEventListener('mouseup', stopResizing);\n window.removeEventListener('touchmove', resize);\n window.removeEventListener('touchend', stopResizing);\n };\n }, [resize, stopResizing, isDisabled]);\n\n useEffect(() => {\n if (height > minHeight) {\n lastExpandedHeightRef.current = height;\n }\n }, [height, minHeight]);\n\n const handleDoubleClick = useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n const el = containerRef.current;\n if (!el) return;\n\n const { top } = el.getBoundingClientRect();\n if (event.clientY - top > HANDLE_DOUBLE_CLICK_ZONE_PX) return;\n\n event.preventDefault();\n event.stopPropagation();\n\n if (isDisabled) return;\n\n if (height > minHeight) {\n setHeight(minHeight);\n return;\n }\n\n const capped =\n maxHeight !== undefined\n ? Math.min(lastExpandedHeightRef.current, maxHeight)\n : lastExpandedHeightRef.current;\n setHeight(Math.max(capped, minHeight));\n },\n [height, maxHeight, minHeight, isDisabled]\n );\n\n return (\n <div\n className={cn(\n 'relative h-full w-full border-dashed transition',\n !isDisabled &&\n 'max-h-[80%] cursor-ns-resize border-neutral-200 border-t-[1px] dark:border-neutral-950',\n !isDisabled &&\n 'before:absolute before:top-0 before:left-1/2 before:z-10 before:block before:h-1 before:w-10 before:-translate-x-1/2 before:-translate-y-1/2 before:transform before:cursor-ns-resize before:rounded-full before:bg-neutral-200 before:transition before:content-[\"\"] dark:before:bg-neutral-950',\n !isDisabled &&\n 'active:border-neutral-400 active:before:bg-neutral-400 dark:active:border-neutral-600 active:dark:before:bg-neutral-600',\n className\n )}\n style={{\n height: isDisabled ? '100%' : `${height}px`,\n maxHeight: isDisabled\n ? '100%'\n : maxHeight\n ? `${maxHeight}px`\n : undefined,\n minHeight: isDisabled ? undefined : `${minHeight}px`,\n }}\n ref={containerRef}\n onMouseDown={isDisabled ? undefined : startResizing}\n onTouchStart={isDisabled ? undefined : startResizing}\n onDoubleClick={isDisabled ? undefined : handleDoubleClick}\n aria-valuemin={isDisabled ? undefined : minHeight}\n aria-valuemax={isDisabled ? undefined : maxHeight}\n aria-valuenow={isDisabled ? undefined : height}\n aria-label={\n isDisabled\n ? undefined\n : 'Resizable component - drag the handle to adjust height'\n }\n role={isDisabled ? 'none' : 'slider'}\n tabIndex={isDisabled ? undefined : 0}\n {...props}\n >\n {/* biome-ignore lint/a11y/noStaticElementInteractions: Stops content clicks from triggering resize on the parent slider */}\n <div\n role=\"presentation\"\n className=\"absolute top-0 left-0 size-full cursor-default overflow-hidden\"\n onMouseDown={(e) => e.stopPropagation()}\n onTouchStart={(e) => e.stopPropagation()}\n >\n {children}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/HeightResizer/index.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport type React from 'react';\nimport {\n type DetailedHTMLProps,\n type FC,\n type HTMLAttributes,\n type PropsWithChildren,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\nconst HANDLE_DOUBLE_CLICK_ZONE_PX = 16;\n\n/**\n * Props for the HeightResizer component\n *\n * @interface HeightResizerProps\n */\ntype HeightResizerProps = {\n /**\n * Initial height in pixels for the resizable container\n * Sets the default size when the component first loads\n * @example 200\n */\n initialHeight: number;\n\n /**\n * Maximum height in pixels that the user can resize to (optional)\n * When undefined, no maximum limit is enforced\n * @example 500\n */\n maxHeight?: number;\n\n /**\n * Minimum height in pixels that the user can resize to (optional)\n * Prevents the container from being resized below this threshold\n * @default 0\n * @example 50\n */\n minHeight?: number;\n /**\n * Disable the resizer. When true, it behaves as a normal static container without drag handle or resizing capability.\n * @default false\n */\n isDisabled?: boolean;\n} & DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;\n\n/**\n * HeightResizer Component\n *\n * A resizable container component that allows users to dynamically adjust the height\n * by dragging a visual handle at the top. Provides smooth resizing with optional\n * minimum and maximum height constraints.\n *\n * ## Key Features\n * - **Interactive Resizing**: Drag handle to resize container vertically\n * - **Touch Support**: Full support for touch devices and mobile interactions\n * - **Height Constraints**: Optional minimum and maximum height limits\n * - **Visual Feedback**: Handle with hover and active states for clear interaction\n * - **Accessibility**: ARIA slider role with value announcements for screen readers\n * - **Smooth Animation**: CSS transitions for polished user experience\n *\n * ## Use Cases\n * - Code editors with resizable panels\n * - Chat interfaces with adjustable message areas\n * - Dashboard widgets with user-customizable sizes\n * - Documentation viewers with resizable content panes\n * - Settings panels with expandable sections\n *\n * ## Interaction Model\n * The component uses a drag interaction model where users click and drag the visual\n * handle (rounded bar) at the top of the container. The resize calculation is based\n * on the difference between the current cursor position and the container's top edge.\n *\n * ## Accessibility Features\n * - **ARIA Slider**: Proper slider role for assistive technologies\n * - **Value Announcements**: Current, min, and max values announced to screen readers\n * - **Keyboard Navigation**: Focusable with standard slider keyboard support\n * - **Visual Indicators**: Clear visual handle for drag interaction\n *\n * @component\n * @example\n * ```tsx\n * // Basic usage\n * <HeightResizer initialHeight={200}>\n * <div>Your resizable content here</div>\n * </HeightResizer>\n *\n * // With height constraints\n * <HeightResizer\n * initialHeight={300}\n * minHeight={100}\n * maxHeight={600}\n * >\n * <div>Content with size limits</div>\n * </HeightResizer>\n *\n * // In a code editor context\n * <HeightResizer\n * initialHeight={400}\n * minHeight={150}\n * className=\"border rounded-lg\"\n * >\n * <CodeEditor />\n * </HeightResizer>\n * ```\n *\n * @param props - HeightResizer component props\n * @param props.initialHeight - Starting height in pixels\n * @param props.minHeight - Optional minimum height constraint\n * @param props.maxHeight - Optional maximum height constraint\n * @param props.children - Content to display in the resizable container\n * @param props.className - Additional CSS classes for styling\n * @returns Interactive resizable container component\n */\nexport const HeightResizer: FC<PropsWithChildren<HeightResizerProps>> = ({\n initialHeight,\n maxHeight,\n minHeight = 0,\n isDisabled = false,\n children,\n className,\n ...props\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const [height, setHeight] = useState(initialHeight);\n const [isResizing, setIsResizing] = useState(false);\n const lastExpandedHeightRef = useRef(initialHeight);\n\n /**\n * Handler to initiate the resizing process\n * Prevents default browser behavior and sets the resizing state\n *\n * @param mouseDownEvent - Mouse or touch event from the drag handle\n */\n const startResizing = useCallback(\n (\n mouseDownEvent:\n | React.MouseEvent<HTMLDivElement>\n | React.TouchEvent<HTMLDivElement>\n ) => {\n if (isDisabled) return;\n setIsResizing(true);\n mouseDownEvent.preventDefault();\n },\n [isDisabled]\n );\n\n /**\n * Handler to stop the resizing process\n * Resets the resizing state when user releases the drag handle\n */\n const stopResizing = useCallback(() => {\n setIsResizing(false);\n }, []);\n\n /**\n * Core resize logic that calculates new height based on cursor position\n * Handles both mouse and touch events with boundary checking\n *\n * @param mouseMoveEvent - Mouse or touch move event during drag\n */\n const resize = useCallback(\n (mouseMoveEvent: MouseEvent | TouchEvent) => {\n const container = containerRef.current;\n if (isResizing && container) {\n const { height: containerHeight, top: containerTop } =\n container.getBoundingClientRect();\n\n let clientY = 0;\n if (mouseMoveEvent instanceof MouseEvent) {\n clientY = mouseMoveEvent.clientY;\n } else if (mouseMoveEvent instanceof TouchEvent) {\n clientY = mouseMoveEvent.touches[0].clientY;\n }\n\n const resizeDifference = clientY - containerTop;\n const newHeight = containerHeight - resizeDifference;\n\n // Apply height constraints\n let correctedHeight = Math.max(newHeight, minHeight);\n if (maxHeight !== undefined) {\n correctedHeight = Math.min(correctedHeight, maxHeight);\n }\n\n setHeight(correctedHeight);\n }\n },\n [isResizing, minHeight, maxHeight]\n );\n\n /**\n * Effect to manage global event listeners for resize interactions\n * Handles both mouse and touch events with proper cleanup\n */\n useEffect(() => {\n if (isDisabled) return;\n\n window.addEventListener('mousemove', resize, { passive: true });\n window.addEventListener('mouseup', stopResizing);\n window.addEventListener('touchmove', resize, { passive: true });\n window.addEventListener('touchend', stopResizing);\n\n return () => {\n window.removeEventListener('mousemove', resize);\n window.removeEventListener('mouseup', stopResizing);\n window.removeEventListener('touchmove', resize);\n window.removeEventListener('touchend', stopResizing);\n };\n }, [resize, stopResizing, isDisabled]);\n\n useEffect(() => {\n if (height > minHeight) {\n lastExpandedHeightRef.current = height;\n }\n }, [height, minHeight]);\n\n const handleDoubleClick = useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n const el = containerRef.current;\n if (!el) return;\n\n const { top } = el.getBoundingClientRect();\n if (event.clientY - top > HANDLE_DOUBLE_CLICK_ZONE_PX) return;\n\n event.preventDefault();\n event.stopPropagation();\n\n if (isDisabled) return;\n\n if (height > minHeight) {\n setHeight(minHeight);\n return;\n }\n\n const capped =\n maxHeight !== undefined\n ? Math.min(lastExpandedHeightRef.current, maxHeight)\n : lastExpandedHeightRef.current;\n setHeight(Math.max(capped, minHeight));\n },\n [height, maxHeight, minHeight, isDisabled]\n );\n\n return (\n <div\n className={cn(\n 'relative h-full w-full border-dashed transition',\n !isDisabled && 'max-h-[80%] cursor-ns-resize border-t-[1px]',\n !isDisabled &&\n 'before:absolute before:top-0 before:left-1/2 before:z-10 before:block before:h-1 before:w-10 before:-translate-x-1/2 before:-translate-y-1/2 before:transform before:cursor-ns-resize before:rounded-full before:bg-muted-foreground before:transition before:content-[\"\"]',\n !isDisabled &&\n 'active:border-neutral-400 active:before:bg-neutral-400 dark:active:border-neutral-600 active:dark:before:bg-neutral-600',\n className\n )}\n style={{\n height: isDisabled ? '100%' : `${height}px`,\n maxHeight: isDisabled\n ? '100%'\n : maxHeight\n ? `${maxHeight}px`\n : undefined,\n minHeight: isDisabled ? undefined : `${minHeight}px`,\n }}\n ref={containerRef}\n onMouseDown={isDisabled ? undefined : startResizing}\n onTouchStart={isDisabled ? undefined : startResizing}\n onDoubleClick={isDisabled ? undefined : handleDoubleClick}\n aria-valuemin={isDisabled ? undefined : minHeight}\n aria-valuemax={isDisabled ? undefined : maxHeight}\n aria-valuenow={isDisabled ? undefined : height}\n aria-label={\n isDisabled\n ? undefined\n : 'Resizable component - drag the handle to adjust height'\n }\n role={isDisabled ? 'none' : 'slider'}\n tabIndex={isDisabled ? undefined : 0}\n {...props}\n >\n {/* biome-ignore lint/a11y/noStaticElementInteractions: Stops content clicks from triggering resize on the parent slider */}\n <div\n role=\"presentation\"\n className=\"absolute top-0 left-0 size-full cursor-default overflow-hidden\"\n onMouseDown={(e) => e.stopPropagation()}\n onTouchStart={(e) => e.stopPropagation()}\n >\n {children}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -105,7 +105,7 @@ const Code = ({ children, language, highlightedHtml, showHeader = true, showLine
|
|
|
105
105
|
transparency: "lg",
|
|
106
106
|
...props,
|
|
107
107
|
children: [showHeader && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
|
|
108
|
-
className: "grid w-full grid-cols-[1fr_auto] items-center justify-between rounded-t-xl bg-card/50 py-1.5 pr-12 pl-4 text-
|
|
108
|
+
className: "grid w-full grid-cols-[1fr_auto] items-center justify-between rounded-t-xl border-b bg-card/50 py-1.5 pr-12 pl-4 text-muted-foreground text-xs",
|
|
109
109
|
children: [/* @__PURE__ */ jsx("span", {
|
|
110
110
|
className: "truncate",
|
|
111
111
|
children: displayedFileName ?? language
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Code.mjs","names":[],"sources":["../../../../src/components/IDE/Code.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport type { FC, HTMLAttributes, ReactNode } from 'react';\nimport { useEffect, useMemo, useState } from 'react';\nimport { Container } from '../Container';\nimport { ExpandCollapse } from '../ExpandCollapse';\nimport { CodeBlock } from './CodeBlockClient';\nimport { CodeBlockHighlight } from './CodeBlockHighlight';\nimport { CodeConditionalRender } from './CodeConditionalRenderer';\nimport type {\n CodeFormat,\n ContentDeclarationFormat,\n PackageManager,\n} from './CodeContext';\nimport { useCodeContext } from './CodeContext';\nimport { CodeFormatSelector } from './CodeFormatSelector';\nimport { ContentDeclarationFormatSelector } from './ContentDeclarationFormatSelector';\nimport { CopyCode } from './CopyCode';\nimport { PackageManagerSelector } from './PackageManagerSelector';\nimport type { CodeLanguage } from './shikiLanguages';\n\nexport type CodeCompAttributes = {\n fileName?: string;\n packageManager?: PackageManager;\n /** Single format, a JSON-array string, or an array of formats. */\n codeFormat?: CodeFormat | string | string[];\n contentDeclarationFormat?: ContentDeclarationFormat | string | string[];\n};\n\ntype CodeCompProps = {\n children: ReactNode;\n fileName?: string;\n language: CodeLanguage;\n /**\n * Markup for `children`, highlighted by Shiki ahead of time. When present the\n * block paints straight from it and the highlighter is never downloaded —\n * except when the reader switches the block to a non-TypeScript format.\n */\n highlightedHtml?: string;\n showHeader?: boolean;\n showLineNumbers?: boolean;\n isRollable?: boolean;\n} & CodeCompAttributes &\n HTMLAttributes<HTMLDivElement>;\n\nconst MIN_HEIGHT = 700;\n\n/** Languages that use JSX syntax — CommonJS doesn't make sense for these. */\nconst JSX_LANGUAGES = new Set(['tsx', 'jsx']);\n\n/** Parse a codeFormat prop that may be a single value, a JSON-array string, or already an array. */\nconst parseFormats = (\n raw: string | string[] | undefined\n): CodeFormat[] | undefined => {\n if (!raw) return undefined;\n if (Array.isArray(raw)) return raw as CodeFormat[];\n if (typeof raw === 'string' && raw.startsWith('[')) {\n try {\n const parsed = JSON.parse(raw);\n if (Array.isArray(parsed)) return parsed as CodeFormat[];\n } catch {\n /* ignore */\n }\n }\n return [raw as CodeFormat];\n};\n\nexport const Code: FC<CodeCompProps> = ({\n children,\n language,\n highlightedHtml,\n showHeader = true,\n showLineNumbers = true,\n className,\n fileName,\n packageManager,\n codeFormat: rawCodeFormat,\n contentDeclarationFormat: rawContentDeclarationFormat,\n isRollable = true,\n lang, // Destructure html lang prop to prevent passing invalid BCP-47 language tag to Code component\n ...props\n}) => {\n const {\n codeFormat: selectedCodeFormat,\n contentDeclarationFormat: selectedContentDeclarationFormat,\n } = useCodeContext();\n\n // Parse whichever attribute is present as an array of formats.\n const codeFormats = useMemo(\n () => parseFormats(rawCodeFormat as string | undefined),\n [rawCodeFormat]\n );\n const contentFormats = useMemo(\n () => parseFormats(rawContentDeclarationFormat as string | undefined),\n [rawContentDeclarationFormat]\n );\n\n // A block is \"multi-format\" when it has multiple formats including TypeScript\n // (the canonical source). Such blocks transform at runtime.\n const isMultiCodeFormat =\n codeFormats !== undefined &&\n codeFormats.length > 1 &&\n codeFormats.includes('typescript');\n\n const isMultiContentFormat =\n contentFormats !== undefined &&\n contentFormats.length > 1 &&\n contentFormats.includes('typescript');\n\n const isMultiFormat = isMultiCodeFormat || isMultiContentFormat;\n\n // Determine which context format drives this block's selection.\n // content declaration blocks use selectedContentDeclarationFormat;\n // regular code blocks use selectedCodeFormat.\n const selectedFormat: CodeFormat = isMultiContentFormat\n ? (selectedContentDeclarationFormat as CodeFormat)\n : selectedCodeFormat;\n\n // The formats actually relevant for transformation (no 'json', no 'commonjs' for JSX).\n const effectiveFormats = useMemo<CodeFormat[] | undefined>(() => {\n const base = isMultiContentFormat ? contentFormats : codeFormats;\n if (!base) return base;\n let filtered = base.filter((f) => f !== 'json') as CodeFormat[];\n if (JSX_LANGUAGES.has(language as string)) {\n filtered = filtered.filter((f) => f !== 'commonjs');\n }\n return filtered;\n }, [isMultiContentFormat, contentFormats, codeFormats, language]);\n\n // When the globally-selected format isn't valid for this block\n // (e.g. CJS selected but this is a JSX file), fall back to the last valid one.\n const resolvedFormat: Exclude<CodeFormat, 'json'> = useMemo(() => {\n if (!effectiveFormats || effectiveFormats.includes(selectedFormat)) {\n return selectedFormat as Exclude<CodeFormat, 'json'>;\n }\n return (effectiveFormats[effectiveFormats.length - 1] ??\n 'typescript') as Exclude<CodeFormat, 'json'>;\n }, [effectiveFormats, selectedFormat]);\n\n // ── Async filename derivation (dynamic import of transformer) ──────────────\n // We derive the displayed fileName so the header updates when format changes.\n // deriveFileName is tiny but it lives inside codeTransformer, which is only\n // imported when actually needed (non-TypeScript selection).\n const [displayedFileName, setDisplayedFileName] = useState<\n string | undefined\n >(fileName);\n\n useEffect(() => {\n if (!isMultiFormat || resolvedFormat === 'typescript') {\n setDisplayedFileName(fileName);\n return;\n }\n if (!fileName) return;\n\n let cancelled = false;\n (async () => {\n const { deriveFileName } = await import('./codeTransformer');\n if (!cancelled)\n setDisplayedFileName(deriveFileName(fileName, resolvedFormat));\n })();\n return () => {\n cancelled = true;\n };\n }, [fileName, isMultiFormat, resolvedFormat]);\n\n // ── Async copy text (transformed code for CopyCode) ───────────────────────\n const rawCode = useMemo(() => {\n const code = Array.isArray(children) ? children.join('') : String(children);\n return code.endsWith('\\n') ? code.slice(0, -1) : code;\n }, [children]);\n\n const [copyCode, setCopyCode] = useState<string>(rawCode);\n\n useEffect(() => {\n if (!isMultiFormat || resolvedFormat === 'typescript') {\n setCopyCode(rawCode);\n return;\n }\n let cancelled = false;\n (async () => {\n const { transformCode } = await import('./codeTransformer');\n if (!cancelled) setCopyCode(transformCode(rawCode, resolvedFormat));\n })();\n return () => {\n cancelled = true;\n };\n }, [rawCode, isMultiFormat, resolvedFormat]);\n\n const hadSelectInHeader =\n packageManager || rawCodeFormat || rawContentDeclarationFormat;\n\n return (\n <CodeConditionalRender\n packageManager={packageManager}\n codeFormat={rawCodeFormat as string | undefined}\n contentDeclarationFormat={\n rawContentDeclarationFormat as string | undefined\n }\n >\n <Container\n className={cn(\n 'relative min-w-0 max-w-full text-sm leading-6',\n showLineNumbers && 'with-line-number ml-0',\n className\n )}\n transparency=\"lg\"\n {...props}\n >\n {showHeader && (\n <>\n <div className=\"grid w-full grid-cols-[1fr_auto] items-center justify-between rounded-t-xl bg-card/50 py-1.5 pr-12 pl-4 text-neutral text-xs\">\n <span className=\"truncate\">{displayedFileName ?? language}</span>\n <div className=\"flex items-center gap-2\">\n {packageManager && <PackageManagerSelector />}\n {rawCodeFormat && (\n <CodeFormatSelector availableFormats={effectiveFormats} />\n )}\n {rawContentDeclarationFormat && (\n <ContentDeclarationFormatSelector />\n )}\n </div>\n </div>\n <div className=\"sticky top-46 z-20\">\n <div\n className={cn(\n 'absolute right-2 bottom-0 flex h-7 items-center',\n hadSelectInHeader && 'h-11'\n )}\n >\n <CopyCode code={copyCode} />\n </div>\n </div>\n </>\n )}\n <ExpandCollapse\n minHeight={MIN_HEIGHT}\n isRollable={isRollable}\n className=\"min-w-0 max-w-full overflow-x-auto p-2\"\n >\n {isMultiFormat ? (\n /*\n * Multi-format: CodeBlockHighlight manages both the transformation\n * (dynamic import of codeTransformer) and the Shiki highlighting in\n * a single useEffect. The previous highlighted output stays visible\n * while the new one loads — no white-text flash.\n */\n <CodeBlockHighlight\n originalLang={language}\n targetFormat={resolvedFormat}\n initialHtml={highlightedHtml}\n >\n {rawCode}\n </CodeBlockHighlight>\n ) : (\n /*\n * Single-format: paint the pre-highlighted markup when it was\n * supplied, otherwise fall back to the Suspense-based async Shiki\n * renderer.\n */\n <CodeBlock lang={language} highlightedHtml={highlightedHtml}>\n {rawCode}\n </CodeBlock>\n )}\n </ExpandCollapse>\n </Container>\n </CodeConditionalRender>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Code.mjs","names":[],"sources":["../../../../src/components/IDE/Code.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport type { FC, HTMLAttributes, ReactNode } from 'react';\nimport { useEffect, useMemo, useState } from 'react';\nimport { Container } from '../Container';\nimport { ExpandCollapse } from '../ExpandCollapse';\nimport { CodeBlock } from './CodeBlockClient';\nimport { CodeBlockHighlight } from './CodeBlockHighlight';\nimport { CodeConditionalRender } from './CodeConditionalRenderer';\nimport type {\n CodeFormat,\n ContentDeclarationFormat,\n PackageManager,\n} from './CodeContext';\nimport { useCodeContext } from './CodeContext';\nimport { CodeFormatSelector } from './CodeFormatSelector';\nimport { ContentDeclarationFormatSelector } from './ContentDeclarationFormatSelector';\nimport { CopyCode } from './CopyCode';\nimport { PackageManagerSelector } from './PackageManagerSelector';\nimport type { CodeLanguage } from './shikiLanguages';\n\nexport type CodeCompAttributes = {\n fileName?: string;\n packageManager?: PackageManager;\n /** Single format, a JSON-array string, or an array of formats. */\n codeFormat?: CodeFormat | string | string[];\n contentDeclarationFormat?: ContentDeclarationFormat | string | string[];\n};\n\ntype CodeCompProps = {\n children: ReactNode;\n fileName?: string;\n language: CodeLanguage;\n /**\n * Markup for `children`, highlighted by Shiki ahead of time. When present the\n * block paints straight from it and the highlighter is never downloaded —\n * except when the reader switches the block to a non-TypeScript format.\n */\n highlightedHtml?: string;\n showHeader?: boolean;\n showLineNumbers?: boolean;\n isRollable?: boolean;\n} & CodeCompAttributes &\n HTMLAttributes<HTMLDivElement>;\n\nconst MIN_HEIGHT = 700;\n\n/** Languages that use JSX syntax — CommonJS doesn't make sense for these. */\nconst JSX_LANGUAGES = new Set(['tsx', 'jsx']);\n\n/** Parse a codeFormat prop that may be a single value, a JSON-array string, or already an array. */\nconst parseFormats = (\n raw: string | string[] | undefined\n): CodeFormat[] | undefined => {\n if (!raw) return undefined;\n if (Array.isArray(raw)) return raw as CodeFormat[];\n if (typeof raw === 'string' && raw.startsWith('[')) {\n try {\n const parsed = JSON.parse(raw);\n if (Array.isArray(parsed)) return parsed as CodeFormat[];\n } catch {\n /* ignore */\n }\n }\n return [raw as CodeFormat];\n};\n\nexport const Code: FC<CodeCompProps> = ({\n children,\n language,\n highlightedHtml,\n showHeader = true,\n showLineNumbers = true,\n className,\n fileName,\n packageManager,\n codeFormat: rawCodeFormat,\n contentDeclarationFormat: rawContentDeclarationFormat,\n isRollable = true,\n lang, // Destructure html lang prop to prevent passing invalid BCP-47 language tag to Code component\n ...props\n}) => {\n const {\n codeFormat: selectedCodeFormat,\n contentDeclarationFormat: selectedContentDeclarationFormat,\n } = useCodeContext();\n\n // Parse whichever attribute is present as an array of formats.\n const codeFormats = useMemo(\n () => parseFormats(rawCodeFormat as string | undefined),\n [rawCodeFormat]\n );\n const contentFormats = useMemo(\n () => parseFormats(rawContentDeclarationFormat as string | undefined),\n [rawContentDeclarationFormat]\n );\n\n // A block is \"multi-format\" when it has multiple formats including TypeScript\n // (the canonical source). Such blocks transform at runtime.\n const isMultiCodeFormat =\n codeFormats !== undefined &&\n codeFormats.length > 1 &&\n codeFormats.includes('typescript');\n\n const isMultiContentFormat =\n contentFormats !== undefined &&\n contentFormats.length > 1 &&\n contentFormats.includes('typescript');\n\n const isMultiFormat = isMultiCodeFormat || isMultiContentFormat;\n\n // Determine which context format drives this block's selection.\n // content declaration blocks use selectedContentDeclarationFormat;\n // regular code blocks use selectedCodeFormat.\n const selectedFormat: CodeFormat = isMultiContentFormat\n ? (selectedContentDeclarationFormat as CodeFormat)\n : selectedCodeFormat;\n\n // The formats actually relevant for transformation (no 'json', no 'commonjs' for JSX).\n const effectiveFormats = useMemo<CodeFormat[] | undefined>(() => {\n const base = isMultiContentFormat ? contentFormats : codeFormats;\n if (!base) return base;\n let filtered = base.filter((f) => f !== 'json') as CodeFormat[];\n if (JSX_LANGUAGES.has(language as string)) {\n filtered = filtered.filter((f) => f !== 'commonjs');\n }\n return filtered;\n }, [isMultiContentFormat, contentFormats, codeFormats, language]);\n\n // When the globally-selected format isn't valid for this block\n // (e.g. CJS selected but this is a JSX file), fall back to the last valid one.\n const resolvedFormat: Exclude<CodeFormat, 'json'> = useMemo(() => {\n if (!effectiveFormats || effectiveFormats.includes(selectedFormat)) {\n return selectedFormat as Exclude<CodeFormat, 'json'>;\n }\n return (effectiveFormats[effectiveFormats.length - 1] ??\n 'typescript') as Exclude<CodeFormat, 'json'>;\n }, [effectiveFormats, selectedFormat]);\n\n // ── Async filename derivation (dynamic import of transformer) ──────────────\n // We derive the displayed fileName so the header updates when format changes.\n // deriveFileName is tiny but it lives inside codeTransformer, which is only\n // imported when actually needed (non-TypeScript selection).\n const [displayedFileName, setDisplayedFileName] = useState<\n string | undefined\n >(fileName);\n\n useEffect(() => {\n if (!isMultiFormat || resolvedFormat === 'typescript') {\n setDisplayedFileName(fileName);\n return;\n }\n if (!fileName) return;\n\n let cancelled = false;\n (async () => {\n const { deriveFileName } = await import('./codeTransformer');\n if (!cancelled)\n setDisplayedFileName(deriveFileName(fileName, resolvedFormat));\n })();\n return () => {\n cancelled = true;\n };\n }, [fileName, isMultiFormat, resolvedFormat]);\n\n // ── Async copy text (transformed code for CopyCode) ───────────────────────\n const rawCode = useMemo(() => {\n const code = Array.isArray(children) ? children.join('') : String(children);\n return code.endsWith('\\n') ? code.slice(0, -1) : code;\n }, [children]);\n\n const [copyCode, setCopyCode] = useState<string>(rawCode);\n\n useEffect(() => {\n if (!isMultiFormat || resolvedFormat === 'typescript') {\n setCopyCode(rawCode);\n return;\n }\n let cancelled = false;\n (async () => {\n const { transformCode } = await import('./codeTransformer');\n if (!cancelled) setCopyCode(transformCode(rawCode, resolvedFormat));\n })();\n return () => {\n cancelled = true;\n };\n }, [rawCode, isMultiFormat, resolvedFormat]);\n\n const hadSelectInHeader =\n packageManager || rawCodeFormat || rawContentDeclarationFormat;\n\n return (\n <CodeConditionalRender\n packageManager={packageManager}\n codeFormat={rawCodeFormat as string | undefined}\n contentDeclarationFormat={\n rawContentDeclarationFormat as string | undefined\n }\n >\n <Container\n className={cn(\n 'relative min-w-0 max-w-full text-sm leading-6',\n showLineNumbers && 'with-line-number ml-0',\n className\n )}\n transparency=\"lg\"\n {...props}\n >\n {showHeader && (\n <>\n <div className=\"grid w-full grid-cols-[1fr_auto] items-center justify-between rounded-t-xl border-b bg-card/50 py-1.5 pr-12 pl-4 text-muted-foreground text-xs\">\n <span className=\"truncate\">{displayedFileName ?? language}</span>\n <div className=\"flex items-center gap-2\">\n {packageManager && <PackageManagerSelector />}\n {rawCodeFormat && (\n <CodeFormatSelector availableFormats={effectiveFormats} />\n )}\n {rawContentDeclarationFormat && (\n <ContentDeclarationFormatSelector />\n )}\n </div>\n </div>\n <div className=\"sticky top-46 z-20\">\n <div\n className={cn(\n 'absolute right-2 bottom-0 flex h-7 items-center',\n hadSelectInHeader && 'h-11'\n )}\n >\n <CopyCode code={copyCode} />\n </div>\n </div>\n </>\n )}\n <ExpandCollapse\n minHeight={MIN_HEIGHT}\n isRollable={isRollable}\n className=\"min-w-0 max-w-full overflow-x-auto p-2\"\n >\n {isMultiFormat ? (\n /*\n * Multi-format: CodeBlockHighlight manages both the transformation\n * (dynamic import of codeTransformer) and the Shiki highlighting in\n * a single useEffect. The previous highlighted output stays visible\n * while the new one loads — no white-text flash.\n */\n <CodeBlockHighlight\n originalLang={language}\n targetFormat={resolvedFormat}\n initialHtml={highlightedHtml}\n >\n {rawCode}\n </CodeBlockHighlight>\n ) : (\n /*\n * Single-format: paint the pre-highlighted markup when it was\n * supplied, otherwise fall back to the Suspense-based async Shiki\n * renderer.\n */\n <CodeBlock lang={language} highlightedHtml={highlightedHtml}>\n {rawCode}\n </CodeBlock>\n )}\n </ExpandCollapse>\n </Container>\n </CodeConditionalRender>\n );\n};\n"],"mappings":"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"}
|