mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2026-04-06 01:25:33 +00:00
a lot of ui fixes
This commit is contained in:
parent
30069973e5
commit
758730b523
@ -38,7 +38,7 @@ export default function NavBar({ appName }: { appName: string }) {
|
|||||||
<div className='tw:flex-1 tw:mr-2'>
|
<div className='tw:flex-1 tw:mr-2'>
|
||||||
<div
|
<div
|
||||||
className={'tw:flex-1 tw:truncate tw:grid tw:grid-flow-col'}
|
className={'tw:flex-1 tw:truncate tw:grid tw:grid-flow-col'}
|
||||||
style={{ maxWidth: nameWidth + 60 }}
|
style={{ maxWidth: nameWidth + 62 }}
|
||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
className='tw:btn tw:btn-ghost tw:px-2 tw:normal-case tw:text-xl tw:flex-1 tw:truncate'
|
className='tw:btn tw:btn-ghost tw:px-2 tw:normal-case tw:text-xl tw:flex-1 tw:truncate'
|
||||||
|
|||||||
@ -13,7 +13,6 @@ interface RichTextEditorProps {
|
|||||||
labelTitle?: string
|
labelTitle?: string
|
||||||
labelStyle?: string
|
labelStyle?: string
|
||||||
containerStyle?: string
|
containerStyle?: string
|
||||||
inputStyle?: string
|
|
||||||
defaultValue: string
|
defaultValue: string
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
required?: boolean
|
required?: boolean
|
||||||
|
|||||||
@ -6,13 +6,14 @@ import H3Icon from '@heroicons/react/24/solid/H3Icon'
|
|||||||
import ItalicIcon from '@heroicons/react/24/solid/ItalicIcon'
|
import ItalicIcon from '@heroicons/react/24/solid/ItalicIcon'
|
||||||
import ListBulletIcon from '@heroicons/react/24/solid/ListBulletIcon'
|
import ListBulletIcon from '@heroicons/react/24/solid/ListBulletIcon'
|
||||||
import NumberedListIcon from '@heroicons/react/24/solid/NumberedListIcon'
|
import NumberedListIcon from '@heroicons/react/24/solid/NumberedListIcon'
|
||||||
import { Editor, useEditorState } from '@tiptap/react'
|
import { useEditorState } from '@tiptap/react'
|
||||||
import { FaQuoteLeft } from 'react-icons/fa6'
|
import { FaQuoteLeft } from 'react-icons/fa6'
|
||||||
import { MdUndo, MdRedo, MdHorizontalRule } from 'react-icons/md'
|
import { MdUndo, MdRedo, MdHorizontalRule } from 'react-icons/md'
|
||||||
|
import { LiaTextHeightSolid } from 'react-icons/lia'
|
||||||
|
|
||||||
|
import type { Editor } from '@tiptap/react'
|
||||||
|
|
||||||
export const TextEditorMenu = ({ editor }: { editor: Editor }) => {
|
export const TextEditorMenu = ({ editor }: { editor: Editor }) => {
|
||||||
|
|
||||||
|
|
||||||
const editorState = useEditorState({
|
const editorState = useEditorState({
|
||||||
editor,
|
editor,
|
||||||
selector: (ctx) => {
|
selector: (ctx) => {
|
||||||
@ -57,10 +58,10 @@ export const TextEditorMenu = ({ editor }: { editor: Editor }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ul className='tw:menu tw:menu-horizontal tw:flex-none tw:bg-base-200 tw:rounded-box tw:w-full tw:rounded-b-none'>
|
<ul className='tw:menu tw:p-1 tw:menu-horizontal tw:flex-none tw:bg-base-200 tw:rounded-box tw:w-full tw:rounded-b-none'>
|
||||||
<li>
|
<li>
|
||||||
<div
|
<div
|
||||||
className={`tw:tooltip ${editorState.canUndo ? '' : 'tw:opacity-50'}`}
|
className={`tw:tooltip tw:px-1 ${editorState.canUndo ? '' : 'tw:opacity-50'}`}
|
||||||
data-tip='Undo'
|
data-tip='Undo'
|
||||||
onClick={() => editor.chain().focus().undo().run()}
|
onClick={() => editor.chain().focus().undo().run()}
|
||||||
>
|
>
|
||||||
@ -69,7 +70,7 @@ export const TextEditorMenu = ({ editor }: { editor: Editor }) => {
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div
|
<div
|
||||||
className={`tw:tooltip ${editorState.canRedo ? '' : 'tw:opacity-50'}`}
|
className={`tw:tooltip tw:px-1 ${editorState.canRedo ? '' : 'tw:opacity-50'}`}
|
||||||
data-tip='Redo'
|
data-tip='Redo'
|
||||||
onClick={() => editor.chain().focus().redo().run()}
|
onClick={() => editor.chain().focus().redo().run()}
|
||||||
>
|
>
|
||||||
@ -78,7 +79,7 @@ export const TextEditorMenu = ({ editor }: { editor: Editor }) => {
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div
|
<div
|
||||||
className={`tw:tooltip ${editorState.isBold ? 'tw:bg-base-content/10' : ''}`}
|
className={`tw:tooltip tw:px-1 ${editorState.isBold ? 'tw:bg-base-content/10' : ''}`}
|
||||||
data-tip='Bold'
|
data-tip='Bold'
|
||||||
onClick={() => editor.chain().focus().toggleBold().run()}
|
onClick={() => editor.chain().focus().toggleBold().run()}
|
||||||
>
|
>
|
||||||
@ -87,7 +88,7 @@ export const TextEditorMenu = ({ editor }: { editor: Editor }) => {
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div
|
<div
|
||||||
className={`tw:tooltip ${editorState.isItalic ? 'tw:bg-base-content/10' : ''}`}
|
className={`tw:tooltip tw:px-1 ${editorState.isItalic ? 'tw:bg-base-content/10' : ''}`}
|
||||||
data-tip='Italic'
|
data-tip='Italic'
|
||||||
onClick={() => editor.chain().focus().toggleItalic().run()}
|
onClick={() => editor.chain().focus().toggleItalic().run()}
|
||||||
>
|
>
|
||||||
@ -95,35 +96,45 @@ export const TextEditorMenu = ({ editor }: { editor: Editor }) => {
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div
|
<details className='tw:z-10000'>
|
||||||
className={`tw:tooltip ${editorState.isHeading1 ? 'tw:bg-base-content/10' : ''}`}
|
<summary>
|
||||||
data-tip='Heading 1'
|
<LiaTextHeightSolid className='tw:w-5 tw:h-5' />
|
||||||
onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}
|
</summary>
|
||||||
>
|
<ul>
|
||||||
<H1Icon className='tw:w-5 tw:h-5' />
|
<li>
|
||||||
</div>
|
<div
|
||||||
|
className={`tw:tooltip tw:px-1 ${editorState.isHeading1 ? 'tw:bg-base-content/10' : ''}`}
|
||||||
|
data-tip='Heading 1'
|
||||||
|
onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}
|
||||||
|
>
|
||||||
|
<H1Icon className='tw:w-5 tw:h-5' />
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div
|
||||||
|
className={`tw:tooltip tw:px-1 ${editorState.isHeading2 ? 'tw:bg-base-content/10' : ''}`}
|
||||||
|
data-tip='Heading 2'
|
||||||
|
onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
|
||||||
|
>
|
||||||
|
<H2Icon className='tw:w-5 tw:h-5' />
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div
|
||||||
|
className={`tw:tooltip tw:px-1 ${editorState.isHeading3 ? 'tw:bg-base-content/10' : ''}`}
|
||||||
|
data-tip='Heading 3'
|
||||||
|
onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}
|
||||||
|
>
|
||||||
|
<H3Icon className='tw:w-5 tw:h-5' />
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<div
|
<div
|
||||||
className={`tw:tooltip ${editorState.isHeading2 ? 'tw:bg-base-content/10' : ''}`}
|
className={`tw:tooltip tw:px-1 ${editorState.isBulletList ? 'tw:bg-base-content/10' : ''}`}
|
||||||
data-tip='Heading 2'
|
|
||||||
onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
|
|
||||||
>
|
|
||||||
<H2Icon className='tw:w-5 tw:h-5' />
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<div
|
|
||||||
className={`tw:tooltip ${editorState.isHeading3 ? 'tw:bg-base-content/10' : ''}`}
|
|
||||||
data-tip='Heading 3'
|
|
||||||
onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}
|
|
||||||
>
|
|
||||||
<H3Icon className='tw:w-5 tw:h-5' />
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<div
|
|
||||||
className={`tw:tooltip ${editorState.isBulletList ? 'tw:bg-base-content/10' : ''}`}
|
|
||||||
data-tip='List'
|
data-tip='List'
|
||||||
onClick={() => editor.chain().focus().toggleBulletList().run()}
|
onClick={() => editor.chain().focus().toggleBulletList().run()}
|
||||||
>
|
>
|
||||||
@ -132,7 +143,7 @@ export const TextEditorMenu = ({ editor }: { editor: Editor }) => {
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div
|
<div
|
||||||
className={`tw:tooltip ${editorState.isOrderedList ? 'tw:bg-base-content/10' : ''}`}
|
className={`tw:tooltip tw:px-1 ${editorState.isOrderedList ? 'tw:bg-base-content/10' : ''}`}
|
||||||
data-tip='List'
|
data-tip='List'
|
||||||
onClick={() => editor.chain().focus().toggleOrderedList().run()}
|
onClick={() => editor.chain().focus().toggleOrderedList().run()}
|
||||||
>
|
>
|
||||||
@ -141,16 +152,7 @@ export const TextEditorMenu = ({ editor }: { editor: Editor }) => {
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div
|
<div
|
||||||
className={`tw:tooltip ${editorState.isCodeBlock ? 'tw:bg-base-content/10' : ''}`}
|
className={`tw:tooltip tw:px-1 ${editorState.isBlockquote ? 'tw:bg-base-content/10' : ''}`}
|
||||||
data-tip='Code'
|
|
||||||
onClick={() => editor.chain().focus().toggleCodeBlock().run()}
|
|
||||||
>
|
|
||||||
<CodeBracketIcon className='tw:w-5 tw:h-5' />
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<div
|
|
||||||
className={`tw:tooltip ${editorState.isBlockquote ? 'tw:bg-base-content/10' : ''}`}
|
|
||||||
data-tip='Quote'
|
data-tip='Quote'
|
||||||
onClick={() => editor.chain().focus().toggleBlockquote().run()}
|
onClick={() => editor.chain().focus().toggleBlockquote().run()}
|
||||||
>
|
>
|
||||||
@ -159,7 +161,7 @@ export const TextEditorMenu = ({ editor }: { editor: Editor }) => {
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div
|
<div
|
||||||
className='tw:tooltip'
|
className='tw:tooltip tw:px-1'
|
||||||
data-tip='Horizontal Line'
|
data-tip='Horizontal Line'
|
||||||
onClick={() => editor.chain().focus().setHorizontalRule().run()}
|
onClick={() => editor.chain().focus().setHorizontalRule().run()}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export const PopupStartEndInput = ({
|
|||||||
placeholder='start'
|
placeholder='start'
|
||||||
dataField='start'
|
dataField='start'
|
||||||
inputStyle='tw:text-sm tw:px-2'
|
inputStyle='tw:text-sm tw:px-2'
|
||||||
labelTitle={showLabels ? 'start' : ''}
|
labelTitle={showLabels ? 'Start' : ''}
|
||||||
defaultValue={item && item.start ? item.start.substring(0, 10) : ''}
|
defaultValue={item && item.start ? item.start.substring(0, 10) : ''}
|
||||||
autocomplete='one-time-code'
|
autocomplete='one-time-code'
|
||||||
updateFormValue={updateStartValue}
|
updateFormValue={updateStartValue}
|
||||||
@ -36,7 +36,7 @@ export const PopupStartEndInput = ({
|
|||||||
placeholder='end'
|
placeholder='end'
|
||||||
dataField='end'
|
dataField='end'
|
||||||
inputStyle='tw:text-sm tw:px-2'
|
inputStyle='tw:text-sm tw:px-2'
|
||||||
labelTitle={showLabels ? 'end' : ''}
|
labelTitle={showLabels ? 'End' : ''}
|
||||||
defaultValue={item && item.end ? item.end.substring(0, 10) : ''}
|
defaultValue={item && item.end ? item.end.substring(0, 10) : ''}
|
||||||
autocomplete='one-time-code'
|
autocomplete='one-time-code'
|
||||||
updateFormValue={updateEndValue}
|
updateFormValue={updateEndValue}
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export const PopupTextInput = ({
|
|||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
inputStyle={style}
|
inputStyle={style}
|
||||||
type='text'
|
type='text'
|
||||||
containerStyle={'tw:mt-4'}
|
containerStyle={'tw:mt-4 tw:mb-2'}
|
||||||
></TextInput>
|
></TextInput>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -158,7 +158,7 @@ export function ProfileForm() {
|
|||||||
<>
|
<>
|
||||||
<MapOverlayPage
|
<MapOverlayPage
|
||||||
backdrop
|
backdrop
|
||||||
className='tw:mx-4 tw:mt-4 tw:mb-4 tw:overflow-x-hidden tw:w-[calc(100%-32px)] tw:md:w-[calc(50%-32px)] tw:max-w-3xl tw:left-auto! tw:top-0 tw:bottom-0 tw:flex tw:flex-col tw:max-h-[calc(1200px)]'
|
className='tw:mx-4 tw:mt-4 tw:mb-4 tw:overflow-x-hidden tw:w-[calc(100%-32px)] tw:md:w-[calc(50%-32px)] tw:max-w-3xl tw:left-auto! tw:top-0 tw:bottom-0 tw:flex tw:flex-col tw:max-h-[calc(1000px)]'
|
||||||
>
|
>
|
||||||
<form
|
<form
|
||||||
className='tw:flex tw:flex-col tw:flex-1 tw:min-h-0'
|
className='tw:flex tw:flex-col tw:flex-1 tw:min-h-0'
|
||||||
@ -204,7 +204,7 @@ export function ProfileForm() {
|
|||||||
></TabsForm>
|
></TabsForm>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className='tw:mb-4 tw:mt-6 tw:flex-none'>
|
<div className='tw:mt-6 tw:flex-none'>
|
||||||
<button
|
<button
|
||||||
className={`${loading ? ' tw:loading tw:btn tw:float-right' : 'tw:btn tw:float-right'}`}
|
className={`${loading ? ' tw:loading tw:btn tw:float-right' : 'tw:btn tw:float-right'}`}
|
||||||
type='submit'
|
type='submit'
|
||||||
|
|||||||
@ -174,7 +174,7 @@ export function ProfileView({ attestationApi }: { attestationApi?: ItemsApi<any>
|
|||||||
{item && (
|
{item && (
|
||||||
<MapOverlayPage
|
<MapOverlayPage
|
||||||
key={item.id}
|
key={item.id}
|
||||||
className={`tw:p-0! tw:overflow-scroll tw:m-4! tw:md:w-[calc(50%-32px)] tw:w-[calc(100%-32px)] tw:min-w-80 tw:max-w-3xl tw:left-0! tw:sm:left-auto! tw:top-0 tw:bottom-0 tw:transition-opacity tw:duration-500 ${!selectPosition ? 'tw:opacity-100 tw:pointer-events-auto' : 'tw:opacity-0 tw:pointer-events-none'}`}
|
className={`tw:p-0! tw:overflow-scroll tw:m-4! tw:md:w-[calc(50%-32px)] tw:w-[calc(100%-32px)] tw:min-w-80 tw:max-w-3xl tw:left-0! tw:sm:left-auto! tw:top-0 tw:bottom-0 tw:transition-opacity tw:duration-500 ${!selectPosition ? 'tw:opacity-100 tw:pointer-events-auto' : 'tw:opacity-0 tw:pointer-events-none'} tw:max-h-[1000px]`}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
<div className={'tw:px-6 tw:pt-6'}>
|
<div className={'tw:px-6 tw:pt-6'}>
|
||||||
|
|||||||
@ -38,7 +38,7 @@ export const ProfileTextForm = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`tw:min-h-32 tw:flex tw:flex-col tw:mt-2 ${size === 'full' ? 'tw:flex-1' : 'tw:h-32 tw:flex-none'}`}
|
className={`tw:min-h-36 tw:max-h-156 tw:flex tw:flex-col tw:mt-2 ${size === 'full' ? 'tw:flex-1' : 'tw:h-36 tw:flex-none'}`}
|
||||||
>
|
>
|
||||||
<div className='tw:flex tw:justify-between tw:items-center'>
|
<div className='tw:flex tw:justify-between tw:items-center'>
|
||||||
<label
|
<label
|
||||||
@ -60,7 +60,7 @@ export const ProfileTextForm = ({
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
labelStyle={hideInputLabel ? 'tw:hidden' : ''}
|
labelStyle={hideInputLabel ? 'tw:hidden' : ''}
|
||||||
containerStyle={size === 'full' ? 'tw:flex-1' : 'tw:h-28 tw:flex-none'}
|
containerStyle={size === 'full' ? 'tw:flex-1' : 'tw:h-32 tw:flex-none'}
|
||||||
required={required}
|
required={required}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -33,6 +33,9 @@ export const TagsWidget = ({ placeholder, containerStyle, defaultTags, onUpdate
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onKeyDown = (e) => {
|
const onKeyDown = (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
const { key } = e
|
const { key } = e
|
||||||
const trimmedInput = input.trim()
|
const trimmedInput = input.trim()
|
||||||
|
|
||||||
@ -42,7 +45,6 @@ export const TagsWidget = ({ placeholder, containerStyle, defaultTags, onUpdate
|
|||||||
// eslint-disable-next-line react/prop-types
|
// eslint-disable-next-line react/prop-types
|
||||||
!defaultTags.some((tag) => tag.name.toLocaleLowerCase() === trimmedInput.toLocaleLowerCase())
|
!defaultTags.some((tag) => tag.name.toLocaleLowerCase() === trimmedInput.toLocaleLowerCase())
|
||||||
) {
|
) {
|
||||||
e.preventDefault()
|
|
||||||
const newTag = tags.find((t) => t.name === trimmedInput.toLocaleLowerCase())
|
const newTag = tags.find((t) => t.name === trimmedInput.toLocaleLowerCase())
|
||||||
newTag && onUpdate([...currentTags, newTag])
|
newTag && onUpdate([...currentTags, newTag])
|
||||||
!newTag &&
|
!newTag &&
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
/* eslint-disable @typescript-eslint/restrict-plus-operands */
|
/* eslint-disable @typescript-eslint/restrict-plus-operands */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||||
/* eslint-disable react/prop-types */
|
/* eslint-disable react/prop-types */
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
import { RichTextEditor } from '#components/Input/RichTextEditor'
|
import { RichTextEditor } from '#components/Input/RichTextEditor'
|
||||||
@ -15,6 +14,7 @@ import { PopupStartEndInput, TextView } from '#components/Map/Subcomponents/Item
|
|||||||
import { ActionButton } from '#components/Profile/Subcomponents/ActionsButton'
|
import { ActionButton } from '#components/Profile/Subcomponents/ActionsButton'
|
||||||
import { LinkedItemsHeaderView } from '#components/Profile/Subcomponents/LinkedItemsHeaderView'
|
import { LinkedItemsHeaderView } from '#components/Profile/Subcomponents/LinkedItemsHeaderView'
|
||||||
import { TagsWidget } from '#components/Profile/Subcomponents/TagsWidget'
|
import { TagsWidget } from '#components/Profile/Subcomponents/TagsWidget'
|
||||||
|
import { Tabs } from '#components/Templates/Tabs'
|
||||||
|
|
||||||
export const TabsForm = ({
|
export const TabsForm = ({
|
||||||
item,
|
item,
|
||||||
@ -26,114 +26,70 @@ export const TabsForm = ({
|
|||||||
loading,
|
loading,
|
||||||
setUrlParams,
|
setUrlParams,
|
||||||
}) => {
|
}) => {
|
||||||
const [activeTab, setActiveTab] = useState<number>(1)
|
|
||||||
const navigate = useNavigate()
|
|
||||||
const updateItem = useUpdateItem()
|
const updateItem = useUpdateItem()
|
||||||
|
const navigate = useNavigate()
|
||||||
const updateActiveTab = useCallback(
|
|
||||||
(id: number) => {
|
|
||||||
setActiveTab(id)
|
|
||||||
|
|
||||||
const params = new URLSearchParams(window.location.search)
|
|
||||||
|
|
||||||
params.set('tab', `${id}`)
|
|
||||||
const newUrl = location.pathname + '?' + params.toString()
|
|
||||||
window.history.pushState({}, '', newUrl)
|
|
||||||
setUrlParams(params)
|
|
||||||
},
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
[location.pathname],
|
|
||||||
)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const params = new URLSearchParams(location.search)
|
|
||||||
const urlTab = params.get('tab')
|
|
||||||
setActiveTab(urlTab ? Number(urlTab) : 1)
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [location.search])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='tw:grow tw:flex tw:flex-col tw:min-h-0'>
|
<div className='tw:flex tw:flex-col tw:flex-1 tw:min-h-0 tw:mt-4'>
|
||||||
<div role='tablist' className='tw:tabs tw:flex tw:flex-1 tw:min-h-0 tw:tabs-lift tw:mt-3'>
|
<Tabs
|
||||||
<input
|
setUrlParams={setUrlParams}
|
||||||
type='radio'
|
items={[
|
||||||
name='my_tabs_2'
|
{
|
||||||
role='tab'
|
title: 'Info',
|
||||||
className={'tw:tab'}
|
component: (
|
||||||
aria-label='Info'
|
<div
|
||||||
checked={activeTab === 1 && true}
|
className={`tw:flex tw:flex-col tw:flex-1 tw:min-h-0 ${item.layer.itemType.show_start_end_input && 'tw:pt-4'}`}
|
||||||
onChange={() => updateActiveTab(1)}
|
>
|
||||||
/>
|
{item.layer.itemType.show_start_end_input && (
|
||||||
<div
|
<PopupStartEndInput
|
||||||
role='tabpanel'
|
item={item}
|
||||||
className='tw:!flex tw:!flex-col tw:tab-content tw:bg-base-100 tw:border-(--fallback-bc,oklch(var(--bc)/0.2)) tw:rounded-box tw:!h-[calc(100%-48px)] tw:min-h-56 tw:border-none'
|
showLabels={true}
|
||||||
>
|
updateEndValue={(e) =>
|
||||||
<div
|
setState((prevState) => ({
|
||||||
className={`tw:flex tw:flex-col tw:flex-1 tw:min-h-0 ${item.layer.itemType.show_start_end_input && 'tw:pt-4'}`}
|
...prevState,
|
||||||
>
|
end: e,
|
||||||
{item.layer.itemType.show_start_end_input && (
|
}))
|
||||||
<PopupStartEndInput
|
}
|
||||||
item={item}
|
updateStartValue={(s) =>
|
||||||
showLabels={false}
|
setState((prevState) => ({
|
||||||
updateEndValue={(e) =>
|
...prevState,
|
||||||
setState((prevState) => ({
|
start: s,
|
||||||
...prevState,
|
}))
|
||||||
end: e,
|
}
|
||||||
}))
|
></PopupStartEndInput>
|
||||||
}
|
)}
|
||||||
updateStartValue={(s) =>
|
|
||||||
setState((prevState) => ({
|
|
||||||
...prevState,
|
|
||||||
start: s,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
></PopupStartEndInput>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<RichTextEditor
|
<RichTextEditor
|
||||||
labelTitle='About me'
|
labelTitle='About'
|
||||||
placeholder='about ...'
|
placeholder='about ...'
|
||||||
defaultValue={item?.text ? item.text : ''}
|
defaultValue={item?.text ? item.text : ''}
|
||||||
updateFormValue={(v) =>
|
updateFormValue={(v) =>
|
||||||
setState((prevState) => ({
|
setState((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
text: v,
|
text: v,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
containerStyle='tw:pt-2 tw:grow'
|
containerStyle='tw:pt-2 tw:flex-1 tw:min-h-36 tw:max-h-136'
|
||||||
inputStyle={`tw:h-full ${!item.layer.itemType.show_start_end_input && 'tw:border-t-0 tw:rounded-tl-none'}`}
|
/>
|
||||||
/>
|
<RichTextEditor
|
||||||
<RichTextEditor
|
labelTitle='Contact Info'
|
||||||
labelTitle='Contact Info'
|
placeholder='contact info ...'
|
||||||
placeholder='contact info ...'
|
defaultValue={state.contact || ''}
|
||||||
defaultValue={state.contact || ''}
|
updateFormValue={(c) =>
|
||||||
updateFormValue={(c) =>
|
setState((prevState) => ({
|
||||||
setState((prevState) => ({
|
...prevState,
|
||||||
...prevState,
|
contact: c,
|
||||||
contact: c,
|
}))
|
||||||
}))
|
}
|
||||||
}
|
containerStyle='tw:pt-2 tw:h-36 tw:flex-none'
|
||||||
inputStyle=''
|
required={false}
|
||||||
containerStyle='tw:pt-2 tw:h-36 tw:flex-none'
|
/>
|
||||||
required={false}
|
</div>
|
||||||
/>
|
),
|
||||||
</div>
|
},
|
||||||
</div>
|
{
|
||||||
{item.layer?.itemType.offers_and_needs && (
|
title: 'Offers & Needs',
|
||||||
<>
|
component: (
|
||||||
<input
|
|
||||||
type='radio'
|
|
||||||
name='my_tabs_2'
|
|
||||||
role='tab'
|
|
||||||
className={'tw:tab tw:min-w-[10em] '}
|
|
||||||
aria-label='Offers & Needs'
|
|
||||||
checked={activeTab === 3 && true}
|
|
||||||
onChange={() => updateActiveTab(3)}
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
role='tabpanel'
|
|
||||||
className='tw:tab-content tw:bg-base-100 tw:border-(--fallback-bc,oklch(var(--bc)/0.2)) tw:rounded-box tw:!h-[calc(100%-48px)] tw:min-h-56 tw:border-none'
|
|
||||||
>
|
|
||||||
<div className='tw:h-full'>
|
<div className='tw:h-full'>
|
||||||
<div className='tw:w-full tw:h-[calc(50%-0.75em)] tw:mb-4'>
|
<div className='tw:w-full tw:h-[calc(50%-0.75em)] tw:mb-4'>
|
||||||
<TagsWidget
|
<TagsWidget
|
||||||
@ -162,24 +118,11 @@ export const TabsForm = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
),
|
||||||
</>
|
},
|
||||||
)}
|
{
|
||||||
{item.layer?.itemType.relations && (
|
title: 'Links',
|
||||||
<>
|
component: (
|
||||||
<input
|
|
||||||
type='radio'
|
|
||||||
name='my_tabs_2'
|
|
||||||
role='tab'
|
|
||||||
className='tw:tab '
|
|
||||||
aria-label='Links'
|
|
||||||
checked={activeTab === 7 && true}
|
|
||||||
onChange={() => updateActiveTab(7)}
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
role='tabpanel'
|
|
||||||
className='tw:tab-content tw:rounded-box tw:!h-[calc(100%-48px)] tw:overflow-y-auto tw:pt-4 tw:overflow-x-hidden fade'
|
|
||||||
>
|
|
||||||
<div className='tw:h-full'>
|
<div className='tw:h-full'>
|
||||||
<div className='tw:grid tw:grid-cols-1 tw:sm:grid-cols-2 tw:md:grid-cols-1 tw:lg:grid-cols-1 tw:xl:grid-cols-1 tw:2xl:grid-cols-2 tw:mb-4'>
|
<div className='tw:grid tw:grid-cols-1 tw:sm:grid-cols-2 tw:md:grid-cols-1 tw:lg:grid-cols-1 tw:xl:grid-cols-1 tw:2xl:grid-cols-2 tw:mb-4'>
|
||||||
{state.relations &&
|
{state.relations &&
|
||||||
@ -211,10 +154,10 @@ export const TabsForm = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
),
|
||||||
</>
|
},
|
||||||
)}
|
]}
|
||||||
</div>
|
></Tabs>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -91,9 +91,9 @@ export const TabsView = ({
|
|||||||
name='my_tabs_2'
|
name='my_tabs_2'
|
||||||
role='tab'
|
role='tab'
|
||||||
className={'tw:tab tw:font-bold tw:ps-2! tw:pe-2! '}
|
className={'tw:tab tw:font-bold tw:ps-2! tw:pe-2! '}
|
||||||
aria-label={`${item.layer?.itemType.icon_as_labels && activeTab !== 1 ? '📝' : '📝\u00A0Info'}`}
|
aria-label={`${item.layer?.itemType.icon_as_labels && activeTab !== 0 ? '📝' : '📝\u00A0Info'}`}
|
||||||
checked={activeTab === 1 && true}
|
checked={activeTab === 0 && true}
|
||||||
onChange={() => updateActiveTab(1)}
|
onChange={() => updateActiveTab(0)}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
role='tabpanel'
|
role='tabpanel'
|
||||||
@ -115,9 +115,9 @@ export const TabsView = ({
|
|||||||
name='my_tabs_2'
|
name='my_tabs_2'
|
||||||
role='tab'
|
role='tab'
|
||||||
className={'tw:tab tw:font-bold tw:ps-2! tw:pe-2!'}
|
className={'tw:tab tw:font-bold tw:ps-2! tw:pe-2!'}
|
||||||
aria-label={`${item.layer.itemType.icon_as_labels && activeTab !== 2 ? '❤️' : '❤️\u00A0Trust'}`}
|
aria-label={`${item.layer.itemType.icon_as_labels && activeTab !== 3 ? '❤️' : '❤️\u00A0Trust'}`}
|
||||||
checked={activeTab === 2 && true}
|
checked={activeTab === 3 && true}
|
||||||
onChange={() => updateActiveTab(2)}
|
onChange={() => updateActiveTab(3)}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
role='tabpanel'
|
role='tabpanel'
|
||||||
@ -197,10 +197,10 @@ export const TabsView = ({
|
|||||||
type='radio'
|
type='radio'
|
||||||
name='my_tabs_2'
|
name='my_tabs_2'
|
||||||
role='tab'
|
role='tab'
|
||||||
className={`tw:tab tw:font-bold tw:ps-2! tw:pe-2! ${!(item.layer.itemType.icon_as_labels && activeTab !== 3) && 'tw:min-w-[10.4em]'} `}
|
className={`tw:tab tw:font-bold tw:ps-2! tw:pe-2! ${!(item.layer.itemType.icon_as_labels && activeTab !== 1) && 'tw:min-w-[10.4em]'} `}
|
||||||
aria-label={`${item.layer.itemType.icon_as_labels && activeTab !== 3 ? '♻️' : '♻️\u00A0Offers & Needs'}`}
|
aria-label={`${item.layer.itemType.icon_as_labels && activeTab !== 1 ? '♻️' : '♻️\u00A0Offers & Needs'}`}
|
||||||
checked={activeTab === 3 && true}
|
checked={activeTab === 1 && true}
|
||||||
onChange={() => updateActiveTab(3)}
|
onChange={() => updateActiveTab(1)}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
role='tabpanel'
|
role='tabpanel'
|
||||||
@ -251,9 +251,9 @@ export const TabsView = ({
|
|||||||
name='my_tabs_2'
|
name='my_tabs_2'
|
||||||
role='tab'
|
role='tab'
|
||||||
className='tw:tab tw:font-bold tw:ps-2! tw:pe-2! '
|
className='tw:tab tw:font-bold tw:ps-2! tw:pe-2! '
|
||||||
aria-label={`${item.layer.itemType.icon_as_labels && activeTab !== 7 ? '🔗' : '🔗\u00A0Links'}`}
|
aria-label={`${item.layer.itemType.icon_as_labels && activeTab !== 2 ? '🔗' : '🔗\u00A0Links'}`}
|
||||||
checked={activeTab === 7 && true}
|
checked={activeTab === 2 && true}
|
||||||
onChange={() => updateActiveTab(7)}
|
onChange={() => updateActiveTab(2)}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
role='tabpanel'
|
role='tabpanel'
|
||||||
|
|||||||
65
src/Components/Templates/Tabs.tsx
Normal file
65
src/Components/Templates/Tabs.tsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/* eslint-disable security/detect-object-injection */
|
||||||
|
import { useState, useEffect, useCallback } from 'react'
|
||||||
|
import { useLocation, useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
|
interface TabItem {
|
||||||
|
title: string
|
||||||
|
component: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TabsProps {
|
||||||
|
items: TabItem[]
|
||||||
|
setUrlParams: (params: URLSearchParams) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Tabs: React.FC<TabsProps> = ({ items, setUrlParams }: TabsProps) => {
|
||||||
|
const location = useLocation()
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
const [activeIndex, setActiveIndex] = useState<number>(0)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const params = new URLSearchParams(location.search)
|
||||||
|
const urlTab = params.get('tab')
|
||||||
|
if (urlTab !== null && !isNaN(Number(urlTab))) {
|
||||||
|
const index = Number(urlTab)
|
||||||
|
if (index >= 0 && index < items.length) {
|
||||||
|
setActiveIndex(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [items.length, location.search])
|
||||||
|
|
||||||
|
const updateActiveTab = useCallback(
|
||||||
|
(index: number) => {
|
||||||
|
setActiveIndex(index)
|
||||||
|
|
||||||
|
const params = new URLSearchParams(location.search)
|
||||||
|
params.set('tab', `${index}`)
|
||||||
|
setUrlParams(params)
|
||||||
|
const newUrl = location.pathname + '?' + params.toString()
|
||||||
|
|
||||||
|
navigate(newUrl, { replace: false })
|
||||||
|
},
|
||||||
|
[location, navigate],
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='tw:flex tw:flex-col tw:flex-1 tw:min-h-0'>
|
||||||
|
<div role='tablist' className='tw:tabs tw:tabs-lift tw:flex-none'>
|
||||||
|
{items.map((item, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
role='tab'
|
||||||
|
className={`tw:tab ${index === activeIndex ? 'tw:tab-active' : ''}`}
|
||||||
|
onClick={() => updateActiveTab(index)}
|
||||||
|
>
|
||||||
|
{item.title}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className='tw:flex-1 tw:flex tw:flex-col tw:min-h-0'>
|
||||||
|
{items[activeIndex]?.component}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user