nicolium: style migrations

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2026-02-10 17:56:02 +01:00
parent 1b5afbbd23
commit 85fe8113c4
7 changed files with 209 additions and 124 deletions

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import Card, { CardBody } from '@/components/ui/card'; import Card from '@/components/ui/card';
interface IBigCard { interface IBigCard {
title: React.ReactNode; title: React.ReactNode;
@ -10,7 +10,6 @@ interface IBigCard {
const BigCard: React.FC<IBigCard> = ({ title, subtitle, children }) => ( const BigCard: React.FC<IBigCard> = ({ title, subtitle, children }) => (
<Card variant='rounded' size='xl'> <Card variant='rounded' size='xl'>
<CardBody>
<div className='⁂-big-card__header'> <div className='⁂-big-card__header'>
<h1>{title}</h1> <h1>{title}</h1>
{subtitle && <p>{subtitle}</p>} {subtitle && <p>{subtitle}</p>}
@ -19,7 +18,6 @@ const BigCard: React.FC<IBigCard> = ({ title, subtitle, children }) => (
<div className='⁂-big-card__body'> <div className='⁂-big-card__body'>
{children} {children}
</div> </div>
</CardBody>
</Card> </Card>
); );

View File

@ -480,14 +480,14 @@ const ParsedMfm: React.FC<IParsedMfm> = React.memo(({ text, emojis, mentions, sp
const filename = emoji.static_url; const filename = emoji.static_url;
if (filename?.length > 0) { if (filename?.length > 0) {
return <img draggable={false} className='emojione !h-[2em] !w-[2em] transition-transform hover:scale-125' alt={token.props.name} title={token.props.name} src={filename} />; return <img draggable={false} className='emojione ⁂-emoji !h-[2em] !w-[2em]' alt={token.props.name} title={token.props.name} src={filename} />;
} }
return <bdi>{token.props.name}</bdi>; return <bdi>{token.props.name}</bdi>;
} }
case 'unicodeEmoji': { case 'unicodeEmoji': {
return <Emoji emoji={token.props.emoji} className='emojione !h-[2em] !w-[2em]' />; return <Emoji emoji={token.props.emoji} className='emojione ⁂-emoji !h-[2em] !w-[2em]' />;
} }
// TODO // TODO

View File

@ -2,9 +2,6 @@ import React, { useEffect, useRef, useState } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { NODE_ENV } from '@/build-config'; import { NODE_ENV } from '@/build-config';
import HStack from '@/components/ui/hstack';
import Stack from '@/components/ui/stack';
import Text from '@/components/ui/text';
import Textarea from '@/components/ui/textarea'; import Textarea from '@/components/ui/textarea';
import { useFrontendConfig } from '@/hooks/use-frontend-config'; import { useFrontendConfig } from '@/hooks/use-frontend-config';
import { useLogo } from '@/hooks/use-logo'; import { useLogo } from '@/hooks/use-logo';
@ -81,18 +78,11 @@ const SiteError: ErrorRouteComponent = ({ error, info }) => {
if (isNetworkError(error)) { if (isNetworkError(error)) {
return ( return (
<Column label={intl.formatMessage(messages.networkErrorTitle)}> <Column label={intl.formatMessage(messages.networkErrorTitle)}>
<Stack space={4} alignItems='center' justifyContent='center' className='min-h-[160px] rounded-lg p-10'> <div className='⁂-network-error'>
{/* <IconButton <p>
iconClassName='h-10 w-10'
title={intl.formatMessage(messages.networkErrorRetry)}
src={require('@phosphor-icons/core/regular/arrows-clockwise.svg')}
onClick={handleRetry}
/> */}
<Text align='center' theme='muted'>
<FormattedMessage id='bundle_column_error.body' defaultMessage='Something went wrong while loading this page.' /> <FormattedMessage id='bundle_column_error.body' defaultMessage='Something went wrong while loading this page.' />
</Text> </p>
</Stack> </div>
</Column> </Column>
); );
} }
@ -101,25 +91,23 @@ const SiteError: ErrorRouteComponent = ({ error, info }) => {
<div className='⁂-site-error'> <div className='⁂-site-error'>
<main> <main>
{logoSrc && ( {logoSrc && (
<div className='flex shrink-0 justify-center'> <a href='/' className='⁂-site-error__logo'>
<a href='/' className='inline-flex'> <SiteLogo />
<SiteLogo className='h-12 w-auto cursor-pointer' />
</a> </a>
</div>
)} )}
<div className='py-8'> <div className='⁂-site-error__body'>
<div className='mx-auto max-w-xl space-y-2 text-center'> <div className='⁂-site-error__message'>
<h1 className='text-3xl font-extrabold tracking-tight text-gray-900 dark:text-gray-500 sm:text-4xl'> <h1>
<FormattedMessage id='alert.unexpected.message' defaultMessage='Something went wrong.' /> <FormattedMessage id='alert.unexpected.message' defaultMessage='Something went wrong.' />
</h1> </h1>
<p className='text-lg text-gray-700 dark:text-gray-600'> <p className='⁂-site-error__message__body'>
<FormattedMessage <FormattedMessage
id='alert.unexpected.body' id='alert.unexpected.body'
defaultMessage="We're sorry for the interruption. If the problem persists, please report it in our {issueTracker}. You may also try to {clearCookies} (this will log you out)." defaultMessage="We're sorry for the interruption. If the problem persists, please report it in our {issueTracker}. You may also try to {clearCookies} (this will log you out)."
values={{ values={{
issueTracker: ( issueTracker: (
<a href={sourceCode.url + '/issues'} target='_blank' rel='noopener noreferrer' className='text-primary-600 hover:underline dark:text-primary-400'> <a href={sourceCode.url + '/issues'} target='_blank' rel='noopener noreferrer'>
<FormattedMessage <FormattedMessage
id='alert.unexpected.issue_tracker' id='alert.unexpected.issue_tracker'
defaultMessage='issue tracker' defaultMessage='issue tracker'
@ -127,7 +115,7 @@ const SiteError: ErrorRouteComponent = ({ error, info }) => {
</a> </a>
), ),
clearCookies: ( clearCookies: (
<a href='/' onClick={clearCookies} className='text-primary-600 hover:underline dark:text-primary-400'> <a href='/' onClick={clearCookies}>
<FormattedMessage <FormattedMessage
id='alert.unexpected.clear_cookies' id='alert.unexpected.clear_cookies'
defaultMessage='clear cookies and browser data' defaultMessage='clear cookies and browser data'
@ -138,20 +126,17 @@ const SiteError: ErrorRouteComponent = ({ error, info }) => {
/> />
</p> </p>
<Text theme='muted'> <p className='⁂-site-error__message__version'>
<Text weight='medium' tag='span' theme='muted'>{sourceCode.displayName}:</Text> <span>{sourceCode.displayName}:</span>
{' '}{sourceCode.version} {' '}{sourceCode.version}
</Text> </p>
</div> </div>
<div className='mx-auto max-w-lg space-y-4 py-16'> <div className='⁂-site-error__form'>
{(isProduction) ? ( {isProduction && sentryEnabled && sentryEventId && (
(sentryEnabled && sentryEventId) && (
<SentryFeedbackForm eventId={sentryEventId} /> <SentryFeedbackForm eventId={sentryEventId} />
) )}
) : (
<>
{errorText && ( {errorText && (
<Textarea <Textarea
ref={textarea} ref={textarea}
@ -164,19 +149,17 @@ const SiteError: ErrorRouteComponent = ({ error, info }) => {
)} )}
{browser && ( {browser && (
<Stack> <p className='⁂-site-error__browser'>
<Text weight='semibold'><FormattedMessage id='alert.unexpected.browser' defaultMessage='Browser' /></Text> <span><FormattedMessage id='alert.unexpected.browser' defaultMessage='Browser' />{': '}</span>{browser.getBrowserName()} {browser.getBrowserVersion()}
<Text theme='muted'>{browser.getBrowserName()} {browser.getBrowserVersion()}</Text> </p>
</Stack>
)}
</>
)} )}
</div> </div>
</div> </div>
</main> </main>
{[links.status, links.help, links.support].some(Boolean) && (
<footer> <footer>
<HStack justifyContent='center' space={4} element='nav'> <nav>
{links.status && ( {links.status && (
<SiteErrorBoundaryLink href={links.status}> <SiteErrorBoundaryLink href={links.status}>
<FormattedMessage id='alert.unexpected.links.status' defaultMessage='Status' /> <FormattedMessage id='alert.unexpected.links.status' defaultMessage='Status' />
@ -194,8 +177,9 @@ const SiteError: ErrorRouteComponent = ({ error, info }) => {
<FormattedMessage id='alert.unexpected.links.support' defaultMessage='Support' /> <FormattedMessage id='alert.unexpected.links.support' defaultMessage='Support' />
</SiteErrorBoundaryLink> </SiteErrorBoundaryLink>
)} )}
</HStack> </nav>
</footer> </footer>
)}
</div> </div>
); );
}; };
@ -207,10 +191,8 @@ interface ISiteErrorBoundaryLink {
const SiteErrorBoundaryLink = ({ href, children }: ISiteErrorBoundaryLink) => ( const SiteErrorBoundaryLink = ({ href, children }: ISiteErrorBoundaryLink) => (
<> <>
<span className='inline-block border-l border-gray-300' aria-hidden='true' /> <span aria-hidden='true' />
<a href={href} className='text-sm font-medium text-gray-700 hover:underline dark:text-gray-600'> <a href={href}>{children}</a>
{children}
</a>
</> </>
); );

View File

@ -5,9 +5,6 @@ import { FormattedMessage } from 'react-intl';
import { useLocale, useLocaleDirection } from '@/hooks/use-locale'; import { useLocale, useLocaleDirection } from '@/hooks/use-locale';
import { getTextDirection } from '@/utils/rtl'; import { getTextDirection } from '@/utils/rtl';
import Stack from './stack';
import Text from './text';
interface ITextarea extends Pick<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'className' | 'id' | 'lang' | 'maxLength' | 'onChange' | 'onClick' | 'onKeyDown' | 'onKeyUp' | 'onPaste' | 'required' | 'disabled' | 'rows' | 'readOnly'> { interface ITextarea extends Pick<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'className' | 'id' | 'lang' | 'maxLength' | 'onChange' | 'onClick' | 'onKeyDown' | 'onKeyUp' | 'onPaste' | 'required' | 'disabled' | 'rows' | 'readOnly'> {
/** Put the cursor into the input on mount. */ /** Put the cursor into the input on mount. */
autoFocus?: boolean; autoFocus?: boolean;
@ -31,12 +28,10 @@ interface ITextarea extends Pick<React.TextareaHTMLAttributes<HTMLTextAreaElemen
autoComplete?: string; autoComplete?: string;
/** Whether to display the textarea in red. */ /** Whether to display the textarea in red. */
hasError?: boolean; hasError?: boolean;
/** Whether or not you can resize the teztarea */ /** Whether or not you can resize the textarea */
isResizeable?: boolean; isResizeable?: boolean;
/** Textarea theme. */ /** Textarea theme. */
theme?: 'default' | 'transparent'; theme?: 'default' | 'transparent';
/** Whether to display a character counter below the textarea. */
withCounter?: boolean;
} }
/** Textarea with custom styles. */ /** Textarea with custom styles. */
@ -84,37 +79,41 @@ const Textarea = React.forwardRef(({
} }
}; };
return ( const textarea = (
<Stack space={1.5}>
<textarea <textarea
{...props} {...props}
value={value} value={value}
ref={ref} ref={ref}
rows={rows} rows={rows}
onChange={handleChange} onChange={handleChange}
className={clsx('block w-full rounded-md text-gray-900 placeholder:text-gray-600 dark:text-gray-100 dark:placeholder:text-gray-600 sm:text-sm', { className={clsx('⁂-textarea', {
'bg-white dark:bg-transparent shadow-sm border-gray-400 dark:border-gray-800 dark:ring-1 dark:ring-gray-800 focus:ring-primary-500 focus:border-primary-500 dark:focus:ring-primary-500 dark:focus:border-primary-500': '⁂-textarea--transparent': theme === 'transparent',
theme === 'default', '⁂-textarea--mono': isCodeEditor,
'bg-transparent border-0 focus:border-0 focus:ring-0': theme === 'transparent', '⁂-textarea--has-error': hasError,
'font-mono': isCodeEditor, '⁂-textarea--resizable': isResizeable,
'text-red-600 border-red-600': hasError,
'resize-none': !isResizeable,
}, className)} }, className)}
dir={value?.length ? getTextDirection(value, { fallback: direction }) : undefined} dir={value?.length ? getTextDirection(value, { fallback: direction }) : undefined}
/> />
);
if (!maxLength) {
return textarea;
}
return (
<div className='⁂-textarea__container'>
{textarea}
{maxLength && ( {maxLength && (
<div className='text-right rtl:text-left'> <p className={clsx('⁂-textarea__max-length', { '⁂-textarea__max-length--exceeded': maxLength - length < 0 })}>
<Text size='xs' theme={maxLength - length < 0 ? 'danger' : 'muted'}>
<FormattedMessage <FormattedMessage
id='textarea.counter.label' id='textarea.counter.label'
defaultMessage='{count} characters remaining' defaultMessage='{count} characters remaining'
values={{ count: maxLength - length }} values={{ count: maxLength - length }}
/> />
</Text> </p>
</div>
)} )}
</Stack> </div>
); );
}, },
); );

View File

@ -24,8 +24,7 @@ const MaybeEmoji: React.FC<IMaybeEmoji> = ({ text, emojis }) => {
const filename = emoji.static_url; const filename = emoji.static_url;
if (filename?.length > 0) { if (filename?.length > 0) {
return <Emoji className='emojione transition-transform hover:scale-125' emoji={text} src={filename} />; return <Emoji className='emojione ⁂-emoji' emoji={text} src={filename} />;
// return <img draggable={false} className='emojione transition-transform hover:scale-125' alt={text} title={text} src={filename} />;
} }
} }
@ -72,7 +71,7 @@ const Emojify: React.FC<IEmojify> = React.memo(({ text, emojis = {} }) => {
const { unified, shortcode } = unicodeMapping[c]; const { unified, shortcode } = unicodeMapping[c];
nodes.push( nodes.push(
<img key={index} draggable={false} className='emojione transition-transform hover:scale-125' alt={c} title={`:${shortcode}:`} src={joinPublicPath(`packs/emoji/${unified}.svg`)} />, <img key={index} draggable={false} className='emojione ⁂-emoji' alt={c} title={`:${shortcode}:`} src={joinPublicPath(`packs/emoji/${unified}.svg`)} />,
); );
} else if (!systemEmojiFont && unqualified in unicodeMapping) { } else if (!systemEmojiFont && unqualified in unicodeMapping) {
clearStack(); clearStack();
@ -80,7 +79,7 @@ const Emojify: React.FC<IEmojify> = React.memo(({ text, emojis = {} }) => {
const { unified, shortcode } = unicodeMapping[unqualified]; const { unified, shortcode } = unicodeMapping[unqualified];
nodes.push( nodes.push(
<img key={index} draggable={false} className='emojione transition-transform hover:scale-125' alt={unqualified} title={`:${shortcode}:`} src={joinPublicPath(`packs/emoji/${unified}.svg`)} />, <img key={index} draggable={false} className='emojione ⁂-emoji' alt={unqualified} title={`:${shortcode}:`} src={joinPublicPath(`packs/emoji/${unified}.svg`)} />,
); );
} else if (!disableUserProvidedMedia && c === ':') { } else if (!disableUserProvidedMedia && c === ':') {
if (!open) { if (!open) {

View File

@ -523,3 +523,44 @@ div[data-viewport-type="window"]:has(.⁂-empty-message) {
@apply mx-auto sm:w-2/3 sm:pt-10; @apply mx-auto sm:w-2/3 sm:pt-10;
} }
} }
.-emoji {
@apply transition-transform hover:scale-125;
}
.-textarea {
@apply block w-full rounded-md text-gray-900 placeholder:text-gray-600 dark:text-gray-100 dark:placeholder:text-gray-600 sm:text-sm resize-none;
&--transparent {
@apply bg-transparent border-0 p-0 focus:ring-0;
}
&:not(&--transparent) {
@apply bg-white border border-gray-300 p-2 focus:border-primary-500 focus:ring-1 focus:ring-primary-500 disabled:opacity-50 black:bg-black dark:border-gray-800 dark:bg-gray-900 dark:text-gray-100 dark:ring-1 dark:ring-gray-800 dark:focus:border-primary-500 dark:focus:ring-primary-500;
}
&--mono {
@apply font-mono;
}
&--has-error {
@apply text-red-600 border-red-600;
}
&--resizable {
@apply resize;
}
&__container {
@apply flex flex-col gap-1.5;
}
&__max-length {
@include mixins.text($size: xs, $theme: muted);
@apply text-right rtl:text-left;
&--exceeded {
@apply text-danger-700 dark:text-danger-500;
}
}
}

View File

@ -452,9 +452,75 @@ div:has(.⁂-background-shapes), .dark {
@apply mx-auto flex w-full max-w-7xl grow flex-col justify-center px-4 sm:px-6 lg:px-8; @apply mx-auto flex w-full max-w-7xl grow flex-col justify-center px-4 sm:px-6 lg:px-8;
} }
&__body {
@apply py-8;
}
&__message {
@apply mx-auto max-w-xl space-y-2 text-center;
h1 {
@apply text-3xl font-extrabold tracking-tight text-gray-900 dark:text-gray-500 sm:text-4xl;
}
&__body {
@apply text-lg text-gray-700 dark:text-gray-600;
a {
@apply text-primary-600 hover:underline dark:text-primary-400;
}
}
}
&__form {
@apply mx-auto max-w-lg space-y-4 py-16;
&:last-child {
@apply pb-0;
}
}
&__message__version, &__browser {
@include mixins.text($theme: muted);
span {
@apply font-semibold;
}
}
footer { footer {
@apply mx-auto w-full max-w-7xl shrink-0 px-4 sm:px-6 lg:px-8; @apply mx-auto w-full max-w-7xl shrink-0 px-4 sm:px-6 lg:px-8;
} }
nav {
@apply flex justify-center gap-4;
span {
@apply inline-block border-l border-gray-300;
}
a {
@apply text-sm font-medium text-gray-700 hover:underline dark:text-gray-600;
}
}
&__logo {
@apply flex shrink-0 justify-center;
img {
@apply h-12 w-auto;
}
}
}
.-network-error {
@apply flex gap-4 items-center justify-center min-h-[160px] rounded-lg p-10;
p {
@include mixins.text($align: center, $theme: muted);
}
} }
.-layout__content .-site-error { .-layout__content .-site-error {