Minor UI improvements and CSS refactoring

This commit is contained in:
IRBorisov 2024-01-06 03:15:02 +03:00
parent d64e4a9894
commit ead0418564
68 changed files with 816 additions and 653 deletions

View File

@ -108,6 +108,7 @@
"signup",
"Slng",
"SMALLPR",
"Stylesheet",
"tagset",
"tailwindcss",
"tanstack",

View File

@ -6,7 +6,7 @@ import { IoLibrary } from 'react-icons/io5';
import { EducationIcon } from '@/components/Icons';
import { useConceptNavigation } from '@/context/NavigationContext';
import { useConceptTheme } from '@/context/ThemeContext';
import { animateNavigation } from '@/utils/animations';
import { animateNavigation } from '@/styling/animations';
import Logo from './Logo';
import NavigationButton from './NavigationButton';
@ -23,7 +23,14 @@ function Navigation() {
const navigateCreateNew = () => router.push('/library/create');
return (
<nav className={clsx('z-navigation', 'sticky top-0 left-0 right-0', 'clr-app', 'select-none')}>
<nav
className={clsx(
'z-navigation', // prettier: split lines
'sticky top-0 left-0 right-0',
'clr-app',
'select-none'
)}
>
<ToggleNavigationButton />
<motion.div
className={clsx('pl-2 pr-[0.9rem] h-[3rem]', 'flex justify-between', 'shadow-border')}

View File

@ -3,7 +3,7 @@ import { motion } from 'framer-motion';
import { RiPushpinFill, RiUnpinLine } from 'react-icons/ri';
import { useConceptTheme } from '@/context/ThemeContext';
import { animateNavigationToggle } from '@/utils/animations';
import { animateNavigationToggle } from '@/styling/animations';
function ToggleNavigationButton() {
const { noNavigationAnimation, toggleNoNavigation } = useConceptTheme();

View File

@ -0,0 +1,22 @@
import { motion } from 'framer-motion';
import { animateFadeIn } from '@/styling/animations';
import { CProps } from './props';
interface AnimateFadeInProps extends CProps.AnimatedDiv {}
function AnimateFadeIn({ children, ...restProps }: AnimateFadeInProps) {
return (
<motion.div
initial={{ ...animateFadeIn.initial }}
animate={{ ...animateFadeIn.animate }}
exit={{ ...animateFadeIn.exit }}
{...restProps}
>
{children}
</motion.div>
);
}
export default AnimateFadeIn;

View File

@ -1,9 +1,9 @@
import clsx from 'clsx';
import ConstituentaTooltip from '@/components/Help/ConstituentaTooltip';
import ConstituentaTooltip from '@/components/ConstituentaTooltip';
import { IConstituenta } from '@/models/rsform';
import { isMockCst } from '@/models/rsformAPI';
import { colorFgCstStatus, IColorTheme } from '@/utils/color';
import { colorFgCstStatus, IColorTheme } from '@/styling/color';
interface ConstituentaBadgeProps {
prefixID?: string;

View File

@ -10,6 +10,7 @@ import { prefixes } from '@/utils/constants';
import { describeConstituenta } from '@/utils/labels';
import ConstituentaBadge from './ConstituentaBadge';
import FlexColumn from './ui/FlexColumn';
interface ConstituentaPickerProps {
prefixID?: string;
@ -94,10 +95,10 @@ function ConstituentaPicker({
columns={columns}
conditionalRowStyles={conditionalRowStyles}
noDataComponent={
<span className='p-2 min-h-[5rem] flex flex-col justify-center text-center'>
<FlexColumn className='p-3 items-center min-h-[6rem]'>
<p>Список конституент пуст</p>
<p>Измените параметры фильтра</p>
</span>
</FlexColumn>
}
onRowClicked={onSelectValue}
/>

View File

@ -14,7 +14,6 @@ import {
useReactTable,
type VisibilityState
} from '@tanstack/react-table';
import clsx from 'clsx';
import { useState } from 'react';
import { CProps } from '../props';
@ -122,7 +121,7 @@ function DataTable<TData extends RowData>({
const isEmpty = tableImpl.getRowModel().rows.length === 0;
return (
<div className={clsx(className)} style={style}>
<div className={className} style={style}>
<table className='w-full'>
{!noHeader ? (
<TableHeader

View File

@ -52,14 +52,14 @@ function TableBody<TData>({
style={conditionalRowStyles && getRowStyles(row)}
>
{enableRowSelection ? (
<td key={`select-${row.id}`} className='pl-3 pr-1 border-y'>
<td key={`select-${row.id}`} className='pl-3 pr-1 border-y align-middle'>
<SelectRow row={row} />
</td>
) : null}
{row.getVisibleCells().map((cell: Cell<TData, unknown>) => (
<td
key={cell.id}
className='px-2 border-y'
className='px-2 border-y align-middle'
style={{
cursor: onRowClicked || onRowDoubleClicked ? 'pointer' : 'auto',
paddingBottom: dense ? '0.25rem' : '0.5rem',

View File

@ -13,7 +13,7 @@ interface TableHeaderProps<TData> {
function TableHeader<TData>({ table, headPosition, enableRowSelection, enableSorting }: TableHeaderProps<TData>) {
return (
<thead
className={`clr-app shadow-border`}
className='clr-app shadow-border'
style={{
top: headPosition,
position: 'sticky'
@ -22,7 +22,7 @@ function TableHeader<TData>({ table, headPosition, enableRowSelection, enableSor
{table.getHeaderGroups().map((headerGroup: HeaderGroup<TData>) => (
<tr key={headerGroup.id}>
{enableRowSelection ? (
<th className='pl-3 pr-1'>
<th className='pl-3 pr-1 align-middle'>
<SelectAll table={table} />
</th>
) : null}

View File

@ -2,7 +2,7 @@ import clsx from 'clsx';
import { useConceptTheme } from '@/context/ThemeContext';
import { GramData } from '@/models/language';
import { colorFgGrammeme } from '@/utils/color';
import { colorFgGrammeme } from '@/styling/color';
import { labelGrammeme } from '@/utils/labels';
interface GrammemeBadgeProps {

View File

@ -1,15 +1,16 @@
import { BiInfoCircle } from 'react-icons/bi';
import TextURL from '@/components/ui/TextURL';
import Tooltip from '@/components/ui/Tooltip';
import Tooltip, { PlacesType } from '@/components/ui/Tooltip';
import { HelpTopic } from '@/models/miscellaneous';
import InfoTopic from '../InfoTopic';
import { CProps } from '../props';
import InfoTopic from './InfoTopic';
interface HelpButtonProps extends CProps.Styling {
topic: HelpTopic;
offset?: number;
place?: PlacesType;
}
function HelpButton({ topic, ...restProps }: HelpButtonProps) {

View File

@ -4,7 +4,7 @@ import { urls } from '@/utils/constants';
function HelpExteor() {
// prettier-ignore
return (
<div>
<div className='flex flex-col gap-2'>
<h1>Экстеор</h1>
<p>Экстеор 4.9 редактор текстов систем понятий эксплицированных в родах структур, используемых для создания проектов систем организационного управления. Экстеор является идейным предком Портала.</p>
<p>Портал превосходит Экстеор в части редактирования экспликаций, но функции синтеза и вычисления интерпретации пока доступны только в Экстеоре. Также следует использовать Экстеор для выгрузки экспликаций в Word для последующей печати.</p>

View File

@ -4,7 +4,7 @@ import { FiBell } from 'react-icons/fi';
function HelpLibrary() {
// prettier-ignore
return (
<div className='max-w-[80rem]'>
<div>
<h1>Библиотека концептуальных схем</h1>
<p>В библиотеки собраны различные концептуальные схемы.</p>
<p>Группировка и классификации схем на данный момент не проводится.</p>

View File

@ -4,7 +4,7 @@ import { urls } from '@/utils/constants';
function HelpMain() {
// prettier-ignore
return (
<div className='max-w-[60rem]'>
<div>
<h1>Портал</h1>
<p className=''>Портал позволяет анализировать предметные области, формально записывать системы определений (концептуальные схемы) и синтезировать их с помощью математического аппарата родов структур.</p>
<p className='mt-4 mb-1 text-center'><b>Основные разделы</b></p>

View File

@ -1,7 +1,7 @@
function HelpRSTemplates() {
// prettier-ignore
return (
<div className='flex flex-col gap-2 pb-2 max-w-[80rem]'>
<div className='flex flex-col gap-2 pb-2'>
<h1>Банк выражений</h1>
<p>Портал предоставляет быстрый доступ к часто используемым выражениям с помощью функции создания конституенты из шаблона.</p>
<p>Источником шаблонов является <b>Банк выражений</b>, содержащий параметризованные понятия и утверждения, сгруппированные по разделам.</p>

View File

@ -1,7 +1,7 @@
function HelpTerminologyControl() {
// prettier-ignore
return (
<div className='flex flex-col gap-1 max-w-[80rem]'>
<div className='flex flex-col gap-2'>
<h1>Терминологизация</h1>
<p>Портал позволяет контролировать употребление терминов, привязанных к сущностям в концептуальных схемах.</p>
<p>Для этого используется механизм текстовых отсылок: <i>использование термина</i> и <i>связывание слов.</i></p>

View File

@ -1,7 +1,9 @@
import { IConstituenta } from '@/models/rsform';
import { labelCstTypification } from '@/utils/labels';
interface InfoConstituentaProps extends React.HTMLAttributes<HTMLDivElement> {
import { CProps } from './props';
interface InfoConstituentaProps extends CProps.Div {
data: IConstituenta;
}

View File

@ -2,7 +2,7 @@ import clsx from 'clsx';
import { useConceptTheme } from '@/context/ThemeContext';
import { CstClass } from '@/models/rsform';
import { colorBgCstClass } from '@/utils/color';
import { colorBgCstClass } from '@/styling/color';
import { prefixes } from '@/utils/constants';
import { describeCstClass, labelCstClass } from '@/utils/labels';

View File

@ -2,7 +2,7 @@ import clsx from 'clsx';
import { useConceptTheme } from '@/context/ThemeContext';
import { ExpressionStatus } from '@/models/rsform';
import { colorBgCstStatus } from '@/utils/color';
import { colorBgCstStatus } from '@/styling/color';
import { prefixes } from '@/utils/constants';
import { describeExpressionStatus, labelExpressionStatus } from '@/utils/labels';

View File

@ -1,17 +1,17 @@
import { HelpTopic } from '@/models/miscellaneous';
import HelpAPI from './HelpAPI';
import HelpConstituenta from './HelpConstituenta';
import HelpExteor from './HelpExteor';
import HelpLibrary from './HelpLibrary';
import HelpMain from './HelpMain';
import HelpPrivacy from './HelpPrivacy';
import HelpRSFormItems from './HelpRSFormItems';
import HelpRSFormMeta from './HelpRSFormMeta';
import HelpRSLang from './HelpRSLang';
import HelpRSTemplates from './HelpRSTemplates';
import HelpTermGraph from './HelpTermGraph';
import HelpTerminologyControl from './HelpTerminologyControl';
import HelpAPI from './Help/HelpAPI';
import HelpConstituenta from './Help/HelpConstituenta';
import HelpExteor from './Help/HelpExteor';
import HelpLibrary from './Help/HelpLibrary';
import HelpMain from './Help/HelpMain';
import HelpPrivacy from './Help/HelpPrivacy';
import HelpRSFormItems from './Help/HelpRSFormItems';
import HelpRSFormMeta from './Help/HelpRSFormMeta';
import HelpRSLang from './Help/HelpRSLang';
import HelpRSTemplates from './Help/HelpRSTemplates';
import HelpTermGraph from './Help/HelpTermGraph';
import HelpTerminologyControl from './Help/HelpTerminologyControl';
interface InfoTopicProps {
topic: HelpTopic;

View File

@ -5,7 +5,7 @@ import { useMemo, useState } from 'react';
import { Document, Page } from 'react-pdf';
import useWindowSize from '@/hooks/useWindowSize';
import { graphLightT } from '@/utils/color';
import { graphLightT } from '@/styling/color';
import Overlay from '../ui/Overlay';
import PageControls from './PageControls';

View File

@ -1,7 +1,7 @@
import { bracketMatching, MatchResult } from '@codemirror/language';
import { Decoration, EditorView } from '@codemirror/view';
import { bracketsDarkT, bracketsLightT } from '@/utils/color';
import { bracketsDarkT, bracketsLightT } from '@/styling/color';
const matchingMark = Decoration.mark({ class: 'cc-matchingBracket' });
const nonMatchingMark = Decoration.mark({ class: 'cc-nonmatchingBracket' });

View File

@ -10,7 +10,7 @@ import {
findContainedNodes,
findEnvelopingNodes
} from '@/utils/codemirror';
import { IColorTheme } from '@/utils/color';
import { IColorTheme } from '@/styling/color';
import { ReferenceTokens } from './parse';
import { RefEntity, RefSyntactic } from './parse/parser.terms';

View File

@ -38,4 +38,5 @@ export namespace CProps {
export type Input = React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>;
export type AnimatedButton = Omit<HTMLMotionProps<'button'>, 'type'>;
export type AnimatedDiv = HTMLMotionProps<'div'>;
}

View File

@ -37,7 +37,13 @@ function Checkbox({ id, disabled, label, title, className, value, setValue, ...r
<button
type='button'
id={id}
className={clsx('flex items-center gap-2', 'outline-none', 'text-start', cursor, className)}
className={clsx(
'flex items-center gap-2', // prettier: split lines
'outline-none',
'text-start',
cursor,
className
)}
disabled={disabled}
onClick={handleClick}
data-tooltip-id={title ? globalIDs.tooltip : undefined}
@ -45,10 +51,14 @@ function Checkbox({ id, disabled, label, title, className, value, setValue, ...r
{...restProps}
>
<div
className={clsx('max-w-[1rem] min-w-[1rem] h-4', 'border rounded-sm', {
className={clsx(
'max-w-[1rem] min-w-[1rem] h-4', // prettier: split lines
'border rounded-sm',
{
'clr-primary': value !== false,
'clr-app': value === false
})}
}
)}
>
{value ? (
<div className='mt-[1px] ml-[1px]'>

View File

@ -49,7 +49,12 @@ function CheckboxTristate({
<button
type='button'
id={id}
className={clsx('flex items-center gap-2 text-start', 'outline-none', cursor, className)}
className={clsx(
'flex items-center gap-2 text-start', // prettier: split lines
'outline-none',
cursor,
className
)}
disabled={disabled}
onClick={handleClick}
data-tooltip-id={title ? globalIDs.tooltip : undefined}
@ -57,10 +62,14 @@ function CheckboxTristate({
{...restProps}
>
<div
className={clsx('w-4 h-4', 'border rounded-sm', {
className={clsx(
'w-4 h-4', // prettier: split lines
'border rounded-sm',
{
'clr-primary': value !== false,
'clr-app': value === false
})}
}
)}
>
{value ? (
<div className='mt-[1px] ml-[1px]'>

View File

@ -1,7 +1,7 @@
import clsx from 'clsx';
import { motion } from 'framer-motion';
import { animateDropdown } from '@/utils/animations';
import { animateDropdown } from '@/styling/animations';
import { CProps } from '../props';

View File

@ -1,7 +1,7 @@
import clsx from 'clsx';
import { motion } from 'framer-motion';
import { animateDropdownItem } from '@/utils/animations';
import { animateDropdownItem } from '@/styling/animations';
import { globalIDs } from '@/utils/constants';
import { CProps } from '../props';

View File

@ -1,7 +1,7 @@
import clsx from 'clsx';
import { motion } from 'framer-motion';
import { animateDropdownItem } from '@/utils/animations';
import { animateDropdownItem } from '@/styling/animations';
import Checkbox from './Checkbox';

View File

@ -6,7 +6,7 @@ import { useRef } from 'react';
import { BiX } from 'react-icons/bi';
import useEscapeKey from '@/hooks/useEscapeKey';
import { animateModal } from '@/utils/animations';
import { animateModal } from '@/styling/animations';
import { CProps } from '../props';
import Button from './Button';

View File

@ -4,7 +4,7 @@ import { useMemo } from 'react';
import Select, { GroupBase, Props, StylesConfig } from 'react-select';
import { useConceptTheme } from '@/context/ThemeContext';
import { selectDarkT, selectLightT } from '@/utils/color';
import { selectDarkT, selectLightT } from '@/styling/color';
export interface SelectMultiProps<Option, Group extends GroupBase<Option> = GroupBase<Option>>
extends Omit<Props<Option, true, Group>, 'theme' | 'menuPortalTarget'> {

View File

@ -4,7 +4,7 @@ import { useMemo } from 'react';
import Select, { GroupBase, Props, StylesConfig } from 'react-select';
import { useConceptTheme } from '@/context/ThemeContext';
import { selectDarkT, selectLightT } from '@/utils/color';
import { selectDarkT, selectLightT } from '@/styling/color';
interface SelectSingleProps<Option, Group extends GroupBase<Option> = GroupBase<Option>>
extends Omit<Props<Option, false, Group>, 'theme' | 'menuPortalTarget'> {

View File

@ -7,6 +7,8 @@ import { ITooltip, Tooltip as TooltipImpl } from 'react-tooltip';
import { useConceptTheme } from '@/context/ThemeContext';
export type { PlacesType } from 'react-tooltip';
interface TooltipProps extends Omit<ITooltip, 'variant'> {
layer?: string;
text?: string;

View File

@ -5,8 +5,8 @@ import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useSt
import Tooltip from '@/components/ui/Tooltip';
import useLocalStorage from '@/hooks/useLocalStorage';
import { animationDuration } from '@/utils/animations';
import { darkT, IColorTheme, lightT } from '@/utils/color';
import { animationDuration } from '@/styling/animations';
import { darkT, IColorTheme, lightT } from '@/styling/color';
import { globalIDs } from '@/utils/constants';
interface IThemeContext {

View File

@ -6,8 +6,8 @@ import GraphUI, { GraphEdge, GraphNode } from '@/components/GraphUI';
import Modal, { ModalProps } from '@/components/ui/Modal';
import { useConceptTheme } from '@/context/ThemeContext';
import { SyntaxTree } from '@/models/rslang';
import { graphDarkT, graphLightT } from '@/utils/color';
import { colorBgSyntaxTree } from '@/utils/color';
import { graphDarkT, graphLightT } from '@/styling/color';
import { colorBgSyntaxTree } from '@/styling/color';
import { resources } from '@/utils/constants';
import { labelSyntaxTree } from '@/utils/labels';

View File

@ -1,361 +1,4 @@
@import 'react-toastify/dist/ReactToastify.css';
@tailwind base;
@tailwind components;
@tailwind utilities;
/* prettier-ignore */
:root {
--font-ui: 'Geologica', sans-serif;
--font-main: 'Rubik', 'Noto Sans Math', 'Noto Sans Symbols 2', 'Segoe UI Symbol', sans-serif;
--font-math: 'Noto Sans Math', 'Noto Sans Symbols 2', 'Segoe UI Symbol', sans-serif;
/* Light Theme */
--cl-bg-120: hsl(000, 000%, 100%);
--cl-bg-100: hsl(000, 000%, 098%);
--cl-bg-80: hsl(000, 000%, 094%);
--cl-bg-60: hsl(000, 000%, 091%);
--cl-bg-40: hsl(000, 000%, 080%);
--cl-fg-60: hsl(000, 000%, 065%);
--cl-fg-80: hsl(000, 000%, 047%);
--cl-fg-100: hsl(000, 000%, 000%);
--cl-prim-bg-100: hsl(220, 100%, 060%);
--cl-prim-bg-80: hsl(220, 080%, 092%);
--cl-prim-bg-60: hsl(190, 080%, 094%);
--cl-prim-fg-80: hsl(220, 100%, 050%);
--cl-prim-fg-100: hsl(000, 000%, 100%);
--cl-red-bg-100: hsl(000, 100%, 095%);
--cl-red-fg-100: hsl(000, 072%, 051%);
--cl-green-fg-100: hsl(120, 080%, 37%);
/* Dark Theme */
--cd-bg-120: hsl(000, 000%, 005%);
--cd-bg-100: hsl(000, 000%, 009%);
--cd-bg-80: hsl(000, 000%, 015%);
--cd-bg-60: hsl(000, 000%, 022%);
--cd-bg-40: hsl(000, 000%, 035%);
--cd-fg-60: hsl(000, 000%, 055%);
--cd-fg-80: hsl(000, 000%, 080%);
--cd-fg-100: hsl(000, 000%, 093%);
--cd-prim-bg-100: hsl(267, 050%, 050%);
--cd-prim-bg-80: hsl(267, 050%, 032%);
--cd-prim-bg-60: hsl(269, 030%, 028%);
--cd-prim-fg-80: hsl(267, 070%, 070%);
--cd-prim-fg-100: hsl(000, 000%, 100%);
--cd-red-bg-100: hsl(000, 100%, 015%);
--cd-red-fg-100: hsl(000, 080%, 055%);
--cd-green-fg-100: hsl(120, 080%, 042%);
/* Import overrides */
--toastify-color-dark: var(--cd-bg-60);
}
/* Depth layers */
.z-bottom {
z-index: 0;
}
.z-pop {
z-index: 10;
}
:is(.z-sticky, .sticky) {
z-index: 20;
}
.z-tooltip {
z-index: 30;
}
.z-navigation {
z-index: 50;
}
.z-modal {
z-index: 60;
}
.z-modal-controls {
z-index: 70;
}
.z-modal-top {
z-index: 80;
}
.z-modal-tooltip {
z-index: 90;
}
.z-topmost {
z-index: 99;
}
:root {
font-family: var(--font-main);
color: var(--cl-fg-100);
border-color: var(--cl-bg-40);
background-color: var(--cl-bg-100);
&.dark {
color: var(--cd-fg-100);
border-color: var(--cd-bg-40);
background-color: var(--cd-bg-100);
}
}
:focus {
outline-width: 2px;
outline-style: solid;
outline-color: transparent;
.dark & {
outline-color: transparent;
}
}
::selection {
background: var(--cl-prim-bg-60);
.dark & {
background: var(--cd-prim-bg-60);
}
tr :hover& {
background: var(--cl-red-bg-100);
.dark & {
background: var(--cd-red-bg-100);
}
}
}
::placeholder {
color: var(--cl-fg-60);
.dark & {
color: var(--cd-fg-60);
}
}
[data-color-scheme='dark'] {
color-scheme: dark;
}
[data-color-scheme='light'] {
color-scheme: light;
}
@layer utilities {
.font-controls {
font-family: var(--font-ui);
font-weight: 600;
font-variant: small-caps;
}
.font-math {
font-family: var(--font-math);
}
}
@layer components {
h1 {
@apply text-lg font-semibold text-center;
}
h2 {
@apply font-semibold text-center;
}
b {
@apply font-semibold;
}
.border {
@apply rounded;
}
.shadow-border {
@apply shadow-sm shadow-[var(--cl-bg-40)] dark:shadow-[var(--cd-bg-40)];
}
.clr-modal-backdrop {
opacity: 0.75;
}
:is(
.clr-border,
.border,
.border-x,
.border-y,
.border-b,
.border-t,
.border-l,
.border-r,
.border-2,
.border-x-2,
.border-y-2,
.border-b-2,
.border-t-2,
.border-l-2,
.border-r-2,
.divide-x,
.divide-y,
.divide-x-2,
.divide-y-2
) {
border-color: var(--cl-bg-40);
@apply divide-inherit;
.dark & {
border-color: var(--cd-bg-40);
}
}
:is(.clr-app, .clr-footer, .clr-modal-backdrop, .clr-btn-nav, .clr-input:disabled) {
background-color: var(--cl-bg-100);
.dark & {
background-color: var(--cd-bg-100);
}
}
:is(.clr-input) {
background-color: var(--cl-bg-120);
.dark & {
background-color: var(--cd-bg-120);
}
}
:is(.clr-controls, .clr-tab, .clr-btn-default) {
background-color: var(--cl-bg-80);
.dark & {
background-color: var(--cd-bg-80);
}
}
:is(.clr-primary, .clr-btn-primary:hover, .clr-btn-primary:focus) {
@apply transition;
color: var(--cl-prim-fg-100);
background-color: var(--cl-prim-bg-100);
.dark & {
color: var(--cd-prim-fg-100);
background-color: var(--cd-prim-bg-100);
}
}
:is(.clr-selected, .clr-btn-primary) {
color: var(--cl-fg-100);
background-color: var(--cl-prim-bg-80);
.dark & {
color: var(--cd-fg-100);
background-color: var(--cd-prim-bg-80);
}
}
:is(.clr-disabled, .clr-btn-default, .clr-btn-primary):disabled {
@apply transition;
color: var(--cl-fg-80);
background-color: var(--cl-bg-60);
.dark & {
color: var(--cd-fg-80);
background-color: var(--cd-bg-60);
}
}
:is(.clr-hover, .clr-tab, .clr-btn-nav, .clr-btn-default):hover:not(:disabled) {
@apply transition;
color: var(--cl-fg-100);
background-color: var(--cl-prim-bg-60);
.dark & {
color: var(--cd-fg-100);
background-color: var(--cd-prim-bg-60);
}
}
:is(.clr-outline, .clr-btn-primary):focus {
outline-width: 2px;
outline-style: solid;
outline-color: var(--cl-prim-bg-100);
.dark & {
outline-color: var(--cd-prim-bg-100);
}
}
:is(.clr-text-primary, .clr-text-url) {
color: var(--cl-prim-fg-80);
.dark & {
color: var(--cd-prim-fg-80);
}
}
.clr-footer {
color: var(--cl-fg-60);
.dark & {
color: var(--cd-fg-60);
}
}
:is(.clr-text-controls, .clr-btn-nav, .clr-btn-clear) {
color: var(--cl-fg-80);
&:disabled {
color: var(--cl-fg-60);
}
.dark & {
color: var(--cd-fg-80);
&:disabled {
color: var(--cd-fg-60);
}
}
}
.clr-warning {
background-color: var(--cl-red-bg-100);
.dark & {
background-color: var(--cd-red-bg-100);
}
}
.clr-text-default {
color: var(--cl-fg-100);
.dark & {
color: var(--cd-fg-100);
}
}
.clr-text-warning {
color: var(--cl-red-fg-100);
.dark & {
color: var(--cd-red-fg-100);
}
}
.clr-text-success {
color: var(--cl-green-fg-100);
.dark & {
color: var(--cd-green-fg-100);
}
}
}
.cm-editor {
resize: vertical;
overflow-y: auto;
border-color: var(--cl-bg-40);
.dark & {
border-color: var(--cd-bg-40);
}
@apply border rounded px-[0.375rem] py-[0.15rem];
}
.cm-editor.cm-focused {
border-color: var(--cl-bg-40);
outline-color: var(--cl-prim-bg-100);
.dark & {
border-color: var(--cd-bg-40);
outline-color: var(--cd-prim-bg-100);
}
@apply outline-2 outline;
}
.cm-editor .cm-placeholder {
color: var(--cl-fg-60);
.dark & {
color: var(--cd-fg-60);
}
}
.rdt_TableCell {
font-size: 0.875rem;
}
@import 'styling/setup.css';
@import 'styling/styles.css';
@import 'styling/imports.css';
@import 'styling/overrides.css';

View File

@ -5,6 +5,7 @@ import { useEffect, useRef, useState } from 'react';
import { BiDownload } from 'react-icons/bi';
import { toast } from 'react-toastify';
import AnimateFadeIn from '@/components/AnimateFadeIn';
import InfoError from '@/components/InfoError';
import RequireAuth from '@/components/RequireAuth';
import Button from '@/components/ui/Button';
@ -78,6 +79,7 @@ function CreateRSFormPage() {
}
return (
<AnimateFadeIn>
<RequireAuth>
<form className={clsx('px-6 py-3', classnames.flex_col)} onSubmit={handleSubmit}>
<h1>Создание концептуальной схемы</h1>
@ -128,6 +130,7 @@ function CreateRSFormPage() {
{error ? <InfoError error={error} /> : null}
</form>
</RequireAuth>
</AnimateFadeIn>
);
}

View File

@ -20,13 +20,7 @@ function HomePage() {
}
}, [router, user]);
return (
<div className='flex flex-col items-center justify-center px-4 py-2'>
{user?.is_staff ? (
<p>Лендинг находится в разработке. Данная страница видна только пользователям с правами администратора.</p>
) : null}
</div>
);
return <div />;
}
export default HomePage;

View File

@ -2,6 +2,7 @@
import { useCallback, useLayoutEffect, useState } from 'react';
import AnimateFadeIn from '@/components/AnimateFadeIn';
import InfoError from '@/components/InfoError';
import { Loader } from '@/components/ui/Loader';
import { useAuth } from '@/context/AuthContext';
@ -68,7 +69,7 @@ function LibraryPage() {
{library.loading ? <Loader /> : null}
{library.error ? <InfoError error={library.error} /> : null}
{!library.loading && library.items ? (
<>
<AnimateFadeIn>
<SearchPanel
query={query}
setQuery={setQuery}
@ -78,7 +79,7 @@ function LibraryPage() {
setFilter={setFilter}
/>
<ViewLibrary resetQuery={resetQuery} items={items} />
</>
</AnimateFadeIn>
) : null}
</>
);

View File

@ -6,6 +6,7 @@ import { useIntl } from 'react-intl';
import DataTable, { createColumnHelper } from '@/components/DataTable';
import HelpButton from '@/components/Help/HelpButton';
import FlexColumn from '@/components/ui/FlexColumn';
import TextURL from '@/components/ui/TextURL';
import { useAuth } from '@/context/AuthContext';
import { useConceptNavigation } from '@/context/NavigationContext';
@ -95,7 +96,7 @@ function ViewLibrary({ items, resetQuery: cleanQuery }: ViewLibraryProps) {
'flex gap-1'
)}
>
<HelpButton topic={HelpTopic.LIBRARY} className='max-w-[35rem]' offset={0} />
<HelpButton topic={HelpTopic.LIBRARY} className='max-w-[35rem]' offset={5} place='right-start' />
</div>
</div>
<DataTable
@ -103,13 +104,13 @@ function ViewLibrary({ items, resetQuery: cleanQuery }: ViewLibraryProps) {
data={items}
headPosition='2.3rem'
noDataComponent={
<div className='p-3 text-center min-h-[6rem]'>
<FlexColumn className='p-3 items-center min-h-[6rem]'>
<p>Список схем пуст</p>
<p className='flex justify-center gap-6 mt-3'>
<p className='flex gap-6'>
<TextURL text='Создать схему' href='/library/create' />
<TextURL text='Очистить фильтр' onClick={cleanQuery} />
</p>
</div>
</FlexColumn>
}
onRowClicked={handleOpenItem}
enableSorting

View File

@ -4,6 +4,7 @@ import axios from 'axios';
import clsx from 'clsx';
import { useEffect, useState } from 'react';
import AnimateFadeIn from '@/components/AnimateFadeIn';
import ExpectedAnonymous from '@/components/ExpectedAnonymous';
import InfoError, { ErrorData } from '@/components/InfoError';
import SubmitButton from '@/components/ui/SubmitButton';
@ -62,6 +63,7 @@ function LoginPage() {
return <ExpectedAnonymous />;
}
return (
<AnimateFadeIn>
<form className={clsx('w-[24rem]', 'pt-12 pb-6 px-6', classnames.flex_col)} onSubmit={handleSubmit}>
<img alt='Концепт Портал' src={resources.logo} className='max-h-[2.5rem] min-w-[2.5rem] mb-3' />
<TextInput
@ -95,6 +97,7 @@ function LoginPage() {
</div>
{error ? <ProcessError error={error} /> : null}
</form>
</AnimateFadeIn>
);
}

View File

@ -14,7 +14,6 @@ function TopicsList({ activeTopic, onChangeTopic }: TopicsListProps) {
<div
className={clsx('sticky top-0 left-0', 'min-w-[13rem] self-start', 'border-x', 'clr-controls', '', 'select-none')}
>
<h1 className='my-1'>Справка</h1>
{Object.values(HelpTopic).map((topic, index) => (
<div
key={`${prefixes.topic_list}${index}`}

View File

@ -1,4 +1,5 @@
import InfoTopic from '@/components/Help/InfoTopic';
import AnimateFadeIn from '@/components/AnimateFadeIn';
import InfoTopic from '@/components/InfoTopic';
import { HelpTopic } from '@/models/miscellaneous';
interface ViewTopicProps {
@ -7,9 +8,9 @@ interface ViewTopicProps {
function ViewTopic({ topic }: ViewTopicProps) {
return (
<div className='px-2 py-2 mx-auto'>
<AnimateFadeIn key={topic} className='px-2 py-2 mx-auto'>
<InfoTopic topic={topic} />
</div>
</AnimateFadeIn>
);
}

View File

@ -5,7 +5,7 @@ import { motion } from 'framer-motion';
import { IExpressionParse, IRSErrorDescription } from '@/models/rslang';
import { getRSErrorPrefix } from '@/models/rslangAPI';
import { animateParseResults } from '@/utils/animations';
import { animateParseResults } from '@/styling/animations';
import { describeRSError } from '@/utils/labels';
interface ParsingResultProps {

View File

@ -1,7 +1,7 @@
import { motion } from 'framer-motion';
import { TokenID } from '@/models/rslang';
import { animateRSControl } from '@/utils/animations';
import { animateRSControl } from '@/styling/animations';
import { prefixes } from '@/utils/constants';
import RSLocalButton from './RSLocalButton';

View File

@ -9,7 +9,7 @@ import { ExpressionStatus } from '@/models/rsform';
import { type IConstituenta } from '@/models/rsform';
import { inferStatus } from '@/models/rsformAPI';
import { IExpressionParse, ParsingStatus } from '@/models/rslang';
import { colorBgCstStatus } from '@/utils/color';
import { colorBgCstStatus } from '@/styling/color';
import { globalIDs } from '@/utils/constants';
import { labelExpressionStatus } from '@/utils/labels';

View File

@ -195,6 +195,12 @@ function EditorRSList({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Edito
return (
<div tabIndex={-1} className='outline-none' onKeyDown={handleTableKey}>
<SelectedCounter
total={schema?.stats?.count_all ?? 0}
selected={selected.length}
position='top-[0.3rem] left-2'
/>
<RSListToolbar
selectedCount={selected.length}
isMutable={isMutable}
@ -204,7 +210,6 @@ function EditorRSList({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Edito
onCreate={handleCreateCst}
onDelete={handleDelete}
/>
<SelectedCounter total={schema?.stats?.count_all ?? 0} selected={selected.length} position='left-1 top-2' />
<div className='pt-[2.3rem] border-b' />

View File

@ -5,6 +5,7 @@ import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
import ConstituentaBadge from '@/components/ConstituentaBadge';
import DataTable, { createColumnHelper, RowSelectionState, VisibilityState } from '@/components/DataTable';
import FlexColumn from '@/components/ui/FlexColumn';
import { useConceptTheme } from '@/context/ThemeContext';
import useWindowSize from '@/hooks/useWindowSize';
import { IConstituenta } from '@/models/rsform';
@ -135,12 +136,12 @@ function RSTable({ items, selected, setSelected, onEdit, onCreateNew }: RSTableP
rowSelection={selected}
onRowSelectionChange={setSelected}
noDataComponent={
<span className='flex flex-col justify-center p-2 text-center'>
<FlexColumn className='p-3 items-center'>
<p>Список пуст</p>
<p className='cursor-pointer clr-text-primary hover:underline' onClick={() => onCreateNew()}>
Создать новую конституенту
</p>
</span>
</FlexColumn>
}
/>
);

View File

@ -14,7 +14,7 @@ import DlgGraphParams from '@/dialogs/DlgGraphParams';
import useLocalStorage from '@/hooks/useLocalStorage';
import { GraphColoringScheme, GraphFilterParams } from '@/models/miscellaneous';
import { CstType, ICstCreateData } from '@/models/rsform';
import { colorBgGraphNode } from '@/utils/color';
import { colorBgGraphNode } from '@/styling/color';
import { classnames, TIMEOUT_GRAPH_REFRESH } from '@/utils/constants';
import GraphSidebar from './GraphSidebar';

View File

@ -4,7 +4,7 @@ import { useCallback, useLayoutEffect, useMemo, useRef } from 'react';
import GraphUI, { GraphCanvasRef, GraphEdge, GraphNode, LayoutTypes, Sphere, useSelection } from '@/components/GraphUI';
import { useConceptTheme } from '@/context/ThemeContext';
import { graphDarkT, graphLightT } from '@/utils/color';
import { graphDarkT, graphLightT } from '@/styling/color';
import { resources } from '@/utils/constants';
interface TermGraphProps {

View File

@ -2,11 +2,11 @@
import { useCallback, useMemo } from 'react';
import ConstituentaTooltip from '@/components/Help/ConstituentaTooltip';
import ConstituentaTooltip from '@/components/ConstituentaTooltip';
import { useConceptTheme } from '@/context/ThemeContext';
import { GraphColoringScheme } from '@/models/miscellaneous';
import { IRSForm } from '@/models/rsform';
import { colorBgGraphNode } from '@/utils/color';
import { colorBgGraphNode } from '@/styling/color';
import { prefixes } from '@/utils/constants';
interface ViewHiddenProps {

View File

@ -8,6 +8,7 @@ import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
import { TabList, TabPanel, Tabs } from 'react-tabs';
import { toast } from 'react-toastify';
import AnimateFadeIn from '@/components/AnimateFadeIn';
import InfoError, { ErrorData } from '@/components/InfoError';
import { Loader } from '@/components/ui/Loader';
import TabLabel from '@/components/ui/TabLabel';
@ -434,6 +435,7 @@ function RSTabs() {
<TabLabel label='Граф термов' />
</TabList>
<AnimateFadeIn>
<TabPanel forceRender style={{ display: activeTab === RSTabID.CARD ? '' : 'none' }}>
<EditorRSForm
isMutable={isMutable}
@ -479,6 +481,7 @@ function RSTabs() {
onDeleteCst={promptDeleteCst}
/>
</TabPanel>
</AnimateFadeIn>
</Tabs>
) : null}
</>

View File

@ -125,7 +125,7 @@ function ConstituentsTable({ items, activeID, onOpenEdit, maxHeight, denseThresh
columnVisibility={columnVisibility}
onColumnVisibilityChange={setColumnVisibility}
noDataComponent={
<div className={clsx('min-h-[5rem]', 'p-2', 'text-center', 'select-none')}>
<div className={clsx('min-h-[5rem]', 'p-3', 'text-center', 'select-none')}>
<p>Список конституент пуст</p>
<p>Измените параметры фильтра</p>
</div>

View File

@ -5,7 +5,7 @@ import { useMemo, useState } from 'react';
import { useConceptTheme } from '@/context/ThemeContext';
import { IConstituenta, IRSForm } from '@/models/rsform';
import { animateSideView } from '@/utils/animations';
import { animateSideView } from '@/styling/animations';
import ConstituentsSearch from './ConstituentsSearch';
import ConstituentsTable from './ConstituentsTable';

View File

@ -5,6 +5,7 @@ import { useEffect, useState } from 'react';
import { BiInfoCircle } from 'react-icons/bi';
import { toast } from 'react-toastify';
import AnimateFadeIn from '@/components/AnimateFadeIn';
import ExpectedAnonymous from '@/components/ExpectedAnonymous';
import InfoError from '@/components/InfoError';
import Button from '@/components/ui/Button';
@ -67,6 +68,7 @@ function RegisterPage() {
return <ExpectedAnonymous />;
}
return (
<AnimateFadeIn>
<form className={clsx('px-6 py-3', classnames.flex_col)} onSubmit={handleSubmit}>
<h1>Новый пользователь</h1>
<div className='flex gap-12'>
@ -146,6 +148,7 @@ function RegisterPage() {
</div>
{error ? <InfoError error={error} /> : null}
</form>
</AnimateFadeIn>
);
}

View File

@ -1,14 +1,15 @@
import AnimateFadeIn from '@/components/AnimateFadeIn';
import TextURL from '@/components/ui/TextURL';
import { urls } from '@/utils/constants';
function RestorePasswordPage() {
return (
<div className='py-3'>
<AnimateFadeIn className='py-3'>
<p>Автоматическое восстановление пароля не доступно.</p>
<p>
Возможно восстановление пароля через обращение на <TextURL href={urls.mail_portal} text='portal@acconcept.ru' />
</p>
</div>
</AnimateFadeIn>
);
}

View File

@ -4,6 +4,7 @@ import { AnimatePresence } from 'framer-motion';
import { useMemo, useState } from 'react';
import { FiBell, FiBellOff } from 'react-icons/fi';
import AnimateFadeIn from '@/components/AnimateFadeIn';
import InfoError from '@/components/InfoError';
import { Loader } from '@/components/ui/Loader';
import MiniButton from '@/components/ui/MiniButton';
@ -32,7 +33,7 @@ function UserTabs() {
{loading ? <Loader /> : null}
{error ? <InfoError error={error} /> : null}
{user ? (
<div className='flex gap-6 py-2'>
<AnimateFadeIn className='flex gap-6 py-2'>
<div>
<Overlay position='top-0 right-0'>
<MiniButton
@ -56,7 +57,7 @@ function UserTabs() {
<AnimatePresence>
{subscriptions.length > 0 && showSubs ? <ViewSubscriptions items={subscriptions} /> : null}
</AnimatePresence>
</div>
</AnimateFadeIn>
) : null}
</>
);

View File

@ -7,7 +7,7 @@ import { useIntl } from 'react-intl';
import DataTable, { createColumnHelper } from '@/components/DataTable';
import { useConceptNavigation } from '@/context/NavigationContext';
import { ILibraryItem } from '@/models/library';
import { animateSideView } from '@/utils/animations';
import { animateSideView } from '@/styling/animations';
interface ViewSubscriptionsProps {
items: ILibraryItem[];

View File

@ -190,3 +190,25 @@ export const animateModal = {
}
}
};
export const animateFadeIn = {
initial: {
opacity: 0
},
animate: {
opacity: 1,
transition: {
type: 'tween',
ease: 'linear',
duration: 0.3
}
},
exit: {
opacity: 0,
transition: {
type: 'tween',
ease: 'linear',
duration: 2
}
}
};

View File

@ -0,0 +1,54 @@
/**
* Module: Define CSS variables.
*/
/* prettier-ignore */
:root {
--font-ui: 'Geologica', sans-serif;
--font-main: 'Rubik', 'Noto Sans Math', 'Noto Sans Symbols 2', 'Segoe UI Symbol', sans-serif;
--font-math: 'Noto Sans Math', 'Noto Sans Symbols 2', 'Segoe UI Symbol', sans-serif;
/* Light Theme */
--cl-bg-120: hsl(000, 000%, 100%);
--cl-bg-100: hsl(000, 000%, 098%);
--cl-bg-80: hsl(000, 000%, 094%);
--cl-bg-60: hsl(000, 000%, 091%);
--cl-bg-40: hsl(000, 000%, 080%);
--cl-fg-60: hsl(000, 000%, 065%);
--cl-fg-80: hsl(000, 000%, 047%);
--cl-fg-100: hsl(000, 000%, 000%);
--cl-prim-bg-100: hsl(220, 100%, 060%);
--cl-prim-bg-80: hsl(220, 080%, 092%);
--cl-prim-bg-60: hsl(190, 080%, 094%);
--cl-prim-fg-80: hsl(220, 100%, 050%);
--cl-prim-fg-100: hsl(000, 000%, 100%);
--cl-red-bg-100: hsl(000, 100%, 095%);
--cl-red-fg-100: hsl(000, 072%, 051%);
--cl-green-fg-100: hsl(120, 080%, 37%);
/* Dark Theme */
--cd-bg-120: hsl(000, 000%, 005%);
--cd-bg-100: hsl(000, 000%, 009%);
--cd-bg-80: hsl(000, 000%, 015%);
--cd-bg-60: hsl(000, 000%, 022%);
--cd-bg-40: hsl(000, 000%, 035%);
--cd-fg-60: hsl(000, 000%, 055%);
--cd-fg-80: hsl(000, 000%, 080%);
--cd-fg-100: hsl(000, 000%, 093%);
--cd-prim-bg-100: hsl(267, 050%, 050%);
--cd-prim-bg-80: hsl(267, 050%, 032%);
--cd-prim-bg-60: hsl(269, 030%, 028%);
--cd-prim-fg-80: hsl(267, 070%, 070%);
--cd-prim-fg-100: hsl(000, 000%, 100%);
--cd-red-bg-100: hsl(000, 100%, 015%);
--cd-red-fg-100: hsl(000, 080%, 055%);
--cd-green-fg-100: hsl(120, 080%, 042%);
}

View File

@ -0,0 +1,5 @@
/**
* Module: List external styling imports.
*/
@import 'react-toastify/dist/ReactToastify.css';

View File

@ -0,0 +1,40 @@
/* Depth layers */
.z-bottom {
z-index: 0;
}
.z-pop {
z-index: 10;
}
:is(.z-sticky, .sticky) {
z-index: 20;
}
.z-tooltip {
z-index: 30;
}
.z-navigation {
z-index: 50;
}
.z-modal {
z-index: 60;
}
.z-modal-controls {
z-index: 70;
}
.z-modal-top {
z-index: 80;
}
.z-modal-tooltip {
z-index: 90;
}
.z-topmost {
z-index: 99;
}

View File

@ -0,0 +1,38 @@
/**
* Module: Override imported components CSS styling.
*/
:root {
/* Import overrides */
--toastify-color-dark: var(--cd-bg-60);
}
.cm-editor {
resize: vertical;
overflow-y: auto;
border-color: var(--cl-bg-40);
.dark & {
border-color: var(--cd-bg-40);
}
@apply border rounded px-[0.375rem] py-[0.15rem];
}
.cm-editor.cm-focused {
border-color: var(--cl-bg-40);
outline-color: var(--cl-prim-bg-100);
.dark & {
border-color: var(--cd-bg-40);
outline-color: var(--cd-prim-bg-100);
}
@apply outline-2 outline;
}
.cm-editor .cm-placeholder {
color: var(--cl-fg-60);
.dark & {
color: var(--cd-fg-60);
}
}
.rdt_TableCell {
font-size: 0.875rem;
}

View File

@ -0,0 +1,113 @@
/**
* Module: Basic styling setup.
*/
@import './constants.css';
@import './layers.css';
@tailwind base;
@tailwind components;
@tailwind utilities;
html {
hanging-punctuation: first last;
color-scheme: dark light;
}
[data-color-scheme='dark'] {
color-scheme: dark;
}
[data-color-scheme='light'] {
color-scheme: light;
}
/* Default scroll behavior */
@media (prefers-reduced-motion: no-preference) {
:has(:target) {
scroll-behavior: smooth;
scroll-padding-top: 3rem;
}
}
:root {
font-family: var(--font-main);
color: var(--cl-fg-100);
border-color: var(--cl-bg-40);
background-color: var(--cl-bg-100);
&.dark {
color: var(--cd-fg-100);
border-color: var(--cd-bg-40);
background-color: var(--cd-bg-100);
}
}
:focus {
outline-width: 2px;
outline-style: solid;
outline-color: transparent;
.dark & {
outline-color: transparent;
}
}
::selection {
background: var(--cl-prim-bg-60);
.dark & {
background: var(--cd-prim-bg-60);
}
tr :hover& {
background: var(--cl-red-bg-100);
.dark & {
background: var(--cd-red-bg-100);
}
}
}
::placeholder {
color: var(--cl-fg-60);
.dark & {
color: var(--cd-fg-60);
}
}
/* Wrapping headers */
h1,
h2,
h3,
h4,
h5,
h6 {
text-wrap: balance;
}
/* Limit text lines and setup wrapping */
p,
li {
max-width: 90ch;
text-wrap: pretty;
}
@layer components {
h1 {
@apply text-lg font-semibold text-center;
}
h2 {
@apply font-semibold text-center;
}
b {
@apply font-semibold;
}
.border {
@apply rounded;
}
.shadow-border {
@apply shadow-sm shadow-[var(--cl-bg-40)] dark:shadow-[var(--cd-bg-40)];
}
}

View File

@ -0,0 +1,177 @@
/**
* Module: Custom styling.
*/
@import './constants.css';
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer utilities {
.font-controls {
font-family: var(--font-ui);
font-weight: 600;
font-variant: small-caps;
}
.font-math {
font-family: var(--font-math);
}
}
@layer components {
.clr-modal-backdrop {
opacity: 0.75;
}
:is(
.clr-border,
.border,
.border-x,
.border-y,
.border-b,
.border-t,
.border-l,
.border-r,
.border-2,
.border-x-2,
.border-y-2,
.border-b-2,
.border-t-2,
.border-l-2,
.border-r-2,
.divide-x,
.divide-y,
.divide-x-2,
.divide-y-2
) {
border-color: var(--cl-bg-40);
@apply divide-inherit;
.dark & {
border-color: var(--cd-bg-40);
}
}
:is(.clr-app, .clr-footer, .clr-modal-backdrop, .clr-btn-nav, .clr-input:disabled) {
background-color: var(--cl-bg-100);
.dark & {
background-color: var(--cd-bg-100);
}
}
:is(.clr-input) {
background-color: var(--cl-bg-120);
.dark & {
background-color: var(--cd-bg-120);
}
}
:is(.clr-controls, .clr-tab, .clr-btn-default) {
background-color: var(--cl-bg-80);
.dark & {
background-color: var(--cd-bg-80);
}
}
:is(.clr-primary, .clr-btn-primary:hover, .clr-btn-primary:focus) {
@apply transition;
color: var(--cl-prim-fg-100);
background-color: var(--cl-prim-bg-100);
.dark & {
color: var(--cd-prim-fg-100);
background-color: var(--cd-prim-bg-100);
}
}
:is(.clr-selected, .clr-btn-primary) {
color: var(--cl-fg-100);
background-color: var(--cl-prim-bg-80);
.dark & {
color: var(--cd-fg-100);
background-color: var(--cd-prim-bg-80);
}
}
:is(.clr-disabled, .clr-btn-default, .clr-btn-primary):disabled {
@apply transition;
color: var(--cl-fg-80);
background-color: var(--cl-bg-60);
.dark & {
color: var(--cd-fg-80);
background-color: var(--cd-bg-60);
}
}
:is(.clr-hover, .clr-tab, .clr-btn-nav, .clr-btn-default):hover:not(:disabled) {
@apply transition;
color: var(--cl-fg-100);
background-color: var(--cl-prim-bg-60);
.dark & {
color: var(--cd-fg-100);
background-color: var(--cd-prim-bg-60);
}
}
:is(.clr-outline, .clr-btn-primary):focus {
outline-width: 2px;
outline-style: solid;
outline-color: var(--cl-prim-bg-100);
.dark & {
outline-color: var(--cd-prim-bg-100);
}
}
:is(.clr-text-primary, .clr-text-url) {
color: var(--cl-prim-fg-80);
.dark & {
color: var(--cd-prim-fg-80);
}
}
.clr-footer {
color: var(--cl-fg-60);
.dark & {
color: var(--cd-fg-60);
}
}
:is(.clr-text-controls, .clr-btn-nav, .clr-btn-clear) {
color: var(--cl-fg-80);
&:disabled {
color: var(--cl-fg-60);
}
.dark & {
color: var(--cd-fg-80);
&:disabled {
color: var(--cd-fg-60);
}
}
}
.clr-warning {
background-color: var(--cl-red-bg-100);
.dark & {
background-color: var(--cd-red-bg-100);
}
}
.clr-text-default {
color: var(--cl-fg-100);
.dark & {
color: var(--cd-fg-100);
}
}
.clr-text-warning {
color: var(--cl-red-fg-100);
.dark & {
color: var(--cd-red-fg-100);
}
}
.clr-text-success {
color: var(--cl-green-fg-100);
.dark & {
color: var(--cd-green-fg-100);
}
}
}

View File

@ -10,7 +10,7 @@ import { IEntityReference, ISyntacticReference } from '@/models/language';
import { parseGrammemes } from '@/models/languageAPI';
import { IConstituenta } from '@/models/rsform';
import { colorFgGrammeme, IColorTheme } from './color';
import { colorFgGrammeme, IColorTheme } from '../styling/color';
import { describeConstituentaTerm, labelCstTypification, labelGrammeme } from './labels';
/**