mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50:36 +03:00
Optimize frontend for small screens
This commit is contained in:
parent
46d8d93c4b
commit
5a196b8fa1
|
@ -442,6 +442,7 @@ class RSForm:
|
|||
if transfer_term:
|
||||
substitution.term_raw = original.term_raw
|
||||
substitution.term_forms = original.term_forms
|
||||
substitution.term_resolved = original.term_resolved
|
||||
substitution.save()
|
||||
original.delete()
|
||||
self.on_term_change([substitution.alias])
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.svg" />
|
||||
|
|
|
@ -11,7 +11,7 @@ function ApplicationLayout() {
|
|||
const { viewportHeight, mainHeight, showScroll } = useConceptTheme();
|
||||
return (
|
||||
<NavigationState>
|
||||
<div className='min-w-[30rem] clr-app antialiased'>
|
||||
<div className='min-w-[20rem] clr-app antialiased'>
|
||||
<ConceptToaster
|
||||
className='mt-[4rem] text-sm' // prettier: split lines
|
||||
autoClose={3000}
|
||||
|
|
|
@ -15,8 +15,8 @@ function Footer() {
|
|||
tabIndex={-1}
|
||||
className={clsx(
|
||||
'z-navigation',
|
||||
'px-4 py-2 flex flex-col items-center gap-1',
|
||||
'text-sm select-none whitespace-nowrap'
|
||||
'sm:px-4 sm:py-2 flex flex-col items-center gap-1',
|
||||
'text-xs sm:text-sm select-none whitespace-nowrap'
|
||||
)}
|
||||
>
|
||||
<div className='flex gap-3'>
|
||||
|
|
|
@ -1,12 +1,19 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { useConceptTheme } from '@/context/ThemeContext';
|
||||
import useWindowSize from '@/hooks/useWindowSize';
|
||||
|
||||
function Logo() {
|
||||
const { darkMode } = useConceptTheme();
|
||||
const size = useWindowSize();
|
||||
|
||||
return (
|
||||
<img
|
||||
alt='Логотип КонцептПортал'
|
||||
className='max-h-[1.6rem] min-w-[11.5rem]'
|
||||
src={!darkMode ? '/logo_full.svg' : '/logo_full_dark.svg'}
|
||||
className={clsx('max-h-[1.6rem] min-w-fit', 'text-start', {
|
||||
'min-w-[11.5rem]': size.isSmall
|
||||
})}
|
||||
src={size.isSmall ? '/logo_sign.svg' : !darkMode ? '/logo_full.svg' : '/logo_full_dark.svg'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ function NavigationButton({ icon, title, onClick, text }: NavigationButtonProps)
|
|||
)}
|
||||
>
|
||||
{icon ? <span>{icon}</span> : null}
|
||||
{text ? <span>{text}</span> : null}
|
||||
{text ? <span className='hidden sm:inline'>{text}</span> : null}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,13 +2,16 @@
|
|||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { SMALL_SCREEN_WIDTH } from '@/utils/constants';
|
||||
|
||||
function useWindowSize() {
|
||||
const isClient = typeof window === 'object';
|
||||
|
||||
function getSize() {
|
||||
return {
|
||||
width: isClient ? window.innerWidth : undefined,
|
||||
height: isClient ? window.innerHeight : undefined
|
||||
height: isClient ? window.innerHeight : undefined,
|
||||
isSmall: isClient && window.innerWidth < SMALL_SCREEN_WIDTH
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -47,9 +47,10 @@ function SearchPanel({ total, filtered, query, setQuery, strategy, setFilter }:
|
|||
<div
|
||||
className={clsx(
|
||||
'sticky top-0', // prettier: split lines
|
||||
'w-full max-h-[2.3rem]',
|
||||
'pr-40 flex',
|
||||
'w-full max-h-[2.2rem]',
|
||||
'sm:pr-40 flex',
|
||||
'border-b',
|
||||
'text-sm',
|
||||
'clr-input'
|
||||
)}
|
||||
>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useMemo } from 'react';
|
||||
import { useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import DataTable, { createColumnHelper } from '@/components/DataTable';
|
||||
import DataTable, { createColumnHelper, VisibilityState } from '@/components/DataTable';
|
||||
import HelpButton from '@/components/Help/HelpButton';
|
||||
import FlexColumn from '@/components/ui/FlexColumn';
|
||||
import TextURL from '@/components/ui/TextURL';
|
||||
|
@ -12,6 +12,7 @@ import { useAuth } from '@/context/AuthContext';
|
|||
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||
import { useUsers } from '@/context/UsersContext';
|
||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
||||
import useWindowSize from '@/hooks/useWindowSize';
|
||||
import { ILibraryItem } from '@/models/library';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
|
||||
|
@ -33,6 +34,16 @@ function ViewLibrary({ items, resetQuery: cleanQuery }: ViewLibraryProps) {
|
|||
|
||||
const handleOpenItem = (item: ILibraryItem) => router.push(`/rsforms/${item.id}`);
|
||||
|
||||
const windowSize = useWindowSize();
|
||||
|
||||
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});
|
||||
|
||||
useLayoutEffect(() => {
|
||||
setColumnVisibility({
|
||||
owner: !windowSize.isSmall
|
||||
});
|
||||
}, [windowSize]);
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
columnHelper.display({
|
||||
|
@ -46,43 +57,53 @@ function ViewLibrary({ items, resetQuery: cleanQuery }: ViewLibraryProps) {
|
|||
columnHelper.accessor('alias', {
|
||||
id: 'alias',
|
||||
header: 'Шифр',
|
||||
size: 200,
|
||||
minSize: 200,
|
||||
maxSize: 200,
|
||||
size: 150,
|
||||
minSize: 80,
|
||||
maxSize: 150,
|
||||
enableSorting: true,
|
||||
sortingFn: 'text'
|
||||
}),
|
||||
columnHelper.accessor('title', {
|
||||
id: 'title',
|
||||
header: 'Название',
|
||||
size: 2000,
|
||||
minSize: 400,
|
||||
maxSize: 2000,
|
||||
size: 1200,
|
||||
minSize: 200,
|
||||
maxSize: 1200,
|
||||
enableSorting: true,
|
||||
sortingFn: 'text'
|
||||
}),
|
||||
columnHelper.accessor(item => item.owner ?? 0, {
|
||||
id: 'owner',
|
||||
header: 'Владелец',
|
||||
size: 600,
|
||||
minSize: 200,
|
||||
maxSize: 600,
|
||||
size: 400,
|
||||
minSize: 100,
|
||||
maxSize: 400,
|
||||
cell: props => getUserLabel(props.cell.getValue()),
|
||||
enableSorting: true,
|
||||
sortingFn: 'text'
|
||||
}),
|
||||
columnHelper.accessor('time_update', {
|
||||
id: 'time_update',
|
||||
header: 'Обновлена',
|
||||
header: windowSize.isSmall ? 'Дата' : 'Обновлена',
|
||||
cell: props => (
|
||||
<div className='text-sm whitespace-nowrap'>{new Date(props.cell.getValue()).toLocaleString(intl.locale)}</div>
|
||||
<div className='whitespace-nowrap'>
|
||||
{new Date(props.cell.getValue()).toLocaleString(intl.locale, {
|
||||
year: '2-digit',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
...(!windowSize.isSmall && {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})
|
||||
})}
|
||||
</div>
|
||||
),
|
||||
enableSorting: true,
|
||||
sortingFn: 'datetime',
|
||||
sortDescFirst: true
|
||||
})
|
||||
],
|
||||
[intl, getUserLabel, user]
|
||||
[intl, getUserLabel, user, windowSize]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -102,7 +123,8 @@ function ViewLibrary({ items, resetQuery: cleanQuery }: ViewLibraryProps) {
|
|||
<DataTable
|
||||
columns={columns}
|
||||
data={items}
|
||||
headPosition='2.3rem'
|
||||
headPosition='2.2rem'
|
||||
className='text-xs sm:text-sm'
|
||||
noDataComponent={
|
||||
<FlexColumn className='p-3 items-center min-h-[6rem]'>
|
||||
<p>Список схем пуст</p>
|
||||
|
@ -112,6 +134,7 @@ function ViewLibrary({ items, resetQuery: cleanQuery }: ViewLibraryProps) {
|
|||
</p>
|
||||
</FlexColumn>
|
||||
}
|
||||
columnVisibility={columnVisibility}
|
||||
onRowClicked={handleOpenItem}
|
||||
enableSorting
|
||||
initialSorting={{
|
||||
|
|
|
@ -12,7 +12,14 @@ interface TopicsListProps {
|
|||
function TopicsList({ activeTopic, onChangeTopic }: TopicsListProps) {
|
||||
return (
|
||||
<div
|
||||
className={clsx('sticky top-0 left-0', 'min-w-[13rem] self-start', 'border-x', 'clr-controls', '', 'select-none')}
|
||||
className={clsx(
|
||||
'sticky top-0 left-0',
|
||||
'self-start',
|
||||
'border-x',
|
||||
'clr-controls',
|
||||
'text-xs sm:text-sm',
|
||||
'select-none'
|
||||
)}
|
||||
>
|
||||
{Object.values(HelpTopic).map((topic, index) => (
|
||||
<div
|
||||
|
|
|
@ -70,7 +70,7 @@ function EditorRSForm({
|
|||
onDestroy={onDestroy}
|
||||
onToggleSubscribe={onToggleSubscribe}
|
||||
/>
|
||||
<div tabIndex={-1} className='flex' onKeyDown={handleInput}>
|
||||
<div tabIndex={-1} className='flex flex-col sm:flex-row w-fit' onKeyDown={handleInput}>
|
||||
<FlexColumn className='px-4 pb-2'>
|
||||
<FormRSForm
|
||||
disabled={!isMutable}
|
||||
|
|
|
@ -31,6 +31,7 @@ html {
|
|||
}
|
||||
|
||||
:root {
|
||||
font-size: 16px;
|
||||
font-family: var(--font-main);
|
||||
|
||||
color: var(--cl-fg-100);
|
||||
|
@ -44,6 +45,12 @@ html {
|
|||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 640px) {
|
||||
:root {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
:focus {
|
||||
outline-width: 2px;
|
||||
outline-style: solid;
|
||||
|
|
|
@ -14,6 +14,11 @@ export const buildConstants = {
|
|||
*/
|
||||
export const TIMEOUT_UI_REFRESH = 100;
|
||||
|
||||
/**
|
||||
* Threshold for small screen size optimizations.
|
||||
*/
|
||||
export const SMALL_SCREEN_WIDTH = 640; // == tailwind:xs
|
||||
|
||||
/**
|
||||
* Timeout [in ms] for graph refresh.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue
Block a user