From 24eb249f9f40878e39aba191fcfdc6393517aeec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96ZT=C3=9CRK?= <76204082+iamsedatozturk@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:17:59 +0300 Subject: [PATCH] =?UTF-8?q?Custom=20component=20g=C3=BCncellemeleri?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Seeds/LanguagesData.json | 6 + .../Seeds/MenusData.json | 16 - ui/src/components/codeLayout/StyleModal.tsx | 100 +++++ .../components/codeLayout/TailwindModal.tsx | 79 ---- .../componentEditor/PlatformViewHost.tsx | 3 +- ui/src/components/ui/Card/Card.tsx | 46 ++- .../visualDesigner/VisualCanvas.tsx | 122 ++++-- ui/src/components/visualDesigner/catalog.ts | 10 +- .../visualDesigner/codeGenerator.ts | 71 +++- ui/src/components/visualDesigner/types.ts | 2 - ui/src/contexts/ComponentContext.tsx | 21 +- ui/src/routes/route.constant.ts | 2 - ui/src/views/developerKit/ComponentEditor.tsx | 114 ++---- .../developerKit/ComponentEditorPage.tsx | 17 +- .../views/developerKit/ComponentManager.tsx | 48 ++- .../developerKit/VisualComponentDesigner.tsx | 357 ++++++++++-------- ui/src/views/intranet/Dashboard.tsx | 2 +- ui/src/views/list/List.tsx | 16 +- ui/src/views/list/useListFormColumns.ts | 31 +- .../views/list/useListFormCustomDataSource.ts | 19 +- 20 files changed, 644 insertions(+), 438 deletions(-) create mode 100644 ui/src/components/codeLayout/StyleModal.tsx delete mode 100644 ui/src/components/codeLayout/TailwindModal.tsx diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json b/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json index 49df728e..dc53d793 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json +++ b/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json @@ -14040,6 +14040,12 @@ "en": "Open URL", "tr": "URL'yi Aç" }, + { + "resourceName": "Platform", + "key": "Reset", + "en": "Reset", + "tr": "Sıfırla" + }, { "resourceName": "Platform", "key": "App.Listform.ListformField.ApplicationName", diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/MenusData.json b/api/src/Sozsoft.Platform.DbMigrator/Seeds/MenusData.json index ce3ad019..d464c417 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Seeds/MenusData.json +++ b/api/src/Sozsoft.Platform.DbMigrator/Seeds/MenusData.json @@ -376,22 +376,6 @@ "routeType": "protected", "authority": ["App.DeveloperKit.Components"] }, - { - "key": "admin.developerkit.components.new", - "path": "/admin/developerkit/components/new", - "componentType": "normal", - "componentPath": "@/views/developerKit/ComponentEditorPage", - "routeType": "protected", - "authority": ["App.DeveloperKit.Components"] - }, - { - "key": "admin.developerkit.components.view", - "path": "/admin/developerkit/components/view/:id", - "componentType": "normal", - "componentPath": "@/views/developerKit/ComponentEditorPage", - "routeType": "protected", - "authority": ["App.DeveloperKit.Components"] - }, { "key": "admin.developerkit.components.edit", "path": "/admin/developerkit/components/edit/:id", diff --git a/ui/src/components/codeLayout/StyleModal.tsx b/ui/src/components/codeLayout/StyleModal.tsx new file mode 100644 index 00000000..469c5b50 --- /dev/null +++ b/ui/src/components/codeLayout/StyleModal.tsx @@ -0,0 +1,100 @@ +import { useMemo, useState } from 'react' +import { FaPlus, FaSearch, FaTimes } from 'react-icons/fa' +import { Button } from '@/components/ui' + +interface StyleModalProps { + isOpen: boolean + mode: 'class' | 'css' + propertyName: string + value?: string + onChange: (value: string) => void + onClose: () => void +} + +const STYLE_GROUPS = { + Display: ['block', 'inline-block', 'inline', 'flex', 'inline-flex', 'grid', 'inline-grid', 'contents', 'hidden'], + Layout: ['container', 'box-border', 'box-content', 'overflow-auto', 'overflow-hidden', 'overflow-visible', 'overflow-scroll', 'overflow-x-auto', 'overflow-y-auto'], + Position: ['static', 'fixed', 'absolute', 'relative', 'sticky', 'inset-0', 'top-0', 'right-0', 'bottom-0', 'left-0', 'z-0', 'z-10', 'z-20', 'z-30', 'z-40', 'z-50'], + Flexbox: ['flex-row', 'flex-row-reverse', 'flex-col', 'flex-col-reverse', 'flex-wrap', 'flex-nowrap', 'grow', 'grow-0', 'shrink', 'shrink-0', 'items-start', 'items-center', 'items-end', 'items-stretch', 'justify-start', 'justify-center', 'justify-end', 'justify-between', 'justify-around', 'justify-evenly', 'self-auto', 'self-start', 'self-center', 'self-end', 'self-stretch'], + Grid: ['grid-cols-1', 'grid-cols-2', 'grid-cols-3', 'grid-cols-4', 'grid-cols-5', 'grid-cols-6', 'grid-cols-12', 'col-auto', 'col-span-1', 'col-span-2', 'col-span-3', 'col-span-4', 'col-span-6', 'col-span-12', 'grid-rows-1', 'grid-rows-2', 'grid-rows-3', 'row-span-1', 'row-span-2', 'row-span-3'], + Spacing: ['m-0', 'm-1', 'm-2', 'm-3', 'm-4', 'm-5', 'm-6', 'm-8', 'm-auto', 'mx-auto', 'my-auto', 'mt-1', 'mt-2', 'mt-3', 'mt-4', 'mr-1', 'mr-2', 'mr-4', 'mb-1', 'mb-2', 'mb-4', 'ml-1', 'ml-2', 'ml-4', 'p-0', 'p-1', 'p-2', 'p-3', 'p-4', 'p-5', 'p-6', 'p-8', 'px-1', 'px-2', 'px-3', 'px-4', 'px-6', 'py-1', 'py-2', 'py-3', 'py-4', 'py-6', 'gap-0', 'gap-1', 'gap-2', 'gap-3', 'gap-4', 'gap-6', 'gap-8'], + Sizing: ['w-0', 'w-auto', 'w-full', 'w-screen', 'w-1/2', 'w-1/3', 'w-2/3', 'w-1/4', 'w-3/4', 'h-0', 'h-auto', 'h-full', 'h-screen', 'min-w-0', 'min-w-full', 'min-h-0', 'min-h-full', 'max-w-xs', 'max-w-sm', 'max-w-md', 'max-w-lg', 'max-w-xl', 'max-w-2xl', 'max-w-full', 'max-h-full', 'aspect-square', 'aspect-video'], + Typography: ['text-xs', 'text-sm', 'text-base', 'text-lg', 'text-xl', 'text-2xl', 'text-3xl', 'font-thin', 'font-light', 'font-normal', 'font-medium', 'font-semibold', 'font-bold', 'italic', 'not-italic', 'underline', 'line-through', 'no-underline', 'text-left', 'text-center', 'text-right', 'uppercase', 'lowercase', 'capitalize', 'truncate', 'whitespace-normal', 'whitespace-nowrap', 'break-words', 'leading-none', 'leading-tight', 'leading-normal', 'tracking-tight', 'tracking-normal', 'tracking-wide'], + Colors: ['text-transparent', 'text-white', 'text-black', 'text-slate-400', 'text-slate-500', 'text-slate-600', 'text-slate-700', 'text-slate-800', 'text-slate-900', 'text-sky-500', 'text-blue-600', 'text-red-500', 'text-amber-500', 'text-emerald-500', 'bg-transparent', 'bg-white', 'bg-black', 'bg-slate-50', 'bg-slate-100', 'bg-slate-200', 'bg-slate-700', 'bg-slate-800', 'bg-slate-900', 'bg-sky-500', 'bg-blue-600', 'bg-red-500', 'bg-amber-500', 'bg-emerald-500'], + Border: ['border', 'border-0', 'border-2', 'border-t', 'border-r', 'border-b', 'border-l', 'border-transparent', 'border-slate-200', 'border-slate-300', 'border-slate-700', 'border-sky-500', 'rounded-none', 'rounded-sm', 'rounded', 'rounded-md', 'rounded-lg', 'rounded-xl', 'rounded-2xl', 'rounded-full', 'divide-x', 'divide-y'], + Effects: ['shadow-none', 'shadow-sm', 'shadow', 'shadow-md', 'shadow-lg', 'shadow-xl', 'opacity-0', 'opacity-50', 'opacity-75', 'opacity-100', 'blur-sm', 'blur', 'backdrop-blur-sm', 'backdrop-blur', 'transition', 'transition-all', 'duration-150', 'duration-300', 'ease-in', 'ease-out', 'ease-in-out'], + Interaction: ['cursor-auto', 'cursor-default', 'cursor-pointer', 'cursor-not-allowed', 'select-none', 'select-text', 'pointer-events-none', 'pointer-events-auto', 'resize', 'resize-none', 'outline-none', 'focus:outline-none', 'hover:opacity-80', 'hover:bg-slate-100', 'focus:ring-2', 'focus:ring-sky-500'], + Responsive: ['sm:block', 'sm:flex', 'sm:grid', 'sm:hidden', 'md:block', 'md:flex', 'md:grid', 'md:hidden', 'lg:block', 'lg:flex', 'lg:grid', 'lg:hidden', 'xl:block', 'xl:flex', 'xl:grid', 'dark:bg-slate-900', 'dark:bg-slate-950', 'dark:text-white', 'dark:text-slate-300', 'dark:border-slate-700'], +} as const + +const splitClasses = (value: string) => value.split(/\s+/).filter(Boolean) + +const StyleModal = ({ isOpen, mode, propertyName, value = '', onChange, onClose }: StyleModalProps) => { + const [search, setSearch] = useState('') + const [customClass, setCustomClass] = useState('') + const [category, setCategory] = useState('Tümü') + const selected = useMemo(() => new Set(splitClasses(value)), [value]) + const classes = useMemo(() => { + const source = category === 'Tümü' ? Object.values(STYLE_GROUPS).flat() : STYLE_GROUPS[category] + const query = search.trim().toLowerCase() + return [...new Set(source)].filter((name) => !query || name.toLowerCase().includes(query)) + }, [category, search]) + + if (!isOpen) return null + + const toggleClass = (className: string) => { + const next = new Set(selected) + if (next.has(className)) next.delete(className) + else next.add(className) + onChange([...next].join(' ')) + } + const addCustomClass = () => { + const additions = splitClasses(customClass) + if (!additions.length) return + onChange([...new Set([...selected, ...additions])].join(' ')) + setCustomClass('') + } + + return ( +
+
event.stopPropagation()}> +
+
+

StyleModal · {propertyName}

+

{mode === 'css' ? 'CSS kodunu doğrudan düzenleyin.' : 'Hazır sınıfları seçin veya CSS/Tailwind class adlarını elle düzenleyin.'}

+
+
+ {mode === 'class' &&
+ + +
} + {mode === 'class' &&
+ setCustomClass(event.target.value)} onKeyDown={(event) => { if (event.key === 'Enter') addCustomClass() }} /> + +
} +
+ +