feat(web): tailwind css (#1147)

This commit is contained in:
Aman Harwara
2022-06-28 02:50:52 +05:30
committed by GitHub
parent 0ead805412
commit b80038f607
201 changed files with 1824 additions and 2699 deletions

Binary file not shown.

View File

@@ -37,7 +37,7 @@ const GroupSummary: React.FC<GroupSummaryProps> = ({ groups }) => {
return (
<p data-testid="group-summary" key={`group-${group.name}`} className="mb-1">
{truncateText(group.name, MAX_GROUP_DESCRIPTION_LENGTH)}
<span className="px-2 neutral">
<span className="px-2 text-neutral">
{totalCompletedTasks}/{totalTasks}
</span>
</p>

View File

@@ -69,7 +69,7 @@ panel-resizer {
/* Required for BrowserWindow titleBarStyle: 'hiddenInset' */
.mac-desktop #navigation,
.mac-desktop #navigation .section-title-bar,
.mac-desktop #notes-title-bar,
.mac-desktop #items-title-bar,
.mac-desktop #editor-title-bar,
.mac-desktop #lock-screen {
-webkit-app-region: drag;

View File

@@ -23,7 +23,7 @@ export class SKAlert {
buttonsString() {
const genButton = function (buttonDesc: AlertButton, index: number) {
return `
<button id='button-${index}' class='sn-button small ${buttonDesc.style}'>
<button id='button-${index}' class='font-bold px-2.5 py-2 text-xs text-info-contrast bg-${buttonDesc.style}'>
<div class='sk-label'>${buttonDesc.text}</div>
</button>
`

View File

@@ -0,0 +1,68 @@
:root {
--sn-stylekit-neutral-color: #989898;
--sn-stylekit-neutral-contrast-color: #ffffff;
--sn-stylekit-info-color: #086dd6;
--sn-stylekit-info-color-darkened: #065cb5;
--sn-stylekit-info-contrast-color: #ffffff;
--sn-stylekit-info-backdrop-color: #2b6fcf0f;
--sn-stylekit-success-color: #007662;
--sn-stylekit-success-contrast-color: #ffffff;
--sn-stylekit-warning-color: #ebad00;
--sn-stylekit-warning-contrast-color: #ffffff;
--sn-stylekit-danger-color: #cc2128;
--sn-stylekit-danger-contrast-color: #ffffff;
--sn-stylekit-shadow-color: #c8c8c8;
--sn-stylekit-background-color: #ffffff;
// For borders inside background-color
--sn-stylekit-border-color: #dfe1e4;
--sn-stylekit-foreground-color: #000000;
// Colors for layers placed on top of non-prefixed background, border, and foreground
--sn-stylekit-contrast-background-color: #f6f6f6;
--sn-stylekit-contrast-foreground-color: #2e2e2e;
--sn-stylekit-contrast-border-color: #e3e3e3; // For borders inside contrast-background-color
// Alternative set of background and contrast options
--sn-stylekit-secondary-background-color: #f6f6f6;
--sn-stylekit-secondary-foreground-color: #2e2e2e;
--sn-stylekit-secondary-border-color: #e3e3e3;
--sn-stylekit-secondary-contrast-background-color: #e3e3e3;
--sn-stylekit-secondary-contrast-foreground-color: #2e2e2e;
--sn-stylekit-secondary-contrast-border-color: #a2a2a2;
--sn-stylekit-editor-background-color: var(--sn-stylekit-background-color);
--sn-stylekit-editor-foreground-color: var(--sn-stylekit-foreground-color);
--sn-stylekit-paragraph-text-color: #454545;
--sn-stylekit-input-placeholder-color: #a8a8a8;
--sn-stylekit-input-border-color: #e3e3e3;
--sn-stylekit-scrollbar-thumb-color: #dfdfdf;
--sn-stylekit-scrollbar-track-border-color: #e7e7e7;
--sn-stylekit-theme-type: light;
--sn-stylekit-theme-name: sn-light;
--sn-stylekit-passive-color-0: #515357;
--sn-stylekit-passive-color-1: #72767e;
--sn-stylekit-passive-color-2: #bbbec4;
--sn-stylekit-passive-color-3: #dfe1e4;
--sn-stylekit-passive-color-4: #eeeff1;
--sn-stylekit-passive-color-4-opacity-variant: #bbbec43d;
--sn-stylekit-passive-color-5: #f4f5f7;
--sn-stylekit-passive-color-6: #e5e5e5;
--sn-stylekit-passive-color-super-light: #f9f9f9;
--sn-stylekit-accessory-tint-color-1: #086dd6;
--sn-stylekit-accessory-tint-color-2: #ea6595;
--sn-stylekit-accessory-tint-color-3: #ebad00;
--sn-stylekit-accessory-tint-color-4: #7049cf;
--sn-stylekit-accessory-tint-color-5: #1aa772;
--sn-stylekit-accessory-tint-color-6: #f28c52;
}

View File

@@ -131,7 +131,8 @@
}
.sk-panel-section-subtitle {
@extend .sk-label;
font-size: var(--sn-stylekit-font-size-p);
font-weight: bold;
font-size: var(--sn-stylekit-font-size-h5);
margin-bottom: 2px;

View File

@@ -1,4 +1,5 @@
@import 'normalize';
@import 'colors';
:root {
--sn-stylekit-base-font-size: 0.8125rem;
@@ -13,53 +14,6 @@
--sn-stylekit-font-size-h2: 0.975rem;
--sn-stylekit-font-size-h1: 1.05625rem;
--sn-stylekit-neutral-color: #989898;
--sn-stylekit-neutral-contrast-color: #ffffff;
--sn-stylekit-info-color: #086DD6;
--sn-stylekit-info-color-darkened: #065cb5;
--sn-stylekit-info-contrast-color: #ffffff;
--sn-stylekit-info-backdrop-color: #2b6fcf0f;
--sn-stylekit-success-color: #007662;
--sn-stylekit-success-contrast-color: #ffffff;
--sn-stylekit-warning-color: #EBAD00;
--sn-stylekit-warning-contrast-color: #ffffff;
--sn-stylekit-danger-color: #cc2128;
--sn-stylekit-danger-contrast-color: #ffffff;
--sn-stylekit-shadow-color: #c8c8c8;
--sn-stylekit-background-color: #ffffff;
// For borders inside background-color
--sn-stylekit-border-color: #dfe1e4;
--sn-stylekit-foreground-color: #000000;
// Colors for layers placed on top of non-prefixed background, border, and foreground
--sn-stylekit-contrast-background-color: #f6f6f6;
--sn-stylekit-contrast-foreground-color: #2e2e2e;
--sn-stylekit-contrast-border-color: #e3e3e3; // For borders inside contrast-background-color
// Alternative set of background and contrast options
--sn-stylekit-secondary-background-color: #f6f6f6;
--sn-stylekit-secondary-foreground-color: #2e2e2e;
--sn-stylekit-secondary-border-color: #e3e3e3;
--sn-stylekit-secondary-contrast-background-color: #e3e3e3;
--sn-stylekit-secondary-contrast-foreground-color: #2e2e2e;
--sn-stylekit-secondary-contrast-border-color: #a2a2a2;
--sn-stylekit-editor-background-color: var(--sn-stylekit-background-color);
--sn-stylekit-editor-foreground-color: var(--sn-stylekit-foreground-color);
--sn-stylekit-paragraph-text-color: #454545;
--sn-stylekit-input-placeholder-color: #a8a8a8;
--sn-stylekit-input-border-color: #e3e3e3;
--sn-stylekit-scrollbar-thumb-color: #dfdfdf;
--sn-stylekit-scrollbar-track-border-color: #e7e7e7;
--sn-stylekit-menu-border: none;
--sn-stylekit-general-border-radius: 2px;
@@ -70,26 +24,6 @@
--sn-stylekit-sans-serif-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', var(--sn-stylekit-simplified-chinese-font), sans-serif;
--sn-stylekit-editor-font-family: var(--sn-stylekit-sans-serif-font);
--sn-stylekit-theme-type: light;
--sn-stylekit-theme-name: sn-light;
--sn-stylekit-passive-color-0: #515357;
--sn-stylekit-passive-color-1: #72767e;
--sn-stylekit-passive-color-2: #bbbec4;
--sn-stylekit-passive-color-3: #dfe1e4;
--sn-stylekit-passive-color-4: #eeeff1;
--sn-stylekit-passive-color-4-opacity-variant: #bbbec43d;
--sn-stylekit-passive-color-5: #f4f5f7;
--sn-stylekit-passive-color-6: #e5e5e5;
--sn-stylekit-passive-color-super-light: #f9f9f9;
--sn-stylekit-accessory-tint-color-1: #086dd6;
--sn-stylekit-accessory-tint-color-2: #ea6595;
--sn-stylekit-accessory-tint-color-3: #ebad00;
--sn-stylekit-accessory-tint-color-4: #7049cf;
--sn-stylekit-accessory-tint-color-5: #1aa772;
--sn-stylekit-accessory-tint-color-6: #f28c52;
}
.sn-component {

View File

@@ -12,23 +12,23 @@ const prefersReducedMotion = () => {
const colorForToastType = (type: ToastType) => {
switch (type) {
case ToastType.Success:
return 'color-success'
return 'text-success'
case ToastType.Error:
return 'color-danger'
return 'text-danger'
default:
return 'color-info'
return 'text-info'
}
}
const iconForToastType = (type: ToastType) => {
switch (type) {
case ToastType.Success:
return <CheckCircleFilledIcon className={colorForToastType(type)} />
return <CheckCircleFilledIcon className={`w-5 h-5 ${colorForToastType(type)}`} />
case ToastType.Error:
return <ClearCircleFilledIcon className={colorForToastType(type)} />
return <ClearCircleFilledIcon className={`w-5 h-5 ${colorForToastType(type)}`} />
case ToastType.Progress:
case ToastType.Loading:
return <div className="sk-spinner w-4 h-4 spinner-info" />
return <div className="animate-spin border border-solid border-info border-r-transparent rounded-full w-4 h-4" />
default:
return null
}
@@ -92,7 +92,7 @@ export const Toast = forwardRef(({ toast, index }: Props, ref: ForwardedRef<HTML
>
<div className={`flex items-center w-full ${hasActions ? 'p-2 pl-3' : hasProgress ? 'px-3 py-2.5' : 'p-3'}`}>
{icon ? <div className="flex flex-shrink-0 items-center justify-center sn-icon mr-2">{icon}</div> : null}
<div className="text-sm">{toast.message}</div>
<div className="text-sm text-text">{toast.message}</div>
{hasActions && (
<div className="ml-4">
{toast.actions?.map((action, index) => (
@@ -116,9 +116,9 @@ export const Toast = forwardRef(({ toast, index }: Props, ref: ForwardedRef<HTML
)}
</div>
{hasProgress && (
<div className="toast-progress-bar">
<div className="rounded w-full bg-default overflow-hidden rounded-tl-none rounded-tr-none">
<div
className="toast-progress-bar__value"
className="rounded h-2 bg-info rounded-tl-none transition-[width] duration-100"
role="progressbar"
style={{
width: `${toast.progress}%`,

View File

@@ -11,7 +11,7 @@ export const ToastContainer: FunctionComponent = () => {
}
return (
<div className="flex flex-col items-end fixed z-index-toast bottom-6 right-6">
<div className="flex flex-col items-end fixed z-toast bottom-6 right-6">
{toasts.map((toast, index) => (
<ToastTimer toast={toast} index={index} key={toast.id} />
))}

View File

@@ -28,6 +28,7 @@
"@types/react": "^17.0.42",
"@types/react-dom": "^18.0.5",
"@types/wicg-file-system-access": "^2020.9.5",
"autoprefixer": "^10.4.7",
"babel-loader": "^8.2.5",
"circular-dependency-plugin": "^5.2.2",
"css-loader": "*",
@@ -43,9 +44,12 @@
"mini-css-extract-plugin": "^2.6.0",
"node-sass": "*",
"npm-check-updates": "*",
"postcss": "^8.4.14",
"postcss-loader": "^7.0.0",
"prettier": "*",
"sass-loader": "*",
"svg-jest": "^1.0.1",
"tailwindcss": "^3.1.4",
"ts-jest": "^27.1.4",
"ts-loader": "^9.2.8",
"typescript": "*",

View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View File

@@ -58,8 +58,8 @@ const AccountMenu: FunctionComponent<Props> = ({
return (
<div ref={ref} id="account-menu" className="sn-component">
<div
className={`sn-account-menu sn-dropdown ${
shouldAnimateCloseMenu ? 'slide-up-animation' : 'sn-dropdown--animated'
className={`z-footer-bar-item-panel bottom-full left-0 cursor-auto bg-default rounded shadow-main ${
shouldAnimateCloseMenu ? 'slide-up-animation' : 'transition-transform duration-150 slide-down-animation'
} min-w-80 max-h-120 max-w-xs flex flex-col py-2 overflow-y-auto absolute`}
onKeyDown={handleKeyDown}
>

View File

@@ -98,12 +98,12 @@ const AdvancedOptions: FunctionComponent<Props> = ({
return (
<>
<button
className="sn-dropdown-item focus:bg-info-backdrop focus:shadow-none font-bold"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full text-sm focus:bg-info-backdrop focus:shadow-none font-bold"
onClick={toggleShowAdvanced}
>
<div className="flex items-center">
Advanced options
<Icon type="chevron-down" className="color-passive-1 ml-1" />
<Icon type="chevron-down" className="text-passive-1 ml-1" />
</div>
</button>
{showAdvanced ? (
@@ -119,7 +119,7 @@ const AdvancedOptions: FunctionComponent<Props> = ({
onChange={handleIsPrivateWorkspaceChange}
/>
<a href="https://standardnotes.com/help/80" target="_blank" rel="noopener noreferrer" title="Learn more">
<Icon type="info" className="color-neutral" />
<Icon type="info" className="text-neutral" />
</a>
</div>
@@ -127,7 +127,7 @@ const AdvancedOptions: FunctionComponent<Props> = ({
<>
<DecoratedInput
className={'mb-2'}
left={[<Icon type="server" className="color-neutral" />]}
left={[<Icon type="server" className="text-neutral" />]}
type="text"
placeholder="Userphrase"
value={privateWorkspaceUserphrase}
@@ -136,7 +136,7 @@ const AdvancedOptions: FunctionComponent<Props> = ({
/>
<DecoratedInput
className={'mb-2'}
left={[<Icon type="folder" className="color-neutral" />]}
left={[<Icon type="folder" className="text-neutral" />]}
type="text"
placeholder="Name"
value={privateWorkspaceName}
@@ -161,7 +161,7 @@ const AdvancedOptions: FunctionComponent<Props> = ({
rel="noopener noreferrer"
title="Learn more"
>
<Icon type="info" className="color-neutral" />
<Icon type="info" className="text-neutral" />
</a>
</div>
)}
@@ -175,7 +175,7 @@ const AdvancedOptions: FunctionComponent<Props> = ({
/>
<DecoratedInput
type="text"
left={[<Icon type="server" className="color-neutral" />]}
left={[<Icon type="server" className="text-neutral" />]}
placeholder="https://api.standardnotes.com"
value={server}
onChange={handleSyncServerChange}

View File

@@ -105,34 +105,35 @@ const ConfirmPassword: FunctionComponent<Props> = ({
<IconButton
icon="arrow-left"
title="Go back"
className="flex mr-2 color-neutral p-0"
className="flex mr-2 text-neutral p-0"
onClick={handleGoBack}
focusable={true}
disabled={isRegistering}
/>
<div className="sn-account-menu-headline">Confirm password</div>
<div className="font-bold text-base">Confirm password</div>
</div>
<div className="px-3 mb-3 text-sm">
Because your notes are encrypted using your password,{' '}
<span className="color-danger">Standard Notes does not have a password reset option</span>. If you forget your
<span className="text-danger">Standard Notes does not have a password reset option</span>. If you forget your
password, you will permanently lose access to your data.
</div>
<form onSubmit={handleConfirmFormSubmit} className="px-3 mb-1">
<DecoratedPasswordInput
className="mb-2"
disabled={isRegistering}
left={[<Icon type="password" className="color-neutral" />]}
left={[<Icon type="password" className="text-neutral" />]}
onChange={handlePasswordChange}
onKeyDown={handleKeyDown}
placeholder="Confirm password"
ref={passwordInputRef}
value={confirmPassword}
/>
{error ? <div className="color-danger my-2">{error}</div> : null}
{error ? <div className="text-danger my-2">{error}</div> : null}
<Button
className="btn-w-full mt-1 mb-3"
primary
fullWidth
className="mt-1 mb-3"
label={isRegistering ? 'Creating account...' : 'Create account & sign in'}
variant="primary"
onClick={handleConfirmFormSubmit}
disabled={isRegistering}
/>

View File

@@ -9,6 +9,7 @@ import DecoratedPasswordInput from '@/Components/Input/DecoratedPasswordInput'
import Icon from '@/Components/Icon/Icon'
import IconButton from '@/Components/Button/IconButton'
import AdvancedOptions from './AdvancedOptions'
import HorizontalSeparator from '../Shared/HorizontalSeparator'
type Props = {
viewControllerManager: ViewControllerManager
@@ -105,17 +106,17 @@ const CreateAccount: FunctionComponent<Props> = ({
<IconButton
icon="arrow-left"
title="Go back"
className="flex mr-2 color-neutral p-0"
className="flex mr-2 text-neutral p-0"
onClick={handleClose}
focusable={true}
/>
<div className="sn-account-menu-headline">Create account</div>
<div className="font-bold text-base">Create account</div>
</div>
<form onSubmit={handleRegisterFormSubmit} className="px-3 mb-1">
<DecoratedInput
className="mb-2"
disabled={isPrivateWorkspace}
left={[<Icon type="email" className="color-neutral" />]}
left={[<Icon type="email" className="text-neutral" />]}
onChange={handleEmailChange}
onKeyDown={handleKeyDown}
placeholder="Email"
@@ -125,16 +126,16 @@ const CreateAccount: FunctionComponent<Props> = ({
/>
<DecoratedPasswordInput
className="mb-2"
left={[<Icon type="password" className="color-neutral" />]}
left={[<Icon type="password" className="text-neutral" />]}
onChange={handlePasswordChange}
onKeyDown={handleKeyDown}
placeholder="Password"
ref={passwordInputRef}
value={password}
/>
<Button className="btn-w-full mt-1" label="Next" variant="primary" onClick={handleRegisterFormSubmit} />
<Button className="mt-1" label="Next" primary onClick={handleRegisterFormSubmit} fullWidth={true} />
</form>
<div className="h-1px my-2 bg-border"></div>
<HorizontalSeparator classes="my-2" />
<AdvancedOptions
application={application}
viewControllerManager={viewControllerManager}

View File

@@ -13,6 +13,7 @@ import { MenuItemType } from '@/Components/Menu/MenuItemType'
import WorkspaceSwitcherOption from './WorkspaceSwitcher/WorkspaceSwitcherOption'
import { ApplicationGroup } from '@/Application/ApplicationGroup'
import { formatLastSyncDate } from '@/Utils/FormatLastSyncDate'
import Spinner from '@/Components/Spinner/Spinner'
type Props = {
viewControllerManager: ViewControllerManager
@@ -22,7 +23,7 @@ type Props = {
closeMenu: () => void
}
const iconClassName = 'color-neutral mr-2'
const iconClassName = 'text-neutral mr-2'
const GeneralAccountMenu: FunctionComponent<Props> = ({
application,
@@ -89,34 +90,34 @@ const GeneralAccountMenu: FunctionComponent<Props> = ({
return (
<>
<div className="flex items-center justify-between px-3 mt-1 mb-1">
<div className="sn-account-menu-headline">Account</div>
<div className="font-bold text-base">Account</div>
<div className="flex cursor-pointer" onClick={closeMenu}>
<Icon type="close" className="color-neutral" />
<Icon type="close" className="text-neutral" />
</div>
</div>
{user ? (
<>
<div className="px-3 mb-3 color-foreground text-sm">
<div className="px-3 mb-3 text-foreground text-sm">
<div>You're signed in as:</div>
<div className="my-0.5 font-bold wrap">{user.email}</div>
<span className="color-neutral">{application.getHost()}</span>
<span className="text-neutral">{application.getHost()}</span>
</div>
<div className="flex items-start justify-between px-3 mb-3">
<div className="flex items-start justify-between px-3 mb-2">
{isSyncingInProgress ? (
<div className="flex items-center color-info font-semibold">
<div className="sk-spinner w-5 h-5 mr-2 spinner-info"></div>
<div className="flex items-center text-info text-sm font-semibold">
<Spinner className="w-5 h-5 mr-2" />
Syncing...
</div>
) : (
<div className="flex items-start">
<Icon type="check-circle" className="mr-2 success" />
<Icon type="check-circle" className="mr-2 text-success" />
<div>
<div className="font-semibold success">Last synced:</div>
<div className="color-text">{lastSyncDate}</div>
<div className="font-semibold text-success text-sm">Last synced:</div>
<div className="text-text text-sm">{lastSyncDate}</div>
</div>
</div>
)}
<div className="flex cursor-pointer color-passive-1" onClick={doSynchronization}>
<div className="flex cursor-pointer text-passive-1" onClick={doSynchronization}>
<Icon type="sync" />
</div>
</div>
@@ -124,13 +125,13 @@ const GeneralAccountMenu: FunctionComponent<Props> = ({
) : (
<>
<div className="px-3 mb-1">
<div className="mb-3 color-foreground">
<div className="mb-3 text-foreground text-sm">
Youre offline. Sign in to sync your notes and preferences across all your devices and enable end-to-end
encryption.
</div>
<div className="flex items-center color-passive-1">
<div className="flex items-center text-passive-1">
<Icon type="cloud-off" className="mr-2" />
<span className="font-semibold">Offline</span>
<span className="font-semibold text-sm">Offline</span>
</div>
</div>
</>
@@ -169,7 +170,7 @@ const GeneralAccountMenu: FunctionComponent<Props> = ({
<Icon type="help" className={iconClassName} />
Help &amp; feedback
</div>
<span className="color-neutral">v{application.version}</span>
<span className="text-neutral">v{application.version}</span>
</MenuItem>
{user ? (
<>

View File

@@ -11,6 +11,7 @@ import DecoratedPasswordInput from '@/Components/Input/DecoratedPasswordInput'
import Icon from '@/Components/Icon/Icon'
import IconButton from '@/Components/Button/IconButton'
import AdvancedOptions from './AdvancedOptions'
import HorizontalSeparator from '../Shared/HorizontalSeparator'
type Props = {
viewControllerManager: ViewControllerManager
@@ -143,17 +144,17 @@ const SignInPane: FunctionComponent<Props> = ({ application, viewControllerManag
<IconButton
icon="arrow-left"
title="Go back"
className="flex mr-2 color-neutral p-0"
className="flex mr-2 text-neutral p-0"
onClick={() => setMenuPane(AccountMenuPane.GeneralMenu)}
focusable={true}
disabled={isSigningIn}
/>
<div className="sn-account-menu-headline">Sign in</div>
<div className="font-bold text-base">Sign in</div>
</div>
<div className="px-3 mb-1">
<DecoratedInput
className={`mb-2 ${error ? 'border-danger' : null}`}
left={[<Icon type="email" className="color-neutral" />]}
left={[<Icon type="email" className="text-neutral" />]}
type="email"
placeholder="Email"
value={email}
@@ -166,7 +167,7 @@ const SignInPane: FunctionComponent<Props> = ({ application, viewControllerManag
<DecoratedPasswordInput
className={`mb-2 ${error ? 'border-danger' : null}`}
disabled={isSigningIn}
left={[<Icon type="password" className="color-neutral" />]}
left={[<Icon type="password" className="text-neutral" />]}
onChange={handlePasswordChange}
onFocus={resetInvalid}
onKeyDown={handleKeyDown}
@@ -174,13 +175,14 @@ const SignInPane: FunctionComponent<Props> = ({ application, viewControllerManag
ref={passwordInputRef}
value={password}
/>
{error ? <div className="color-danger my-2">{error}</div> : null}
{error ? <div className="text-danger my-2">{error}</div> : null}
<Button
className="btn-w-full mt-1 mb-3"
className="mt-1 mb-3"
label={isSigningIn ? 'Signing in...' : 'Sign in'}
variant="primary"
primary
onClick={handleSignInFormSubmit}
disabled={isSigningIn}
fullWidth={true}
/>
<Checkbox
name="is-ephemeral"
@@ -199,7 +201,7 @@ const SignInPane: FunctionComponent<Props> = ({ application, viewControllerManag
/>
) : null}
</div>
<div className="h-1px my-2 bg-border"></div>
<HorizontalSeparator classes="my-2" />
<AdvancedOptions
viewControllerManager={viewControllerManager}
application={application}

View File

@@ -58,7 +58,7 @@ const WorkspaceMenuItem: FunctionComponent<Props> = ({
return (
<MenuItem
type={MenuItemType.RadioButton}
className="sn-dropdown-item py-2 focus:bg-info-backdrop focus:shadow-none"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-2 text-left w-full focus:bg-info-backdrop focus:shadow-none text-sm"
onClick={onClick}
checked={descriptor.primary}
>
@@ -76,7 +76,7 @@ const WorkspaceMenuItem: FunctionComponent<Props> = ({
<div>{descriptor.label}</div>
)}
{descriptor.primary && !hideOptions && (
<div>
<div className="flex items-center">
<a
role="button"
className="w-5 h-5 p-0 mr-3 border-0 bg-transparent hover:bg-contrast cursor-pointer"
@@ -85,7 +85,7 @@ const WorkspaceMenuItem: FunctionComponent<Props> = ({
setIsRenaming((isRenaming) => !isRenaming)
}}
>
<Icon type="pencil" className="sn-icon--mid color-neutral" />
<Icon type="pencil" className="text-neutral" size="medium" />
</a>
<a
role="button"
@@ -95,7 +95,7 @@ const WorkspaceMenuItem: FunctionComponent<Props> = ({
onDelete()
}}
>
<Icon type="trash" className="sn-icon--mid color-danger" />
<Icon type="trash" className="text-danger" size="medium" />
</a>
</div>
)}

View File

@@ -78,13 +78,13 @@ const WorkspaceSwitcherMenu: FunctionComponent<Props> = ({
void mainApplicationGroup.unloadCurrentAndCreateNewDescriptor()
}}
>
<Icon type="user-add" className="color-neutral mr-2" />
<Icon type="user-add" className="text-neutral mr-2" />
Add another workspace
</MenuItem>
{!hideWorkspaceOptions && (
<MenuItem type={MenuItemType.IconButton} onClick={signoutAll}>
<Icon type="signOut" className="color-neutral mr-2" />
<Icon type="signOut" className="text-neutral mr-2" />
Sign out all workspaces
</MenuItem>
)}

View File

@@ -6,6 +6,8 @@ import { observer } from 'mobx-react-lite'
import { FunctionComponent, useCallback, useEffect, useRef, useState } from 'react'
import Icon from '@/Components/Icon/Icon'
import WorkspaceSwitcherMenu from './WorkspaceSwitcherMenu'
import MenuItem from '@/Components/Menu/MenuItem'
import { MenuItemType } from '@/Components/Menu/MenuItemType'
type Props = {
mainApplicationGroup: ApplicationGroup
@@ -43,21 +45,25 @@ const WorkspaceSwitcherOption: FunctionComponent<Props> = ({ mainApplicationGrou
return (
<>
<button
ref={buttonRef}
className="sn-dropdown-item justify-between focus:bg-info-backdrop focus:shadow-none"
<MenuItem
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
role="menuitem"
ref={buttonRef}
type={MenuItemType.IconButton}
onClick={toggleMenu}
className="justify-between"
>
<div className="flex items-center">
<Icon type="user-switch" className="color-neutral mr-2" />
<Icon type="user-switch" className="text-neutral mr-2" />
Switch workspace
</div>
<Icon type="chevron-right" className="color-neutral" />
</button>
<Icon type="chevron-right" className="text-neutral" />
</MenuItem>
{isOpen && (
<div ref={menuRef} className="sn-dropdown max-h-120 min-w-68 py-2 fixed overflow-y-auto" style={menuStyle}>
<div
ref={menuRef}
className="bg-default rounded shadow-main max-h-120 min-w-68 py-2 fixed overflow-y-auto"
style={menuStyle}
>
<WorkspaceSwitcherMenu
mainApplicationGroup={mainApplicationGroup}
viewControllerManager={viewControllerManager}

View File

@@ -84,7 +84,7 @@ class ApplicationGroupView extends Component<Props, State> {
<DialogContent
aria-label="Switching workspace"
className={
'challenge-modal flex flex-col items-center bg-default p-8 rounded relative shadow-overlay-light border-1 border-solid border-main'
'challenge-modal flex flex-col items-center bg-default p-8 rounded relative shadow-overlay-light border border-solid border-border'
}
>
{message}

View File

@@ -270,12 +270,14 @@ const AttachedFilesButton: FunctionComponent<Props> = ({
}
}}
ref={buttonRef}
className={`sn-icon-button border-contrast ${attachedFilesCount > 0 ? 'py-1 px-3' : ''}`}
className={`flex justify-center items-center min-w-8 h-8 bg-text-padding hover:bg-contrast focus:bg-contrast text-neutral border border-solid border-border rounded-full cursor-pointer ${
attachedFilesCount > 0 ? 'py-1 px-3' : ''
}`}
onBlur={closeOnBlur}
>
<VisuallyHidden>Attached files</VisuallyHidden>
<Icon type="attachment-file" className="block" />
{attachedFilesCount > 0 && <span className="ml-2">{attachedFilesCount}</span>}
{attachedFilesCount > 0 && <span className="text-sm ml-2">{attachedFilesCount}</span>}
</DisclosureButton>
<DisclosurePanel
onKeyDown={(event) => {
@@ -289,7 +291,7 @@ const AttachedFilesButton: FunctionComponent<Props> = ({
...position,
maxHeight,
}}
className="sn-dropdown sn-dropdown--animated min-w-80 max-h-120 max-w-xs flex flex-col overflow-y-auto fixed"
className="bg-default rounded shadow-main transition-transform duration-150 slide-down-animation min-w-80 max-h-120 max-w-xs flex flex-col overflow-y-auto fixed"
onBlur={closeOnBlur}
>
{open && (

View File

@@ -78,12 +78,12 @@ const AttachedFilesPopover: FunctionComponent<Props> = ({
border: isDraggingFiles ? '2px dashed var(--sn-stylekit-info-color)' : '',
}}
>
<div className="flex border-0 border-b-1 border-solid border-main">
<div className="flex border-b border-solid border-border">
<button
id={PopoverTabs.AttachedFiles}
className={`bg-default border-0 cursor-pointer px-3 py-2.5 relative focus:bg-info-backdrop focus:shadow-bottom ${
currentTab === PopoverTabs.AttachedFiles ? 'color-info font-medium shadow-bottom' : 'color-text'
} ${attachedTabDisabled ? 'color-neutral cursor-not-allowed' : ''}`}
className={`bg-default border-0 cursor-pointer px-3 py-2.5 relative focus:bg-info-backdrop focus:shadow-bottom text-sm ${
currentTab === PopoverTabs.AttachedFiles ? 'text-info font-medium shadow-bottom' : 'text-text'
} ${attachedTabDisabled ? 'text-neutral cursor-not-allowed' : ''}`}
onClick={() => {
setCurrentTab(PopoverTabs.AttachedFiles)
}}
@@ -94,8 +94,8 @@ const AttachedFilesPopover: FunctionComponent<Props> = ({
</button>
<button
id={PopoverTabs.AllFiles}
className={`bg-default border-0 cursor-pointer px-3 py-2.5 relative focus:bg-info-backdrop focus:shadow-bottom ${
currentTab === PopoverTabs.AllFiles ? 'color-info font-medium shadow-bottom' : 'color-text'
className={`bg-default border-0 cursor-pointer px-3 py-2.5 relative focus:bg-info-backdrop focus:shadow-bottom text-sm ${
currentTab === PopoverTabs.AllFiles ? 'text-info font-medium shadow-bottom' : 'text-text'
}`}
onClick={() => {
setCurrentTab(PopoverTabs.AllFiles)
@@ -107,11 +107,11 @@ const AttachedFilesPopover: FunctionComponent<Props> = ({
</div>
<div className="min-h-0 max-h-110 overflow-y-auto">
{filteredList.length > 0 || searchQuery.length > 0 ? (
<div className="sticky top-0 left-0 p-3 bg-default border-0 border-b-1 border-solid border-main">
<div className="sticky top-0 left-0 p-3 bg-default border-b border-solid border-border">
<div className="relative">
<input
type="text"
className="color-text w-full rounded py-1.5 px-3 text-input bg-default border-solid border-1 border-main"
className="text-text w-full rounded py-1.5 px-3 text-sm bg-default border-solid border border-border"
placeholder="Search files..."
value={searchQuery}
onInput={(e) => {
@@ -129,7 +129,7 @@ const AttachedFilesPopover: FunctionComponent<Props> = ({
}}
onBlur={closeOnBlur}
>
<Icon type="clear-circle-filled" className="color-neutral" />
<Icon type="clear-circle-filled" className="text-neutral" />
</button>
)}
</div>
@@ -161,20 +161,20 @@ const AttachedFilesPopover: FunctionComponent<Props> = ({
? 'No files attached to this note'
: 'No files found in this account'}
</div>
<Button variant="normal" onClick={handleAttachFilesClick} onBlur={closeOnBlur}>
<Button onClick={handleAttachFilesClick} onBlur={closeOnBlur}>
{currentTab === PopoverTabs.AttachedFiles ? 'Attach' : 'Upload'} files
</Button>
<div className="text-xs color-passive-0 mt-3">Or drop your files here</div>
<div className="text-xs text-passive-0 mt-3">Or drop your files here</div>
</div>
)}
</div>
{filteredList.length > 0 && (
<button
className="sn-dropdown-item py-3 border-0 border-t-1px border-solid border-main focus:bg-info-backdrop"
className="flex items-center cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-3 text-left w-full focus:bg-info-backdrop focus:shadow-none text-sm border-0 border-t border-solid border-border"
onClick={handleAttachFilesClick}
onBlur={closeOnBlur}
>
<Icon type="add" className="mr-2 color-neutral" />
<Icon type="add" className="mr-2 text-neutral" />
{currentTab === PopoverTabs.AttachedFiles ? 'Attach' : 'Upload'} files
</button>
)}

View File

@@ -91,7 +91,7 @@ const PopoverFileItem: FunctionComponent<PopoverFileItemProps> = ({
{isRenamingFile ? (
<input
type="text"
className="text-input px-1.5 py-1 mb-1 border-1 border-solid border-main bg-transparent color-foreground"
className="text-input px-1.5 py-1 mb-1 border border-solid border-border bg-transparent text-foreground"
value={fileName}
ref={fileNameInputRef}
onInput={handleFileNameInput}
@@ -100,13 +100,13 @@ const PopoverFileItem: FunctionComponent<PopoverFileItemProps> = ({
/>
) : (
<div className="text-sm mb-1 break-word">
<span className="vertical-middle">{file.name}</span>
<span className="align-middle">{file.name}</span>
{file.protected && (
<Icon type="lock-filled" className="sn-icon--small ml-2 color-neutral vertical-middle" />
<Icon type="lock-filled" className="ml-2 text-neutral inline align-middle" size="small" />
)}
</div>
)}
<div className="text-xs color-passive-0">
<div className="text-xs text-passive-0">
{file.created_at.toLocaleString()} · {formatSizeToReadableString(file.decryptedSize)}
</div>
</div>

View File

@@ -7,6 +7,7 @@ import Switch from '@/Components/Switch/Switch'
import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur'
import { PopoverFileSubmenuProps } from './PopoverFileItemProps'
import { PopoverFileItemActionType } from './PopoverFileItemAction'
import HorizontalSeparator from '../Shared/HorizontalSeparator'
const PopoverFileSubmenu: FunctionComponent<PopoverFileSubmenuProps> = ({
file,
@@ -67,7 +68,7 @@ const PopoverFileSubmenu: FunctionComponent<PopoverFileSubmenuProps> = ({
onBlur={closeOnBlur}
className="w-7 h-7 p-1 rounded-full border-0 bg-transparent hover:bg-contrast cursor-pointer"
>
<Icon type="more" className="color-neutral" />
<Icon type="more" className="text-neutral" />
</DisclosureButton>
<DisclosurePanel
ref={menuRef}
@@ -75,25 +76,27 @@ const PopoverFileSubmenu: FunctionComponent<PopoverFileSubmenuProps> = ({
...menuStyle,
position: 'fixed',
}}
className="sn-dropdown flex flex-col max-h-120 min-w-60 py-1 fixed overflow-y-auto"
className={`${
isMenuOpen ? 'flex' : 'hidden'
} flex-col bg-default rounded shadow-main max-h-120 min-w-60 py-1 fixed overflow-y-auto`}
>
{isMenuOpen && (
<>
<button
onBlur={closeOnBlur}
className="sn-dropdown-item focus:bg-info-backdrop"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:shadow-none text-sm focus:bg-info-backdrop"
onClick={() => {
previewHandler(file)
closeMenu()
}}
>
<Icon type="file" className="mr-2 color-neutral" />
<Icon type="file" className="mr-2 text-neutral" />
Preview file
</button>
{isAttachedToNote ? (
<button
onBlur={closeOnBlur}
className="sn-dropdown-item focus:bg-info-backdrop"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:shadow-none text-sm focus:bg-info-backdrop"
onClick={() => {
handleFileAction({
type: PopoverFileItemActionType.DetachFileToNote,
@@ -102,13 +105,13 @@ const PopoverFileSubmenu: FunctionComponent<PopoverFileSubmenuProps> = ({
closeMenu()
}}
>
<Icon type="link-off" className="mr-2 color-neutral" />
<Icon type="link-off" className="mr-2 text-neutral" />
Detach from note
</button>
) : (
<button
onBlur={closeOnBlur}
className="sn-dropdown-item focus:bg-info-backdrop"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:shadow-none text-sm focus:bg-info-backdrop"
onClick={() => {
handleFileAction({
type: PopoverFileItemActionType.AttachFileToNote,
@@ -117,13 +120,13 @@ const PopoverFileSubmenu: FunctionComponent<PopoverFileSubmenuProps> = ({
closeMenu()
}}
>
<Icon type="link" className="mr-2 color-neutral" />
<Icon type="link" className="mr-2 text-neutral" />
Attach to note
</button>
)}
<div className="min-h-1px my-1 bg-border"></div>
<HorizontalSeparator classes="my-1" />
<button
className="sn-dropdown-item justify-between focus:bg-info-backdrop"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:shadow-none text-sm justify-between focus:bg-info-backdrop"
onClick={() => {
handleFileAction({
type: PopoverFileItemActionType.ToggleFileProtection,
@@ -136,7 +139,7 @@ const PopoverFileSubmenu: FunctionComponent<PopoverFileSubmenuProps> = ({
onBlur={closeOnBlur}
>
<span className="flex items-center">
<Icon type="password" className="mr-2 color-neutral" />
<Icon type="password" className="mr-2 text-neutral" />
Password protection
</span>
<Switch
@@ -145,10 +148,10 @@ const PopoverFileSubmenu: FunctionComponent<PopoverFileSubmenuProps> = ({
checked={isFileProtected}
/>
</button>
<div className="min-h-1px my-1 bg-border"></div>
<HorizontalSeparator classes="my-1" />
<button
onBlur={closeOnBlur}
className="sn-dropdown-item focus:bg-info-backdrop"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:shadow-none text-sm focus:bg-info-backdrop"
onClick={() => {
handleFileAction({
type: PopoverFileItemActionType.DownloadFile,
@@ -157,22 +160,22 @@ const PopoverFileSubmenu: FunctionComponent<PopoverFileSubmenuProps> = ({
closeMenu()
}}
>
<Icon type="download" className="mr-2 color-neutral" />
<Icon type="download" className="mr-2 text-neutral" />
Download
</button>
<button
onBlur={closeOnBlur}
className="sn-dropdown-item focus:bg-info-backdrop"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:shadow-none text-sm focus:bg-info-backdrop"
onClick={() => {
setIsRenamingFile(true)
}}
>
<Icon type="pencil" className="mr-2 color-neutral" />
<Icon type="pencil" className="mr-2 text-neutral" />
Rename
</button>
<button
onBlur={closeOnBlur}
className="sn-dropdown-item focus:bg-info-backdrop"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:shadow-none text-sm focus:bg-info-backdrop"
onClick={() => {
handleFileAction({
type: PopoverFileItemActionType.DeleteFile,
@@ -181,9 +184,14 @@ const PopoverFileSubmenu: FunctionComponent<PopoverFileSubmenuProps> = ({
closeMenu()
}}
>
<Icon type="trash" className="mr-2 color-danger" />
<span className="color-danger">Delete permanently</span>
<Icon type="trash" className="mr-2 text-danger" />
<span className="text-danger">Delete permanently</span>
</button>
<div className="px-3 py-1 text-xs text-neutral font-medium">
<div>
<span className="font-semibold">File ID:</span> {file.uuid}
</div>
</div>
</>
)}
</DisclosurePanel>

View File

@@ -7,9 +7,9 @@ type Props = {
}
const styles = {
base: 'px-2 py-1.5 text-center rounded-full cursor-pointer transition border-1 border-solid active:border-info active:bg-info active:color-neutral-contrast',
unselected: 'color-neutral border-secondary',
selected: 'border-info bg-info color-neutral-contrast',
base: 'px-2 py-1 text-center rounded-full cursor-pointer transition border border-solid active:border-info active:bg-info active:text-neutral-contrast',
unselected: 'text-neutral border-secondary-border',
selected: 'border-info bg-info text-neutral-contrast',
}
const Bubble: FunctionComponent<Props> = ({ label, selected, onSelect }) => (

View File

@@ -1,52 +1,99 @@
import { Ref, forwardRef, ReactNode, ComponentPropsWithoutRef } from 'react'
const baseClass = 'rounded px-4 py-1.75 font-bold text-sm fit-content'
type ButtonStyle = 'default' | 'contrast' | 'neutral' | 'info' | 'warning' | 'danger' | 'success'
type ButtonVariant = 'normal' | 'primary'
const getClassName = (variant: ButtonVariant, danger: boolean, disabled: boolean) => {
const borders = variant === 'normal' ? 'border-solid border-main border-1' : 'no-border'
const cursor = disabled ? 'cursor-not-allowed' : 'cursor-pointer'
let colors = variant === 'normal' ? 'bg-default color-text' : 'bg-info color-info-contrast'
let focusHoverStates =
variant === 'normal'
? 'focus:bg-contrast focus:outline-none hover:bg-contrast'
: 'hover:brightness-130 focus:outline-none focus:brightness-130'
if (danger) {
colors = variant === 'normal' ? 'bg-default color-danger' : 'bg-danger color-info-contrast'
const getColorsForNormalVariant = (style: ButtonStyle) => {
switch (style) {
case 'default':
return 'bg-default text-text'
case 'contrast':
return 'bg-default text-contrast'
case 'neutral':
return 'bg-default text-neutral'
case 'info':
return 'bg-default text-info'
case 'warning':
return 'bg-default text-warning'
case 'danger':
return 'bg-default text-danger'
case 'success':
return 'bg-default text-success'
}
}
const getColorsForPrimaryVariant = (style: ButtonStyle) => {
switch (style) {
case 'default':
return 'bg-default text-foreground'
case 'contrast':
return 'bg-contrast text-text'
case 'neutral':
return 'bg-neutral text-neutral-contrast'
case 'info':
return 'bg-info text-info-contrast'
case 'warning':
return 'bg-warning text-warning-contrast'
case 'danger':
return 'bg-danger text-danger-contrast'
case 'success':
return 'bg-success text-success-contrast'
}
}
const getClassName = (
primary: boolean,
style: ButtonStyle,
disabled: boolean,
fullWidth?: boolean,
small?: boolean,
isRounded?: boolean,
) => {
const borders = primary ? 'no-border' : 'border-solid border-border border'
const cursor = disabled ? 'cursor-not-allowed' : 'cursor-pointer'
const width = fullWidth ? 'w-full' : 'w-fit'
const padding = small ? 'px-3 py-1.5' : 'px-4 py-1.5'
const textSize = small ? 'text-xs' : 'text-sm'
const rounded = isRounded ? 'rounded' : ''
let colors = primary ? getColorsForPrimaryVariant(style) : getColorsForNormalVariant(style)
let focusHoverStates = primary
? 'hover:brightness-125 focus:outline-none focus:brightness-125'
: 'focus:bg-contrast focus:outline-none hover:bg-contrast'
if (disabled) {
colors = variant === 'normal' ? 'bg-default color-passive-2' : 'bg-passive-2 color-info-contrast'
focusHoverStates =
variant === 'normal'
? 'focus:bg-default focus:outline-none hover:bg-default'
: 'focus:brightness-default focus:outline-none hover:brightness-default'
colors = primary ? 'bg-passive-2 text-info-contrast' : 'bg-default text-passive-2'
focusHoverStates = primary
? 'focus:brightness-100 focus:outline-none hover:brightness-100'
: 'focus:bg-default focus:outline-none hover:bg-default'
}
return `${baseClass} ${colors} ${borders} ${focusHoverStates} ${cursor}`
return `${rounded} font-bold ${width} ${padding} ${textSize} ${colors} ${borders} ${focusHoverStates} ${cursor}`
}
interface ButtonProps extends ComponentPropsWithoutRef<'button'> {
children?: ReactNode
className?: string
variant?: ButtonVariant
dangerStyle?: boolean
primary?: boolean
colorStyle?: ButtonStyle
label?: string
fullWidth?: boolean
small?: boolean
rounded?: boolean
}
const Button = forwardRef(
(
{
variant = 'normal',
primary = false,
label,
className = '',
dangerStyle: danger = false,
colorStyle = primary ? 'info' : 'default',
disabled = false,
children,
fullWidth,
small,
rounded = true,
...props
}: ButtonProps,
ref: Ref<HTMLButtonElement>,
@@ -54,7 +101,7 @@ const Button = forwardRef(
return (
<button
type="button"
className={`${getClassName(variant, danger, disabled)} ${className}`}
className={`${getClassName(primary, colorStyle, disabled, fullWidth, small, rounded)} ${className}`}
disabled={disabled}
ref={ref}
{...props}

View File

@@ -18,7 +18,12 @@ const RoundIconButton: FunctionComponent<Props> = ({ onClick, type, className, i
}
const classes = type === 'primary' ? 'info ' : ''
return (
<button className={`sn-icon-button ${classes} ${className ?? ''}`} onClick={click}>
<button
className={`text-neutral min-w-8 h-8 flex justify-center items-center border-solid border border-border bg-clip-padding m-0 bg-transparent cursor-pointer rounded-full hover:text-text focus:text-text hover:bg-contrast focus:bg-contrast focus:outline-none focus:ring-info ${classes} ${
className ?? ''
}`}
onClick={click}
>
<Icon type={iconType} />
</button>
)

View File

@@ -186,7 +186,7 @@ const ChallengeModal: FunctionComponent<Props> = ({
aria-label="Challenge modal"
className={`challenge-modal flex flex-col items-center bg-default p-8 rounded relative ${
challenge.reason !== ChallengeReason.ApplicationUnlock
? 'shadow-overlay-light border-1 border-solid border-main'
? 'shadow-overlay-light border border-solid border-border'
: 'focus:shadow-none'
}`}
>
@@ -196,7 +196,7 @@ const ChallengeModal: FunctionComponent<Props> = ({
aria-label="Close modal"
className="flex p-1 bg-transparent border-0 cursor-pointer absolute top-4 right-4"
>
<Icon type="close" className="color-neutral" />
<Icon type="close" className="text-neutral" />
</button>
)}
<ProtectedIllustration className="w-30 h-30 mb-4" />
@@ -224,7 +224,7 @@ const ChallengeModal: FunctionComponent<Props> = ({
/>
))}
</form>
<Button variant="primary" disabled={isProcessing} className="min-w-76 mt-1 mb-3.5" onClick={submit}>
<Button primary disabled={isProcessing} className="min-w-76 mt-1 mb-3.5" onClick={submit}>
{isProcessing ? 'Generating Keys...' : 'Submit'}
</Button>
{shouldShowForgotPasscode && (
@@ -250,7 +250,7 @@ const ChallengeModal: FunctionComponent<Props> = ({
})
}}
>
<Icon type="help" className="mr-2 color-neutral" />
<Icon type="help" className="mr-2 text-neutral" />
Forgot passcode?
</Button>
)}

View File

@@ -38,8 +38,8 @@ const ChallengeModalPrompt: FunctionComponent<Props> = ({ prompt, values, index,
return (
<label
key={option.label}
className={`cursor-pointer px-2 py-1.5 rounded ${
selected ? 'bg-default color-foreground font-semibold' : 'color-passive-0 hover:bg-passive-3'
className={`cursor-pointer px-2 py-1.5 rounded focus-within:ring-2 focus-within:ring-info ${
selected ? 'bg-default text-foreground font-semibold' : 'text-passive-0 hover:bg-passive-3'
}`}
>
<input
@@ -76,7 +76,7 @@ const ChallengeModalPrompt: FunctionComponent<Props> = ({ prompt, values, index,
onChange={(value) => onValueChange(value, prompt)}
/>
)}
{isInvalid && <div className="text-sm color-danger mt-2">Invalid authentication, please try again.</div>}
{isInvalid && <div className="text-sm text-danger mt-2">Invalid authentication, please try again.</div>}
</div>
)
}

View File

@@ -48,11 +48,15 @@ const LockscreenWorkspaceSwitcher: FunctionComponent<Props> = ({ mainApplication
return (
<div ref={containerRef}>
<Button ref={buttonRef} onClick={toggleMenu} className="flex items-center justify-center min-w-76 mt-2">
<Icon type="user-switch" className="color-neutral mr-2" />
<Icon type="user-switch" className="text-neutral mr-2" />
Switch workspace
</Button>
{isOpen && (
<div ref={menuRef} className="sn-dropdown max-h-120 min-w-68 py-2 fixed overflow-y-auto" style={menuStyle}>
<div
ref={menuRef}
className="bg-default rounded-md shadow-main max-h-120 min-w-68 py-2 fixed overflow-y-auto"
style={menuStyle}
>
<WorkspaceSwitcherMenu
mainApplicationGroup={mainApplicationGroup}
viewControllerManager={viewControllerManager}

View File

@@ -72,7 +72,7 @@ const ChangeEditorButton: FunctionComponent<Props> = ({
}}
onBlur={closeOnBlur}
ref={buttonRef}
className="sn-icon-button border-contrast"
className="flex justify-center items-center min-w-8 h-8 hover:bg-contrast focus:bg-contrast text-neutral border border-solid border-border rounded-full cursor-pointer"
>
<VisuallyHidden>Change note type</VisuallyHidden>
<Icon type="dashboard" className="block" />
@@ -89,7 +89,7 @@ const ChangeEditorButton: FunctionComponent<Props> = ({
...position,
maxHeight,
}}
className="sn-dropdown sn-dropdown--animated min-w-68 max-h-120 max-w-xs flex flex-col overflow-y-auto fixed"
className="bg-default rounded shadow-main transition-transform duration-150 slide-down-animation min-w-68 max-h-120 max-w-xs flex flex-col overflow-y-auto fixed"
onBlur={closeOnBlur}
>
{isOpen && (

View File

@@ -183,7 +183,7 @@ const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({
return (
<Fragment key={groupId}>
<div className={`py-1 border-0 border-t-1px border-solid border-main ${index === 0 ? 'border-t-0' : ''}`}>
<div className={`py-1 border-0 border-t border-solid border-border ${index === 0 ? 'border-t-0' : ''}`}>
{group.items.map((item) => {
const onClickEditorItem = () => {
selectEditor(item).catch(console.error)
@@ -193,11 +193,9 @@ const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({
key={item.name}
type={MenuItemType.RadioButton}
onClick={onClickEditorItem}
className={
'sn-dropdown-item py-2 text-input focus:bg-info-backdrop focus:shadow-none flex-row-reverse'
}
className={'py-2 flex-row-reverse'}
onBlur={closeOnBlur}
checked={isSelectedEditor(item)}
checked={item.isEntitled ? isSelectedEditor(item) : undefined}
>
<div className="flex flex-grow items-center justify-between">
<div className="flex items-center">

View File

@@ -75,49 +75,49 @@ export const createEditorMenuGroups = (application: WebApplication, editors: SNC
const editorMenuGroups: EditorMenuGroup[] = [
{
icon: 'plain-text',
iconClassName: 'color-accessory-tint-1',
iconClassName: 'text-accessory-tint-1',
title: 'Plain text',
items: editorItems.plain,
},
{
icon: 'rich-text',
iconClassName: 'color-accessory-tint-1',
iconClassName: 'text-accessory-tint-1',
title: 'Rich text',
items: editorItems['rich-text'],
},
{
icon: 'markdown',
iconClassName: 'color-accessory-tint-2',
iconClassName: 'text-accessory-tint-2',
title: 'Markdown text',
items: editorItems.markdown,
},
{
icon: 'tasks',
iconClassName: 'color-accessory-tint-3',
iconClassName: 'text-accessory-tint-3',
title: 'Todo',
items: editorItems.task,
},
{
icon: 'code',
iconClassName: 'color-accessory-tint-4',
iconClassName: 'text-accessory-tint-4',
title: 'Code',
items: editorItems.code,
},
{
icon: 'spreadsheets',
iconClassName: 'color-accessory-tint-5',
iconClassName: 'text-accessory-tint-5',
title: 'Spreadsheet',
items: editorItems.spreadsheet,
},
{
icon: 'authenticator',
iconClassName: 'color-accessory-tint-6',
iconClassName: 'text-accessory-tint-6',
title: 'Authentication',
items: editorItems.authentication,
},
{
icon: 'editor',
iconClassName: 'color-neutral',
iconClassName: 'text-neutral',
title: 'Others',
items: editorItems.others,
},

View File

@@ -10,7 +10,7 @@ type CheckboxProps = {
const Checkbox: FunctionComponent<CheckboxProps> = ({ name, checked, onChange, disabled, label }) => {
return (
<label htmlFor={name} className="flex items-center fit-content mb-2">
<label htmlFor={name} className="flex items-center fit-content mb-2 text-sm">
<input
className="mr-2"
type="checkbox"

View File

@@ -1,4 +1,5 @@
import { FunctionComponent } from 'react'
import Button from '@/Components/Button/Button'
type Props = {
deprecationMessage: string | undefined
@@ -8,16 +9,18 @@ type Props = {
const IsDeprecated: FunctionComponent<Props> = ({ deprecationMessage, dismissDeprecationMessage }) => {
return (
<div className={'sn-component'}>
<div className={'sk-app-bar no-edges no-top-edge dynamic-height'}>
<div className="flex justify-between items-center w-full min-h-[1.625rem] py-2.5 px-2 bg-contrast text-text border-b border-border select-none">
<div className={'left'}>
<div className={'sk-app-bar-item'}>
<div className={'sk-label warning'}>{deprecationMessage || 'This extension is deprecated.'}</div>
<div className="font-bold text-xs text-warning">
{deprecationMessage || 'This extension is deprecated.'}
</div>
</div>
</div>
<div className={'right'}>
<div className={'sk-app-bar-item'} onClick={dismissDeprecationMessage}>
<button className={'sn-button small info'}>Dismiss</button>
</div>
<Button primary onClick={dismissDeprecationMessage} small>
Dismiss
</Button>
</div>
</div>
</div>

View File

@@ -1,5 +1,7 @@
import { FeatureStatus } from '@standardnotes/snjs'
import { FunctionComponent } from 'react'
import Button from '@/Components/Button/Button'
import IndicatorCircle from '../IndicatorCircle/IndicatorCircle'
type Props = {
expiredDate: string
@@ -24,24 +26,20 @@ const statusString = (featureStatus: FeatureStatus, expiredDate: string, compone
const IsExpired: FunctionComponent<Props> = ({ expiredDate, featureStatus, componentName, manageSubscription }) => {
return (
<div className={'sn-component'}>
<div className={'sk-app-bar no-edges no-top-edge dynamic-height'}>
<div className="flex justify-between items-center w-full min-h-[1.625rem] py-2.5 px-2 bg-contrast text-text border-b border-border select-none">
<div className={'left'}>
<div className={'sk-app-bar-item'}>
<div className={'sk-app-bar-item-column'}>
<div className={'sk-circle danger small'} />
</div>
<div className={'sk-app-bar-item-column'}>
<div>
<strong>{statusString(featureStatus, expiredDate, componentName)}</strong>
<div className={'sk-p'}>{componentName} is in a read-only state.</div>
</div>
<div className="flex items-center">
<IndicatorCircle style="danger" />
<div className="ml-2">
<strong>{statusString(featureStatus, expiredDate, componentName)}</strong>
<div className={'sk-p'}>{componentName} is in a read-only state.</div>
</div>
</div>
</div>
<div className={'right'}>
<div className={'sk-app-bar-item'} onClick={() => manageSubscription()}>
<button className={'sn-button small success'}>Manage Subscription</button>
</div>
<Button onClick={manageSubscription} primary colorStyle="success" small>
Manage subscription
</Button>
</div>
</div>
</div>

View File

@@ -1,4 +1,5 @@
import { FunctionComponent } from 'react'
import Button from '@/Components/Button/Button'
type Props = {
componentName: string
@@ -8,16 +9,16 @@ type Props = {
const IssueOnLoading: FunctionComponent<Props> = ({ componentName, reloadIframe }) => {
return (
<div className={'sn-component'}>
<div className={'sk-app-bar no-edges no-top-edge dynamic-height'}>
<div className="flex justify-between items-center w-full min-h-[1.625rem] py-2.5 px-2 bg-contrast text-text border-b border-border select-none">
<div className={'left'}>
<div className={'sk-app-bar-item'}>
<div className={'sk-label.warning'}>There was an issue loading {componentName}.</div>
</div>
</div>
<div className={'right'}>
<div className={'sk-app-bar-item'} onClick={reloadIframe}>
<button className={'sn-button small info'}>Reload</button>
</div>
<Button primary onClick={reloadIframe} small>
Reload
</Button>
</div>
</div>
</div>

View File

@@ -7,14 +7,14 @@ const OfflineRestricted: FunctionComponent = () => {
<div className={'sk-panel-content'}>
<div className={'sk-panel-section stretch'}>
<div className={'sk-panel-column'} />
<div className={'sk-h1 sk-bold'}>You have restricted this component to not use a hosted version.</div>
<div className="font-bold text-base">You have restricted this component to not use a hosted version.</div>
<div className={'sk-subtitle'}>Locally-installed components are not available in the web application.</div>
<div className={'sk-panel-row'} />
<div className={'sk-panel-row'}>
<div className={'sk-panel-column'}>
<div className={'sk-p'}>To continue, choose from the following options:</div>
<ul>
<li className={'sk-p'}>
<ul className="list-disc pl-8 mt-3">
<li className="sk-p mb-1">
Enable the Hosted option for this component by opening the Preferences {'>'} General {'>'} Advanced
Settings menu and toggling 'Use hosted when local is unavailable' under this component's options.
Then press Reload.

View File

@@ -6,6 +6,7 @@ import { ViewControllerManager } from '@/Services/ViewControllerManager'
import { observer } from 'mobx-react-lite'
import { ApplicationGroup } from '@/Application/ApplicationGroup'
import { isDesktopApplication } from '@/Utils'
import Button from '@/Components/Button/Button'
type Props = {
application: WebApplication
@@ -31,7 +32,7 @@ const ConfirmSignoutModal: FunctionComponent<Props> = ({ application, viewContro
const showWorkspaceWarning = workspaces.length > 1 && isDesktopApplication()
return (
<AlertDialog onDismiss={closeDialog} leastDestructiveRef={cancelRef}>
<AlertDialog onDismiss={closeDialog} leastDestructiveRef={cancelRef} className="p-0 max-w-[600px]">
<div className="sk-modal-content">
<div className="sn-component">
<div className="sk-panel">
@@ -40,11 +41,11 @@ const ConfirmSignoutModal: FunctionComponent<Props> = ({ application, viewContro
<AlertDialogLabel className="sk-h3 sk-panel-section-title">Sign out workspace?</AlertDialogLabel>
<AlertDialogDescription className="sk-panel-row">
<div>
<p className="color-foreground">{STRING_SIGN_OUT_CONFIRMATION}</p>
<p className="text-foreground">{STRING_SIGN_OUT_CONFIRMATION}</p>
{showWorkspaceWarning && (
<>
<br />
<p className="color-foreground">
<p className="text-foreground">
<strong>Note: </strong>
Because you have other workspaces signed in, this sign out may leave logs and other metadata
of your session on this device. For a more robust sign out that performs a hard clear of all
@@ -82,12 +83,15 @@ const ConfirmSignoutModal: FunctionComponent<Props> = ({ application, viewContro
</div>
)}
<div className="flex my-1 mt-4">
<button className="sn-button small neutral" ref={cancelRef} onClick={closeDialog}>
<div className="flex my-1 mt-4 gap-2">
<Button primary small colorStyle="neutral" rounded={false} ref={cancelRef} onClick={closeDialog}>
Cancel
</button>
<button
className="sn-button small danger ml-2"
</Button>
<Button
primary
small
colorStyle="danger"
rounded={false}
onClick={() => {
if (deleteLocalBackups) {
application.signOutAndDeleteLocalBackups().catch(console.error)
@@ -98,7 +102,7 @@ const ConfirmSignoutModal: FunctionComponent<Props> = ({ application, viewContro
}}
>
{application.hasAccount() ? 'Sign Out' : 'Delete Workspace'}
</button>
</Button>
</div>
</div>
</div>

View File

@@ -66,7 +66,7 @@ const ContentList: FunctionComponent<Props> = ({
return (
<div
className="infinite-scroll border-solid border-0 border-t-1px border-main focus:shadow-none focus:outline-none"
className="infinite-scroll border-solid border-t border-border focus:shadow-none focus:outline-none"
id={ElementIds.ContentList}
onScroll={onScroll}
onKeyDown={onKeyDown}

View File

@@ -255,8 +255,8 @@ const ContentListView: FunctionComponent<Props> = ({
/>
</div>
</div>
{completedFullSync && !renderedItems.length ? <p className="empty-items-list faded">No items.</p> : null}
{!completedFullSync && !renderedItems.length ? <p className="empty-items-list faded">Loading...</p> : null}
{completedFullSync && !renderedItems.length ? <p className="empty-items-list opacity-50">No items.</p> : null}
{!completedFullSync && !renderedItems.length ? <p className="empty-items-list opacity-50">Loading...</p> : null}
{renderedItems.length ? (
<ContentList
items={renderedItems}

View File

@@ -53,8 +53,8 @@ const FileListItem: FunctionComponent<DisplayableListItemProps> = ({
return (
<div
className={`content-list-item flex items-stretch w-full cursor-pointer ${
selected && 'selected border-0 border-l-2px border-solid border-info'
className={`content-list-item flex items-stretch w-full cursor-pointer text-text ${
selected && 'selected border-l-2px border-solid border-info'
}`}
id={item.uuid}
onClick={onClick}
@@ -70,8 +70,8 @@ const FileListItem: FunctionComponent<DisplayableListItemProps> = ({
) : (
<div className="pr-4" />
)}
<div className="flex-grow min-w-0 py-4 px-0 border-0 border-b-1 border-solid border-main">
<div className="flex items-start justify-between font-semibold text-base leading-1.3 overflow-hidden">
<div className="flex-grow min-w-0 py-4 px-0 border-b border-solid border-border">
<div className="flex items-start justify-between font-semibold text-base leading-[1.3] overflow-hidden">
<div className="break-word mr-2">{item.title}</div>
</div>
<ListItemMetadata item={item} hideDate={hideDate} sortBy={sortBy} />

View File

@@ -45,16 +45,16 @@ const ContentListHeader = ({
}, [])
return (
<div className="section-title-bar-header">
<div className="flex flex-col">
<div className="text-lg font-semibold title">{panelTitle}</div>
{optionsSubtitle && <div className="text-xs color-passive-0">{optionsSubtitle}</div>}
<div className="section-title-bar-header gap-1">
<div className="flex flex-col flex-grow">
<div className="text-lg font-semibold text-text">{panelTitle}</div>
{optionsSubtitle && <div className="text-xs text-passive-0">{optionsSubtitle}</div>}
</div>
<div className="flex">
<div className="relative" ref={displayOptionsContainerRef}>
<Disclosure open={showDisplayOptionsMenu} onChange={toggleDisplayOptionsMenu}>
<StyledDisplayOptionsButton pressed={showDisplayOptionsMenu} ref={displayOptionsButtonRef}>
<Icon type="sort-descending" className="w-5 h-5" />
<StyledDisplayOptionsButton $pressed={showDisplayOptionsMenu} ref={displayOptionsButtonRef}>
<Icon type="sort-descending" />
</StyledDisplayOptionsButton>
<DisclosurePanel>
{showDisplayOptionsMenu && displayOptionsMenuPosition && (
@@ -72,12 +72,12 @@ const ContentListHeader = ({
</Disclosure>
</div>
<button
className="flex justify-center items-center min-w-8 h-8 ml-3 bg-info hover:brightness-130 color-info-contrast border-1 border-solid border-transparent rounded-full cursor-pointer"
className="flex justify-center items-center min-w-8 h-8 ml-3 bg-info hover:brightness-125 text-info-contrast border border-solid border-transparent rounded-full cursor-pointer"
title={addButtonLabel}
aria-label={addButtonLabel}
onClick={addNewItem}
>
<Icon type="add" className="w-5 h-5" />
<Icon type="add" />
</button>
</div>
</div>

View File

@@ -99,15 +99,15 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
return (
<Menu
className={
'py-1 sn-dropdown sn-dropdown--animated min-w-70 overflow-y-auto \
border-1 border-solid border-main text-sm z-index-dropdown-menu \
'py-1 bg-default rounded shadow-main transition-transform duration-150 slide-down-animation min-w-70 overflow-y-auto \
border border-solid border-border text-sm z-index-dropdown-menu \
flex flex-col'
}
a11yLabel="Notes list options menu"
closeMenu={closeDisplayOptionsMenu}
isOpen={isOpen}
>
<div className="px-3 my-1 text-xs font-semibold color-text uppercase">Sort by</div>
<div className="px-3 my-1 text-xs font-semibold text-text uppercase">Sort by</div>
<MenuItem
className="py-2"
type={MenuItemType.RadioButton}
@@ -118,9 +118,9 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
<span>Date modified</span>
{sortBy === CollectionSort.UpdatedAt ? (
sortReverse ? (
<Icon type="arrows-sort-up" className="color-neutral" />
<Icon type="arrows-sort-up" className="text-neutral" />
) : (
<Icon type="arrows-sort-down" className="color-neutral" />
<Icon type="arrows-sort-down" className="text-neutral" />
)
) : null}
</div>
@@ -135,9 +135,9 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
<span>Creation date</span>
{sortBy === CollectionSort.CreatedAt ? (
sortReverse ? (
<Icon type="arrows-sort-up" className="color-neutral" />
<Icon type="arrows-sort-up" className="text-neutral" />
) : (
<Icon type="arrows-sort-down" className="color-neutral" />
<Icon type="arrows-sort-down" className="text-neutral" />
)
) : null}
</div>
@@ -152,15 +152,15 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
<span>Title</span>
{sortBy === CollectionSort.Title ? (
sortReverse ? (
<Icon type="arrows-sort-up" className="color-neutral" />
<Icon type="arrows-sort-up" className="text-neutral" />
) : (
<Icon type="arrows-sort-down" className="color-neutral" />
<Icon type="arrows-sort-down" className="text-neutral" />
)
) : null}
</div>
</MenuItem>
<MenuItemSeparator />
<div className="px-3 py-1 text-xs font-semibold color-text uppercase">View</div>
<div className="px-3 py-1 text-xs font-semibold text-text uppercase">View</div>
{!isFilesSmartView && (
<MenuItem
type={MenuItemType.SwitchButton}
@@ -195,8 +195,8 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
>
Show icon
</MenuItem>
<div className="h-1px my-2 bg-border"></div>
<div className="px-3 py-1 text-xs font-semibold color-text uppercase">Other</div>
<MenuItemSeparator />
<div className="px-3 py-1 text-xs font-semibold text-text uppercase">Other</div>
<MenuItem
type={MenuItemType.SwitchButton}
className="py-1 hover:bg-contrast focus:bg-info-backdrop"

View File

@@ -3,11 +3,11 @@ import styled from 'styled-components'
const StyledDisplayOptionsButton = styled(DisclosureButton).attrs(() => ({
className:
'flex justify-center items-center min-w-8 h-8 bg-color-padding hover:bg-contrast focus:bg-contrast color-neutral border-1 border-solid border-main rounded-full cursor-pointer',
'flex justify-center items-center min-w-8 h-8 bg-text-padding hover:bg-contrast focus:bg-contrast text-neutral border border-solid border-border rounded-full cursor-pointer',
}))<{
pressed: boolean
$pressed: boolean
}>`
background-color: ${(props) => (props.pressed ? 'var(--sn-stylekit-contrast-background-color)' : 'transparent')};
background-color: ${(props) => (props.$pressed ? 'var(--sn-stylekit-contrast-background-color)' : 'transparent')};
`
export default StyledDisplayOptionsButton

View File

@@ -10,7 +10,7 @@ type Props = {
const ListItemConflictIndicator: FunctionComponent<Props> = ({ item }) => {
return item.conflictOf ? (
<div className="flex flex-wrap items-center mt-0.5">
<div className={'py-1 px-1.5 rounded mr-1 mt-2 bg-danger color-danger-contrast'}>
<div className={'py-1 px-1.5 rounded mr-1 mt-2 bg-danger text-danger-contrast'}>
<div className="text-xs font-bold text-center">Conflicted Copy</div>
</div>
</div>

View File

@@ -14,30 +14,30 @@ type Props = {
const ListItemFlagIcons: FunctionComponent<Props> = ({ item, hasFiles = false }) => {
return (
<div className="flex items-start p-4 pl-0 border-0 border-b-1 border-solid border-main">
<div className="flex items-start p-4 pl-0 border-b border-solid border-border">
{item.locked && (
<span className="flex items-center" title="Editing Disabled">
<Icon ariaLabel="Editing Disabled" type="pencil-off" className="sn-icon--small color-info" />
<Icon ariaLabel="Editing Disabled" type="pencil-off" className="text-info" size="small" />
</span>
)}
{item.trashed && (
<span className="flex items-center ml-1.5" title="Trashed">
<Icon ariaLabel="Trashed" type="trash-filled" className="sn-icon--small color-danger" />
<Icon ariaLabel="Trashed" type="trash-filled" className="text-danger" size="small" />
</span>
)}
{item.archived && (
<span className="flex items-center ml-1.5" title="Archived">
<Icon ariaLabel="Archived" type="archive" className="sn-icon--mid color-accessory-tint-3" />
<Icon ariaLabel="Archived" type="archive" className="text-accessory-tint-3" size="medium" />
</span>
)}
{item.pinned && (
<span className="flex items-center ml-1.5" title="Pinned">
<Icon ariaLabel="Pinned" type="pin-filled" className="sn-icon--small color-info" />
<Icon ariaLabel="Pinned" type="pin-filled" className="text-info" size="small" />
</span>
)}
{hasFiles && (
<span className="flex items-center ml-1.5" title="Files">
<Icon ariaLabel="Files" type="attachment-file" className="sn-icon--small color-info" />
<Icon ariaLabel="Files" type="attachment-file" className="text-info" size="small" />
</span>
)}
</div>

View File

@@ -20,7 +20,7 @@ const ListItemMetadata: FunctionComponent<Props> = ({ item, hideDate, sortBy })
}
return (
<div className="text-xs leading-1.4 mt-1 faded">
<div className="text-xs leading-1.4 mt-1 opacity-50">
{item.protected && <span>Protected {hideDate ? '' : ' • '}</span>}
{!hideDate && showModifiedDate && <span>Modified {item.updatedAtString || 'Now'}</span>}
{!hideDate && !showModifiedDate && <span>{item.createdAtString || 'Now'}</span>}

View File

@@ -16,10 +16,10 @@ const ListItemTags: FunctionComponent<Props> = ({ hideTags, tags }) => {
<div className="flex flex-wrap mt-1.5 text-xs gap-2">
{tags.map((tag) => (
<span
className="inline-flex items-center py-1 px-1.5 bg-passive-4-opacity-variant color-foreground rounded-0.5"
className="inline-flex items-center py-1 px-1.5 bg-passive-4-opacity-variant text-foreground rounded-sm"
key={tag.uuid}
>
<Icon type="hashtag" className="sn-icon--small color-passive-1 mr-1" />
<Icon type="hashtag" className="text-passive-1 mr-1" size="small" />
<span>{tag.title}</span>
</span>
))}

View File

@@ -45,8 +45,8 @@ const NoteListItem: FunctionComponent<DisplayableListItemProps> = ({
return (
<div
className={`content-list-item flex items-stretch w-full cursor-pointer ${
selected && 'selected border-0 border-l-2px border-solid border-info'
className={`content-list-item flex items-stretch w-full cursor-pointer text-text ${
selected && 'selected border-l-2 border-solid border-info'
}`}
id={item.uuid}
onClick={() => {
@@ -58,14 +58,14 @@ const NoteListItem: FunctionComponent<DisplayableListItemProps> = ({
}}
>
{!hideIcon ? (
<div className="flex flex-col items-center justify-between p-4 pr-3 mr-0">
<Icon ariaLabel={`Icon for ${editorName}`} type={icon} className={`color-accessory-tint-${tint}`} />
<div className="flex flex-col items-center justify-between p-4 pr-4 mr-0">
<Icon ariaLabel={`Icon for ${editorName}`} type={icon} className={`text-accessory-tint-${tint}`} />
</div>
) : (
<div className="pr-4" />
)}
<div className="flex-grow min-w-0 py-4 px-0 border-0 border-b-1 border-solid border-main">
<div className="flex items-start justify-between font-semibold text-base leading-1.3 overflow-hidden">
<div className="flex-grow min-w-0 py-4 px-0 border-b border-solid border-border">
<div className="flex items-start justify-between font-semibold text-base leading-[1.3] overflow-hidden">
<div className="break-word mr-2">{item.title}</div>
</div>
{!hidePreview && !item.hidePreview && !item.protected && (

View File

@@ -1,9 +1,11 @@
import { ListboxArrow, ListboxButton, ListboxInput, ListboxList, ListboxOption, ListboxPopover } from '@reach/listbox'
import { ListboxArrow, ListboxInput, ListboxList, ListboxPopover } from '@reach/listbox'
import '@reach/listbox/styles.css'
import VisuallyHidden from '@reach/visually-hidden'
import { FunctionComponent } from 'react'
import Icon from '@/Components/Icon/Icon'
import { DropdownItem } from './DropdownItem'
import StyledListboxButton from './StyledListboxButton'
import StyledListboxOption from './StyledListboxOption'
type DropdownProps = {
id: string
@@ -25,16 +27,16 @@ const CustomDropdownButton: FunctionComponent<ListboxButtonProps> = ({
iconClassName = '',
}) => (
<>
<div className="sn-dropdown-button-label">
<div className="flex items-center">
{icon ? (
<div className="flex mr-2">
<Icon type={icon} className={`sn-icon--small ${iconClassName}`} />
<Icon type={icon} className={iconClassName} size="small" />
</div>
) : null}
<div className="dropdown-selected-label">{label}</div>
</div>
<ListboxArrow className={`sn-dropdown-arrow ${isExpanded ? 'sn-dropdown-arrow-flipped' : ''}`}>
<Icon type="menu-arrow-down" className="sn-icon--small color-passive-1" />
<ListboxArrow className={`flex ${isExpanded ? 'rotate-180' : ''}`}>
<Icon type="menu-arrow-down" className="text-passive-1" size="small" />
</ListboxArrow>
</>
)
@@ -52,8 +54,7 @@ const Dropdown: FunctionComponent<DropdownProps> = ({ id, label, items, value, o
<>
<VisuallyHidden id={labelId}>{label}</VisuallyHidden>
<ListboxInput value={value} onChange={handleChange} aria-labelledby={labelId} disabled={disabled}>
<ListboxButton
className="sn-dropdown-button"
<StyledListboxButton
children={({ value, label, isExpanded }) => {
const current = items.find((item) => item.value === value)
const icon = current ? current?.icon : null
@@ -71,20 +72,14 @@ const Dropdown: FunctionComponent<DropdownProps> = ({ id, label, items, value, o
<div className="sn-component">
<ListboxList>
{items.map((item) => (
<ListboxOption
key={item.value}
className="sn-dropdown-item"
value={item.value}
label={item.label}
disabled={item.disabled}
>
<StyledListboxOption key={item.value} value={item.value} label={item.label} disabled={item.disabled}>
{item.icon ? (
<div className="flex mr-3">
<Icon type={item.icon} className={`sn-icon--small ${item.iconClassName ?? ''}`} />
<Icon type={item.icon} className={item.iconClassName ?? ''} size="small" />
</div>
) : null}
<div className="text-input">{item.label}</div>
</ListboxOption>
</StyledListboxOption>
))}
</ListboxList>
</div>

View File

@@ -0,0 +1,21 @@
import { ListboxButton } from '@reach/listbox'
import styled from 'styled-components'
const StyledListboxButton = styled(ListboxButton)`
&[data-reach-listbox-button] {
background-color: var(--sn-stylekit-background-color);
border-radius: 0.25rem;
border: 1px solid var(--sn-stylekit-border-color);
color: var(--sn-stylekit-contrast-foreground-color);
font-size: 0.875rem;
line-height: 1.25rem;
min-width: 13.75rem;
padding-bottom: 0.375rem;
padding-left: 0.875rem;
padding-right: 0.875rem;
padding-top: 0.375rem;
width: fit-content;
}
`
export default StyledListboxButton

View File

@@ -0,0 +1,38 @@
import { ListboxOption } from '@reach/listbox'
import styled from 'styled-components'
const StyledListboxOption = styled(ListboxOption)`
&[data-reach-listbox-option] {
align-items: center;
background-color: transparent;
border: none;
color: var(--sn-stylekit-contrast-foreground-color);
cursor: pointer;
display: flex;
font-size: 0.875rem;
padding-bottom: 0.375rem;
padding-left: 0.75rem;
padding-right: 0.75rem;
padding-top: 0.375rem;
text-align: left;
width: 100%;
&[data-current-selected] {
color: var(--sn-stylekit-info-color);
background-color: var(--sn-stylekit-info-backdrop-color);
}
&:hover {
background-color: var(--sn-stylekit-contrast-background-color);
color: var(--sn-stylekit-foreground-color);
}
&:focus {
background-color: var(--sn-stylekit-info-backdrop-color);
box-shadow: none;
outline: none;
}
}
`
export default StyledListboxOption

View File

@@ -86,7 +86,7 @@ const FileContextMenu: FunctionComponent<Props> = observer(({ filesController, s
return (
<div
ref={contextMenuRef}
className="sn-dropdown min-w-60 max-h-120 max-w-xs flex flex-col py-2 overflow-y-auto fixed"
className="bg-default rounded shadow-main min-w-60 max-h-120 max-w-xs flex flex-col py-2 overflow-y-auto fixed"
style={{
...contextMenuStyle,
maxHeight: contextMenuMaxHeight,

View File

@@ -6,6 +6,7 @@ import Switch from '@/Components/Switch/Switch'
import { observer } from 'mobx-react-lite'
import { FilesController } from '@/Controllers/FilesController'
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
import HorizontalSeparator from '../Shared/HorizontalSeparator'
type Props = {
closeMenu: () => void
@@ -64,35 +65,47 @@ const FileMenuOptions: FunctionComponent<Props> = ({
return (
<>
<button onBlur={closeOnBlur} className="sn-dropdown-item focus:bg-info-backdrop" onClick={onPreview}>
<Icon type="file" className="mr-2 color-neutral" />
<button
onBlur={closeOnBlur}
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-sm"
onClick={onPreview}
>
<Icon type="file" className="mr-2 text-neutral" />
Preview file
</button>
{selectedFiles.length === 1 && (
<>
{isFileAttachedToNote ? (
<button onBlur={closeOnBlur} className="sn-dropdown-item focus:bg-info-backdrop" onClick={onDetach}>
<Icon type="link-off" className="mr-2 color-neutral" />
<button
onBlur={closeOnBlur}
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-sm"
onClick={onDetach}
>
<Icon type="link-off" className="mr-2 text-neutral" />
Detach from note
</button>
) : shouldShowAttachOption ? (
<button onBlur={closeOnBlur} className="sn-dropdown-item focus:bg-info-backdrop" onClick={onAttach}>
<Icon type="link" className="mr-2 color-neutral" />
<button
onBlur={closeOnBlur}
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-sm"
onClick={onAttach}
>
<Icon type="link" className="mr-2 text-neutral" />
Attach to note
</button>
) : null}
</>
)}
<div className="min-h-1px my-1 bg-border"></div>
<HorizontalSeparator classes="my-1" />
<button
className="sn-dropdown-item justify-between focus:bg-info-backdrop"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-sm justify-between"
onClick={() => {
void filesController.setProtectionForFiles(!hasProtectedFiles, selectionController.selectedFiles)
}}
onBlur={closeOnBlur}
>
<span className="flex items-center">
<Icon type="password" className="mr-2 color-neutral" />
<Icon type="password" className="mr-2 text-neutral" />
Password protection
</span>
<Switch
@@ -101,41 +114,41 @@ const FileMenuOptions: FunctionComponent<Props> = ({
checked={hasProtectedFiles}
/>
</button>
<div className="min-h-1px my-1 bg-border"></div>
<HorizontalSeparator classes="my-1" />
<button
onBlur={closeOnBlur}
className="sn-dropdown-item focus:bg-info-backdrop"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-sm"
onClick={() => {
void filesController.downloadFiles(selectionController.selectedFiles)
}}
>
<Icon type="download" className="mr-2 color-neutral" />
<Icon type="download" className="mr-2 text-neutral" />
Download
</button>
{shouldShowRenameOption && (
<button
onBlur={closeOnBlur}
className="sn-dropdown-item focus:bg-info-backdrop"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-sm"
onClick={() => {
renameToggleCallback?.(true)
}}
>
<Icon type="pencil" className="mr-2 color-neutral" />
<Icon type="pencil" className="mr-2 text-neutral" />
Rename
</button>
)}
<button
onBlur={closeOnBlur}
className="sn-dropdown-item focus:bg-info-backdrop"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-sm"
onClick={() => {
void filesController.deleteFilesPermanently(selectionController.selectedFiles)
}}
>
<Icon type="trash" className="mr-2 color-danger" />
<span className="color-danger">Delete permanently</span>
<Icon type="trash" className="mr-2 text-danger" />
<span className="text-danger">Delete permanently</span>
</button>
{selectedFiles.length === 1 && (
<div className="px-3 pt-1.5 pb-0.5 text-xs color-neutral font-medium">
<div className="px-3 pt-1.5 pb-0.5 text-xs text-neutral font-medium">
<div>
<span className="font-semibold">File ID:</span> {selectedFiles[0].uuid}
</div>

View File

@@ -52,7 +52,7 @@ const FilesOptionsPanel = ({ filesController, selectionController }: Props) => {
}}
onBlur={closeOnBlur}
ref={buttonRef}
className="sn-icon-button border-contrast"
className="flex justify-center items-center min-w-8 h-8 bg-text-padding hover:bg-contrast focus:bg-contrast text-neutral border border-solid border-border rounded-full cursor-pointer"
>
<VisuallyHidden>Actions</VisuallyHidden>
<Icon type="more" className="block" />
@@ -69,7 +69,9 @@ const FilesOptionsPanel = ({ filesController, selectionController }: Props) => {
...position,
maxHeight,
}}
className="sn-dropdown sn-dropdown--animated min-w-80 max-h-120 max-w-xs flex flex-col py-2 overflow-y-auto fixed"
className={`${
open ? 'flex' : 'hidden'
} flex-col min-w-80 max-h-120 max-w-xs py-2 fixed bg-default rounded shadow-main transition-transform duration-150 slide-down-animation overflow-y-auto`}
onBlur={closeOnBlur}
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
>

View File

@@ -2,6 +2,7 @@ import { WebApplication } from '@/Application/Application'
import { concatenateUint8Arrays } from '@/Utils'
import { FileItem } from '@standardnotes/snjs'
import { useEffect, useMemo, useState } from 'react'
import Spinner from '@/Components/Spinner/Spinner'
import FilePreviewError from './FilePreviewError'
import { isFileTypePreviewable } from './isFilePreviewable'
import PreviewComponent from './PreviewComponent'
@@ -59,7 +60,7 @@ const FilePreview = ({ file, application }: Props) => {
return isDownloading ? (
<div className="flex flex-col justify-center items-center flex-grow">
<div className="flex items-center">
<div className="sk-spinner w-5 h-5 spinner-info mr-3"></div>
<Spinner className="w-5 h-5 mr-3" />
<div className="text-base font-semibold">{downloadProgress}%</div>
</div>
<span className="mt-3">Loading file...</span>

View File

@@ -17,12 +17,12 @@ const FilePreviewError = ({ file, filesController, isFilePreviewable, tryAgainCa
<div className="font-bold text-base mb-2">This file can't be previewed.</div>
{isFilePreviewable ? (
<>
<div className="text-sm text-center color-passive-0 mb-4 max-w-35ch">
<div className="text-sm text-center text-passive-0 mb-4 max-w-35ch">
There was an error loading the file. Try again, or download the file and open it using another application.
</div>
<div className="flex items-center">
<Button
variant="primary"
primary
className="mr-3"
onClick={() => {
tryAgainCallback()
@@ -31,7 +31,6 @@ const FilePreviewError = ({ file, filesController, isFilePreviewable, tryAgainCa
Try again
</Button>
<Button
variant="normal"
onClick={() => {
filesController.downloadFile(file).catch(console.error)
}}
@@ -42,11 +41,11 @@ const FilePreviewError = ({ file, filesController, isFilePreviewable, tryAgainCa
</>
) : (
<>
<div className="text-sm text-center color-passive-0 mb-4 max-w-35ch">
<div className="text-sm text-center text-passive-0 mb-4 max-w-35ch">
To view this file, download it and open it using another application.
</div>
<Button
variant="primary"
primary
onClick={() => {
filesController.downloadFile(file).catch(console.error)
}}

View File

@@ -9,7 +9,7 @@ type Props = {
const FilePreviewInfoPanel: FunctionComponent<Props> = ({ file }) => {
return (
<div className="flex flex-col min-w-70 p-4 border-0 border-l-1px border-solid border-main">
<div className="flex flex-col min-w-70 p-4 border-0 border-l border-solid border-border">
<div className="flex items-center mb-4">
<Icon type="info" className="mr-2" />
<div className="font-semibold">File information</div>

View File

@@ -78,16 +78,10 @@ const FilePreviewModal: FunctionComponent<Props> = observer(({ application, view
>
<DialogContent
aria-label="File preview modal"
className="flex flex-col rounded shadow-overlay"
style={{
width: '90%',
maxWidth: '90%',
minHeight: '90%',
background: 'var(--modal-background-color)',
}}
className="flex flex-col rounded shadow-main p-0 min-w-[90%] min-h-[90%] bg-[color:var(--modal-background-color)] "
>
<div
className="flex flex-shrink-0 justify-between items-center min-h-6 px-4 py-3 border-0 border-b-1 border-solid border-main focus:shadow-none"
className="flex flex-shrink-0 justify-between items-center min-h-6 px-4 py-3 border-0 border-b border-solid border-border focus:shadow-none"
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
onKeyDown={keyDownHandler}
>
@@ -97,10 +91,10 @@ const FilePreviewModal: FunctionComponent<Props> = observer(({ application, view
</div>
<div className="flex items-center">
<button
className="flex p-1.5 mr-4 bg-transparent hover:bg-contrast border-solid border-main border-1 cursor-pointer rounded"
className="flex p-1.5 mr-4 bg-transparent hover:bg-contrast border-solid border-border border cursor-pointer rounded"
onClick={() => setShowFileInfoPanel((show) => !show)}
>
<Icon type="info" className="color-neutral" />
<Icon type="info" className="text-neutral" />
</button>
<button
ref={closeButtonRef}
@@ -108,7 +102,7 @@ const FilePreviewModal: FunctionComponent<Props> = observer(({ application, view
aria-label="Close modal"
className="flex p-1 bg-transparent hover:bg-contrast border-0 cursor-pointer rounded"
>
<Icon type="close" className="color-neutral" />
<Icon type="close" className="text-neutral" />
</button>
</div>
</div>

View File

@@ -37,7 +37,7 @@ const ImagePreview: FunctionComponent<Props> = ({ objectUrl }) => {
}}
/>
</div>
<div className="flex items-center absolute left-1/2 -translate-x-1/2 bottom-6 py-1 px-3 bg-default border-1 border-solid border-main rounded">
<div className="flex items-center absolute left-1/2 -translate-x-1/2 bottom-6 py-1 px-3 bg-default border border-solid border-border rounded">
<span className="mr-1.5">Zoom:</span>
<IconButton
className="hover:bg-contrast p-1 rounded"

View File

@@ -31,12 +31,15 @@ const FileViewWithoutProtection = ({ application, viewControllerManager, file }:
return (
<div className="sn-component section editor" aria-label="File">
<div className="flex flex-col">
<div className="content-title-bar section-title-bar w-full" id="file-title-bar">
<div
className="content-title-bar section-title-bar z-editor-title-bar section-title-bar w-full"
id="file-title-bar"
>
<div className="flex items-center justify-between h-8">
<div className="flex-grow">
<form onSubmit={onFormSubmit} className="title overflow-auto">
<input
className="input"
className="input text-lg"
id={ElementIds.FileTitleEditor}
onChange={onTitleChange}
onFocus={(event) => {

View File

@@ -342,9 +342,12 @@ class Footer extends PureComponent<Props, State> {
override render() {
return (
<div className="sn-component">
<div id="footer-bar" className="sk-app-bar no-edges no-bottom-edge">
<div className="left">
<div className="sk-app-bar-item ml-0">
<div
id="footer-bar"
className="flex justify-between items-center w-full h-6 px-3 bg-contrast text-text z-footer-bar border-t border-border select-none"
>
<div className="left flex h-full">
<div className="sk-app-bar-item z-footer-bar-item relative select-none ml-0">
<div
onClick={this.accountMenuClickHandler}
className={
@@ -352,8 +355,12 @@ class Footer extends PureComponent<Props, State> {
' w-8 h-full flex items-center justify-center cursor-pointer rounded-full'
}
>
<div className={this.state.hasError ? 'danger' : (this.user ? 'info' : 'neutral') + ' w-5 h-5'}>
<Icon type="account-circle" className="hover:color-info w-5 h-5 max-h-5" />
<div
className={
this.state.hasError ? 'text-danger' : (this.user ? 'text-info' : 'text-neutral') + ' w-5 h-5'
}
>
<Icon type="account-circle" className="hover:text-info max-h-5" />
</div>
</div>
{this.state.showAccountMenu && (
@@ -365,7 +372,7 @@ class Footer extends PureComponent<Props, State> {
/>
)}
</div>
<div className="sk-app-bar-item ml-0-important">
<div className="sk-app-bar-item z-footer-bar-item relative select-none ml-0-important">
<div
onClick={this.quickSettingsClickHandler}
className="w-8 h-full flex items-center justify-center cursor-pointer"
@@ -373,7 +380,7 @@ class Footer extends PureComponent<Props, State> {
<div className="h-5">
<Icon
type="tune"
className={(this.state.showQuickSettingsMenu ? 'color-info' : '') + ' rounded hover:color-info'}
className={(this.state.showQuickSettingsMenu ? 'text-info' : '') + ' rounded hover:text-info'}
/>
</div>
</div>
@@ -387,9 +394,8 @@ class Footer extends PureComponent<Props, State> {
</div>
{this.state.showBetaWarning && (
<Fragment>
<div className="sk-app-bar-item border" />
<div className="sk-app-bar-item">
<a onClick={this.betaMessageClickHandler} className="no-decoration sk-label title">
<div className="flex items-center z-footer-bar-item pl-3 ml-3 relative select-none border-l border-solid border-border">
<a onClick={this.betaMessageClickHandler} className="no-decoration text-xs font-bold title">
You are using a beta version of the app
</a>
</div>
@@ -398,28 +404,32 @@ class Footer extends PureComponent<Props, State> {
</div>
<div className="center">
{this.state.arbitraryStatusMessage && (
<div className="sk-app-bar-item">
<div className="sk-app-bar-item-column">
<span className="neutral sk-label">{this.state.arbitraryStatusMessage}</span>
</div>
<div className="flex items-center z-footer-bar-item relative select-none text-xs text-neutral font-bold">
{this.state.arbitraryStatusMessage}
</div>
)}
</div>
<div className="right">
<div className="right flex h-full">
{this.state.dataUpgradeAvailable && (
<div onClick={this.securityUpdateClickHandler} className="sk-app-bar-item">
<span className="success sk-label">Encryption upgrade available.</span>
<div
onClick={this.securityUpdateClickHandler}
className="flex items-center text-xs text-success font-bold z-footer-bar-item relative select-none"
>
Encryption upgrade available.
</div>
)}
{this.state.newUpdateAvailable && (
<div onClick={this.newUpdateClickHandler} className="sk-app-bar-item">
<span className="info sk-label">New update available.</span>
<div
onClick={this.newUpdateClickHandler}
className="flex items-center ml-3 text-xs text-info font-bold z-footer-bar-item relative select-none"
>
New update available.
</div>
)}
{(this.state.outOfSync || this.state.showSyncResolution) && (
<div className="sk-app-bar-item">
<div className="flex items-center ml-3 z-footer-bar-item relative select-none">
{this.state.outOfSync && (
<div onClick={this.syncResolutionClickHandler} className="sk-label warning">
<div onClick={this.syncResolutionClickHandler} className="font-bold text-xs text-warning">
Potentially Out of Sync
</div>
)}
@@ -429,22 +439,19 @@ class Footer extends PureComponent<Props, State> {
</div>
)}
{this.state.offline && (
<div className="sk-app-bar-item">
<div className="sk-label">Offline</div>
<div className="flex items-center ml-3 text-xs font-bold z-footer-bar-item relative select-none">
Offline
</div>
)}
{this.state.hasPasscode && (
<Fragment>
<div className="sk-app-bar-item border" />
<div
id="lock-item"
onClick={this.lockClickHandler}
title="Locks application and wipes unencrypted data from memory."
className="sk-app-bar-item pl-1 hover:color-info"
>
<Icon type="lock-filled" />
</div>
</Fragment>
<div
id="lock-item"
onClick={this.lockClickHandler}
title="Locks application and wipes unencrypted data from memory."
className="flex items-center z-footer-bar-item relative select-none pl-2 ml-3 hover:text-info border-l border-solid border-border cursor-pointer"
>
<Icon type="lock-filled" size="custom" className="w-4.5 h-4.5" />
</div>
)}
</div>
</div>

View File

@@ -1,4 +1,4 @@
import { FunctionComponent } from 'react'
import { FunctionComponent, useMemo } from 'react'
import { IconType } from '@standardnotes/snjs'
import {
@@ -187,16 +187,32 @@ type Props = {
type: IconType
className?: string
ariaLabel?: string
size?: 'small' | 'medium' | 'normal' | 'custom'
}
const Icon: FunctionComponent<Props> = ({ type, className = '', ariaLabel }) => {
const Icon: FunctionComponent<Props> = ({ type, className = '', ariaLabel, size = 'normal' }) => {
const IconComponent = ICONS[type as keyof typeof ICONS]
const dimensions = useMemo(() => {
switch (size) {
case 'small':
return 'w-3.5 h-3.5'
case 'medium':
return 'w-4 h-4'
case 'custom':
return ''
default:
return 'w-5 h-5'
}
}, [size])
if (!IconComponent) {
return null
}
return (
<IconComponent
className={`sn-icon ${className}`}
className={`${dimensions} fill-current ${className}`}
role="img"
{...(ariaLabel ? { 'aria-label': ariaLabel } : { 'aria-hidden': true })}
/>

View File

@@ -0,0 +1,18 @@
type Props = {
style: 'neutral' | 'info' | 'danger'
}
const baseClassNames = 'border border-solid w-3 h-3 p-0 rounded-full flex-shrink-0'
const IndicatorCircle = ({ style }: Props) => {
switch (style) {
case 'neutral':
return <div className={`${baseClassNames} bg-neutral border-neutral`} />
case 'info':
return <div className={`${baseClassNames} bg-info border-info`} />
case 'danger':
return <div className={`${baseClassNames} bg-danger border-danger`} />
}
}
export default IndicatorCircle

View File

@@ -3,10 +3,10 @@ import { DecoratedInputProps } from './DecoratedInputProps'
const getClassNames = (hasLeftDecorations: boolean, hasRightDecorations: boolean) => {
return {
container: `flex items-stretch position-relative bg-default border-1 border-solid border-main rounded focus-within:ring-info overflow-hidden ${
container: `flex items-stretch position-relative bg-default border border-solid border-border rounded focus-within:ring-2 focus-within:ring-info overflow-hidden text-sm ${
!hasLeftDecorations && !hasRightDecorations ? 'px-2 py-1.5' : ''
}`,
input: `w-full border-0 focus:shadow-none bg-transparent color-text ${
input: `w-full border-0 focus:shadow-none focus:outline-none focus:ring-none bg-transparent text-text ${
!hasLeftDecorations && hasRightDecorations ? 'pl-2' : ''
} ${hasRightDecorations ? 'pr-2' : ''}`,
disabled: 'bg-passive-5 cursor-not-allowed',
@@ -21,6 +21,7 @@ const DecoratedInput = forwardRef(
{
type = 'text',
className = '',
id = '',
disabled = false,
left,
right,
@@ -49,6 +50,7 @@ const DecoratedInput = forwardRef(
<input
type={type}
id={id}
className={`${classNames.input} ${disabled ? classNames.disabled : ''}`}
disabled={disabled}
value={value}

View File

@@ -3,6 +3,7 @@ import { FocusEventHandler, KeyboardEventHandler, ReactNode } from 'react'
export type DecoratedInputProps = {
type?: 'text' | 'email' | 'password'
className?: string
id?: string
disabled?: boolean
left?: ReactNode[]
right?: ReactNode[]

View File

@@ -8,9 +8,9 @@ const Toggle: FunctionComponent<{
setIsToggled: Dispatch<SetStateAction<boolean>>
}> = ({ isToggled, setIsToggled }) => (
<IconButton
className="w-5 h-5 p-0 justify-center sk-circle hover:bg-passive-4 color-neutral"
className="w-5 h-5 p-0 justify-center rounded-full hover:bg-passive-4 text-neutral"
icon={isToggled ? 'eye-off' : 'eye'}
iconClassName="sn-icon--small"
iconClassName="w-3.5 h-3.5"
title="Show/hide password"
onClick={() => setIsToggled((isToggled) => !isToggled)}
focusable={true}

View File

@@ -33,14 +33,14 @@ const FloatingLabelInput = forwardRef(
const BASE_CLASSNAME = 'relative bg-default'
const LABEL_CLASSNAME = `hidden absolute ${!focused ? 'color-neutral' : 'color-info'} ${
focused || value ? 'flex top-0 left-2 pt-1.5 px-1' : ''
} ${isInvalid ? 'color-danger' : ''} ${labelClassName}`
const LABEL_CLASSNAME = `absolute ${!focused ? 'text-neutral' : 'text-info'} ${
focused || value ? 'flex top-0 left-2 pt-1.5 px-1' : 'hidden'
} ${isInvalid ? 'text-danger' : ''} ${labelClassName}`
const INPUT_CLASSNAME = `w-full h-full ${
focused || value ? 'pt-6 pb-2' : 'py-2.5'
} px-3 text-input border-1 border-solid border-main rounded placeholder-medium text-input focus:ring-info ${
isInvalid ? 'border-danger placeholder-dark-red' : ''
} px-3 text-sm border border-solid border-border rounded placeholder:font-medium focus:ring-info ${
isInvalid ? 'border-danger placeholder:text-danger' : ''
} ${inputClassName}`
const handleFocus = () => setFocused(true)

View File

@@ -8,7 +8,7 @@ interface Props {
const Input: FunctionComponent<Props> = ({ className = '', disabled = false, text }) => {
const base = 'rounded py-1.5 px-3 text-input my-1 h-8 bg-contrast'
const stateClasses = disabled ? 'no-border' : 'border-solid border-1 border-main'
const stateClasses = disabled ? 'no-border' : 'border-solid border border-border'
const classes = `${base} ${stateClasses} ${className}`
return <input type="text" className={classes} disabled={disabled} value={text} />
}

View File

@@ -53,7 +53,7 @@ const Menu: FunctionComponent<MenuProps> = ({
return (
<menu
className={`m-0 pl-0 list-style-none focus:shadow-none ${className}`}
className={`m-0 pl-0 list-none focus:shadow-none ${className}`}
onKeyDown={handleKeyDown}
ref={menuElementRef}
style={style}

View File

@@ -5,6 +5,7 @@ import { SwitchProps } from '@/Components/Switch/SwitchProps'
import { IconType } from '@standardnotes/snjs'
import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants/Constants'
import { MenuItemType } from './MenuItemType'
import RadioIndicator from '../RadioIndicator/RadioIndicator'
type MenuItemProps = {
type: MenuItemType
@@ -36,10 +37,10 @@ const MenuItem = forwardRef(
ref: Ref<HTMLButtonElement>,
) => {
return type === MenuItemType.SwitchButton && typeof onChange === 'function' ? (
<li className="list-style-none" role="none">
<li className="list-none" role="none">
<button
ref={ref}
className="sn-dropdown-item focus:bg-info-backdrop focus:shadow-none justify-between"
className="flex items-center justify-between border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none"
onClick={() => {
onChange(!checked)
}}
@@ -53,19 +54,19 @@ const MenuItem = forwardRef(
</button>
</li>
) : (
<li className="list-style-none" role="none">
<li className="list-none" role="none">
<button
ref={ref}
role={type === MenuItemType.RadioButton ? 'menuitemradio' : 'menuitem'}
tabIndex={typeof tabIndex === 'number' ? tabIndex : FOCUSABLE_BUT_NOT_TABBABLE}
className={`sn-dropdown-item focus:bg-info-backdrop focus:shadow-none ${className}`}
className={`flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-menu-item ${className}`}
onClick={onClick}
onBlur={onBlur}
{...(type === MenuItemType.RadioButton ? { 'aria-checked': checked } : {})}
>
{type === MenuItemType.IconButton && icon ? <Icon type={icon} className={iconClassName} /> : null}
{type === MenuItemType.RadioButton && typeof checked === 'boolean' ? (
<div className={`pseudo-radio-btn ${checked ? 'pseudo-radio-btn--checked' : ''} flex-shrink-0`}></div>
<RadioIndicator checked={checked} className="flex-shrink-0" />
) : null}
{children}
</button>

View File

@@ -1,8 +1,8 @@
import { FunctionComponent } from 'react'
const MenuItemSeparator: FunctionComponent = () => (
<li className="list-style-none" role="none">
<div role="separator" className="h-1px my-2 bg-border" />
<li className="list-none" role="none">
<div role="separator" className="h-[1px] my-2 bg-border" />
</li>
)

View File

@@ -40,7 +40,7 @@ const MultipleSelectedFiles = ({
return (
<div className="flex flex-col h-full items-center">
<div className="flex items-center justify-between p-4 w-full">
<h1 className="sk-h1 font-bold m-0">{count} selected files</h1>
<h1 className="text-lg font-bold m-0">{count} selected files</h1>
<div className="flex">
<div className="mr-3">
<AttachedFilesButton
@@ -58,7 +58,7 @@ const MultipleSelectedFiles = ({
</div>
<div className="flex-grow flex flex-col justify-center items-center w-full max-w-md">
<IlNotesIcon className="block" />
<h2 className="text-lg m-0 text-center mt-4">{count} selected files</h2>
<h2 className="font-bold text-lg m-0 text-center mt-4">{count} selected files</h2>
<p className="text-sm mt-2 text-center max-w-60">Actions will be performed on all selected files.</p>
<Button className="mt-2.5" onClick={cancelMultipleSelection}>
Cancel multiple selection

View File

@@ -47,7 +47,7 @@ const MultipleSelectedNotes = ({
return (
<div className="flex flex-col h-full items-center">
<div className="flex items-center justify-between p-4 w-full">
<h1 className="sk-h1 font-bold m-0">{count} selected notes</h1>
<h1 className="text-lg font-bold m-0">{count} selected notes</h1>
<div className="flex">
<div className="mr-3">
<AttachedFilesButton
@@ -74,7 +74,7 @@ const MultipleSelectedNotes = ({
</div>
<div className="flex-grow flex flex-col justify-center items-center w-full max-w-md">
<IlNotesIcon className="block" />
<h2 className="text-lg m-0 text-center mt-4">{count} selected notes</h2>
<h2 className="font-bold text-lg m-0 text-center mt-4">{count} selected notes</h2>
<p className="text-sm mt-2 text-center max-w-60">Actions will be performed on all selected notes.</p>
<Button className="mt-2.5" onClick={cancelMultipleSelection}>
Cancel multiple selection

View File

@@ -52,8 +52,8 @@ const Navigation: FunctionComponent<Props> = ({ application }) => {
<div id="navigation-content" className="content">
<div className="section-title-bar">
<div className="section-title-bar-header">
<div className="sk-h3 title">
<span className="sk-bold">Views</span>
<div className="text-sm title">
<span className="font-bold">Views</span>
</div>
</div>
</div>

View File

@@ -3,6 +3,7 @@ import { AccountMenuController } from '@/Controllers/AccountMenu/AccountMenuCont
import { NoAccountWarningController } from '@/Controllers/NoAccountWarningController'
import { observer } from 'mobx-react-lite'
import { MouseEventHandler, useCallback } from 'react'
import Button from '@/Components/Button/Button'
type Props = {
accountMenuController: AccountMenuController
@@ -23,18 +24,18 @@ const NoAccountWarningContent = ({ accountMenuController, noAccountWarningContro
}, [noAccountWarningController])
return (
<div className="mt-4 p-4 rounded-md shadow-sm grid grid-template-cols-1fr">
<h1 className="sk-h3 m-0 font-semibold">Data not backed up</h1>
<p className="m-0 mt-1 col-start-1 col-end-3">Sign in or register to back up your notes.</p>
<button className="sn-button small info mt-3 col-start-1 col-end-3 justify-self-start" onClick={showAccountMenu}>
<div className="mt-4 p-4 rounded-md shadow grid grid-cols-1">
<h1 className="sk-h3 m-0 font-semibold text-sm">Data not backed up</h1>
<p className="m-0 mt-1 col-start-1 col-end-3 text-sm">Sign in or register to back up your notes.</p>
<Button primary small className="mt-3 col-start-1 col-end-3 justify-self-start" onClick={showAccountMenu}>
Open Account menu
</button>
</Button>
<button
onClick={hideWarning}
title="Ignore warning"
aria-label="Ignore warning"
style={{ height: '20px' }}
className="border-0 m-0 p-0 bg-transparent cursor-pointer rounded-md col-start-2 row-start-1 color-neutral hover:color-info"
className="border-0 m-0 p-0 bg-transparent cursor-pointer rounded-md col-start-2 row-start-1 text-neutral hover:text-info"
>
<Icon type="close" className="block" />
</button>

View File

@@ -116,7 +116,7 @@ const NoteTag = ({ viewControllerManager, tag }: Props) => {
return (
<button
ref={tagRef}
className="sn-tag pl-1 pr-2 mr-2"
className="h-6 bg-contrast border-0 rounded text-xs text-text flex items-center mt-2 cursor-pointer hover:bg-secondary-contrast focus:bg-secondary-contrast py-2 pl-1 pr-2 mr-2"
onClick={onTagClick}
onKeyDown={onKeyDown}
onFocus={onFocus}
@@ -124,9 +124,9 @@ const NoteTag = ({ viewControllerManager, tag }: Props) => {
tabIndex={getTabIndex()}
title={longTitle}
>
<Icon type="hashtag" className="sn-icon--small color-info mr-1" />
<Icon type="hashtag" className="text-info mr-1" size="small" />
<span className="whitespace-nowrap overflow-hidden overflow-ellipsis max-w-290px">
{prefixTitle && <span className="color-passive-1">{prefixTitle}</span>}
{prefixTitle && <span className="text-passive-1">{prefixTitle}</span>}
{title}
</span>
{showDeleteButton && (
@@ -138,7 +138,7 @@ const NoteTag = ({ viewControllerManager, tag }: Props) => {
onClick={onDeleteTagClick}
tabIndex={-1}
>
<Icon type="close" className="sn-icon--small color-neutral hover:color-info" />
<Icon type="close" className="text-neutral hover:text-info" size="small" />
</a>
)}
</button>

View File

@@ -17,12 +17,12 @@ const EditingDisabledBanner: FunctionComponent<Props> = ({
lockText,
}) => {
const background = showLockedIcon ? 'bg-warning-faded' : 'bg-info-faded'
const iconColor = showLockedIcon ? 'color-accessory-tint-3' : 'color-accessory-tint-1'
const textColor = showLockedIcon ? 'color-warning' : 'color-accessory-tint-1'
const iconColor = showLockedIcon ? 'text-accessory-tint-3' : 'text-accessory-tint-1'
const textColor = showLockedIcon ? 'text-warning' : 'text-accessory-tint-1'
return (
<div
className={`flex items-center relative ${background} px-3.5 py-2 cursor-pointer`}
className={`flex items-center relative ${background} px-3.5 py-2 cursor-pointer text-sm`}
onMouseLeave={onMouseLeave}
onMouseOver={onMouseOver}
onClick={onClick}

View File

@@ -36,6 +36,7 @@ import {
import { reloadFont } from './FontFunctions'
import { NoteViewProps } from './NoteViewProps'
import { WebAppEvent } from '@/Application/WebAppEvent'
import IndicatorCircle from '../IndicatorCircle/IndicatorCircle'
const MINIMUM_STATUS_DURATION = 400
const TEXTAREA_DEBOUNCE = 100
@@ -907,12 +908,15 @@ class NoteView extends PureComponent<NoteViewProps, State> {
)}
{this.note && (
<div id="editor-title-bar" className="content-title-bar section-title-bar w-full">
<div
id="editor-title-bar"
className="content-title-bar section-title-bar z-editor-title-bar section-title-bar w-full"
>
<div className="flex items-center justify-between h-8">
<div className={(this.state.noteLocked ? 'locked' : '') + ' flex-grow'}>
<div className="title overflow-auto">
<input
className="input"
className="input text-lg"
disabled={this.state.noteLocked}
id={ElementIds.NoteTitleEditor}
onChange={this.onTitleChange}
@@ -931,14 +935,14 @@ class NoteView extends PureComponent<NoteViewProps, State> {
<div id="save-status">
<div
className={
(this.state.syncTakingTooLong ? 'warning sk-bold ' : '') +
(this.state.saveError ? 'danger sk-bold ' : '') +
' message'
(this.state.syncTakingTooLong ? 'text-warning font-bold ' : '') +
(this.state.saveError ? 'text-danger font-bold ' : '') +
'text-xs message'
}
>
{this.state.noteStatus?.message}
</div>
{this.state.noteStatus?.desc && <div className="desc">{this.state.noteStatus.desc}</div>}
{this.state.noteStatus?.desc && <div className="text-xs desc">{this.state.noteStatus.desc}</div>}
</div>
</div>
<div className="mr-3">
@@ -980,7 +984,11 @@ class NoteView extends PureComponent<NoteViewProps, State> {
</div>
)}
<div id={ElementIds.EditorContent} className={ElementIds.EditorContent} ref={this.editorContentRef}>
<div
id={ElementIds.EditorContent}
className={`${ElementIds.EditorContent} z-editor-content`}
ref={this.editorContentRef}
>
{this.state.marginResizersEnabled && this.editorContentRef.current ? (
<PanelResizer
minWidth={300}
@@ -1039,8 +1047,11 @@ class NoteView extends PureComponent<NoteViewProps, State> {
<div id="editor-pane-component-stack">
{this.state.availableStackComponents.length > 0 && (
<div id="component-stack-menu-bar" className="sk-app-bar no-edges">
<div className="left">
<div
id="component-stack-menu-bar"
className="flex justify-between items-center w-full h-6 px-2 py-0 bg-contrast text-text border-t border-solid border-border"
>
<div className="flex h-full">
{this.state.availableStackComponents.map((component) => {
return (
<div
@@ -1048,19 +1059,16 @@ class NoteView extends PureComponent<NoteViewProps, State> {
onClick={() => {
this.toggleStackComponent(component).catch(console.error)
}}
className="sk-app-bar-item"
className="flex justify-center items-center flex-grow [&:not(:first-child)]:ml-3 cursor-pointer"
>
<div className="sk-app-bar-item-column">
<div
className={
(this.stackComponentExpanded(component) && component.active ? 'info ' : '') +
(!this.stackComponentExpanded(component) ? 'neutral ' : '') +
' sk-circle small'
}
/>
<div className="flex items-center h-full [&:not(:first-child)]:ml-2">
{this.stackComponentExpanded(component) && component.active && (
<IndicatorCircle style="info" />
)}
{!this.stackComponentExpanded(component) && <IndicatorCircle style="neutral" />}
</div>
<div className="sk-app-bar-item-column">
<div className="sk-label">{component.name}</div>
<div className="flex items-center h-full [&:not(:first-child)]:ml-2">
<div className="font-bold whitespace-nowrap text-xs">{component.name}</div>
</div>
</div>
)

View File

@@ -45,7 +45,7 @@ const NotesContextMenu = ({
return contextMenuOpen ? (
<div
ref={contextMenuRef}
className="sn-dropdown min-w-80 max-h-120 max-w-xs flex flex-col pt-2 overflow-y-auto fixed"
className="bg-default rounded shadow-main min-w-80 max-h-120 max-w-xs flex flex-col pt-2 overflow-y-auto fixed z-dropdown-menu"
style={{
...contextMenuPosition,
maxHeight: contextMenuMaxHeight,

View File

@@ -66,13 +66,13 @@ const AddTagOption: FunctionComponent<Props> = ({ navigationController, notesCon
}}
onBlur={closeOnBlur}
ref={menuButtonRef}
className="sn-dropdown-item justify-between"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-menu-item justify-between"
>
<div className="flex items-center">
<Icon type="hashtag" className="mr-2 color-neutral" />
<Icon type="hashtag" className="mr-2 text-neutral" />
Add tag
</div>
<Icon type="chevron-right" className="color-neutral" />
<Icon type="chevron-right" className="text-neutral" />
</DisclosureButton>
<DisclosurePanel
ref={menuRef}
@@ -86,12 +86,14 @@ const AddTagOption: FunctionComponent<Props> = ({ navigationController, notesCon
...menuStyle,
position: 'fixed',
}}
className="sn-dropdown min-w-80 flex flex-col py-2 max-h-120 max-w-xs fixed overflow-y-auto"
className={`${
isMenuOpen ? 'flex' : 'hidden'
} flex-col py-2 bg-default rounded shadow-main min-w-80 max-h-120 max-w-xs fixed overflow-y-auto`}
>
{navigationController.tags.map((tag) => (
<button
key={tag.uuid}
className="sn-dropdown-item sn-dropdown-item--no-icon max-w-80"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-2 text-left w-full focus:bg-info-backdrop focus:shadow-none text-menu-item max-w-80"
onBlur={closeOnBlur}
onClick={() => {
notesController.isTagInSelectedNotes(tag)

View File

@@ -50,7 +50,7 @@ const ChangeEditorOption: FunctionComponent<ChangeEditorOptionProps> = ({ applic
setMenuStyle(newMenuStyle)
setIsVisible(true)
}
})
}, 5)
}
}, [isOpen])
@@ -65,13 +65,13 @@ const ChangeEditorOption: FunctionComponent<ChangeEditorOptionProps> = ({ applic
}}
onBlur={closeOnBlur}
ref={buttonRef}
className="sn-dropdown-item justify-between"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-menu-item justify-between"
>
<div className="flex items-center">
<Icon type="dashboard" className="color-neutral mr-2" />
<Icon type="dashboard" className="text-neutral mr-2" />
Change note type
</div>
<Icon type="chevron-right" className="color-neutral" />
<Icon type="chevron-right" className="text-neutral" />
</DisclosureButton>
<DisclosurePanel
ref={menuRef}
@@ -85,7 +85,7 @@ const ChangeEditorOption: FunctionComponent<ChangeEditorOptionProps> = ({ applic
...menuStyle,
position: 'fixed',
}}
className="sn-dropdown flex flex-col max-h-120 min-w-68 fixed overflow-y-auto"
className="bg-default rounded shadow-main flex flex-col max-h-120 min-w-68 fixed overflow-y-auto"
>
{isOpen && (
<ChangeEditorMenu

View File

@@ -0,0 +1,145 @@
import { WebApplication } from '@/Application/Application'
import { Action, SNNote } from '@standardnotes/snjs'
import { Fragment, useCallback, useEffect, useState } from 'react'
import Icon from '@/Components/Icon/Icon'
import { ListedMenuGroup } from './ListedMenuGroup'
import ListedMenuItem from './ListedMenuItem'
import Spinner from '@/Components/Spinner/Spinner'
type ListedActionsMenuProps = {
application: WebApplication
note: SNNote
recalculateMenuStyle: () => void
}
const ListedActionsMenu = ({ application, note, recalculateMenuStyle }: ListedActionsMenuProps) => {
const [menuGroups, setMenuGroups] = useState<ListedMenuGroup[]>([])
const [isFetchingAccounts, setIsFetchingAccounts] = useState(true)
const reloadMenuGroup = useCallback(
async (group: ListedMenuGroup) => {
const updatedAccountInfo = await application.getListedAccountInfo(group.account, note.uuid)
if (!updatedAccountInfo) {
return
}
const updatedGroup: ListedMenuGroup = {
name: updatedAccountInfo.display_name,
account: group.account,
actions: updatedAccountInfo.actions as Action[],
}
const updatedGroups = menuGroups.map((group) => {
if (updatedGroup.account.authorId === group.account.authorId) {
return updatedGroup
} else {
return group
}
})
setMenuGroups(updatedGroups)
},
[application, menuGroups, note],
)
useEffect(() => {
const fetchListedAccounts = async () => {
if (!application.hasAccount()) {
setIsFetchingAccounts(false)
return
}
try {
const listedAccountEntries = await application.getListedAccounts()
if (!listedAccountEntries.length) {
throw new Error('No Listed accounts found')
}
const menuGroups: ListedMenuGroup[] = []
await Promise.all(
listedAccountEntries.map(async (account) => {
const accountInfo = await application.getListedAccountInfo(account, note.uuid)
if (accountInfo) {
menuGroups.push({
name: accountInfo.display_name,
account,
actions: accountInfo.actions as Action[],
})
} else {
menuGroups.push({
name: account.authorId,
account,
actions: [],
})
}
}),
)
setMenuGroups(
menuGroups.sort((a, b) => {
return a.name.toString().toLowerCase() < b.name.toString().toLowerCase() ? -1 : 1
}),
)
} catch (err) {
console.error(err)
} finally {
setIsFetchingAccounts(false)
setTimeout(() => {
recalculateMenuStyle()
})
}
}
void fetchListedAccounts()
}, [application, note.uuid, recalculateMenuStyle])
return (
<>
{isFetchingAccounts && (
<div className="w-full flex items-center justify-center p-4">
<Spinner className="w-5 h-5" />
</div>
)}
{!isFetchingAccounts && menuGroups.length ? (
<>
{menuGroups.map((group, index) => (
<Fragment key={group.account.authorId}>
<div
className={`w-full flex items-center px-2.5 py-2 text-input font-semibold text-text border-y border-solid border-border ${
index === 0 ? 'border-t-0 mb-1' : 'my-1'
}`}
>
<Icon type="notes" className="mr-2 text-info" /> {group.name}
</div>
{group.actions.length ? (
group.actions.map((action) => (
<ListedMenuItem
action={action}
note={note}
key={action.url}
group={group}
application={application}
reloadMenuGroup={reloadMenuGroup}
/>
))
) : (
<div className="px-3 py-2 text-sm text-passive-0 select-none">No actions available</div>
)}
</Fragment>
))}
</>
) : null}
{!isFetchingAccounts && !menuGroups.length ? (
<div className="w-full flex items-center justify-center px-4 py-6">
<div className="text-sm text-passive-0 select-none">No Listed accounts found</div>
</div>
) : null}
</>
)
}
export default ListedActionsMenu

View File

@@ -1,210 +1,17 @@
import { WebApplication } from '@/Application/Application'
import { calculateSubmenuStyle, SubmenuStyle } from '@/Utils/CalculateSubmenuStyle'
import { Disclosure, DisclosureButton, DisclosurePanel } from '@reach/disclosure'
import { Action, ListedAccount, SNNote } from '@standardnotes/snjs'
import { Fragment, FunctionComponent, useCallback, useEffect, useRef, useState } from 'react'
import { SNNote } from '@standardnotes/snjs'
import { FunctionComponent, useCallback, useEffect, useRef, useState } from 'react'
import Icon from '@/Components/Icon/Icon'
import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur'
import ListedActionsMenu from './ListedActionsMenu'
type Props = {
application: WebApplication
note: SNNote
}
type ListedMenuGroup = {
name: string
account: ListedAccount
actions: Action[]
}
type ListedMenuItemProps = {
action: Action
note: SNNote
group: ListedMenuGroup
application: WebApplication
reloadMenuGroup: (group: ListedMenuGroup) => Promise<void>
}
const ListedMenuItem: FunctionComponent<ListedMenuItemProps> = ({
action,
note,
application,
group,
reloadMenuGroup,
}) => {
const [isRunning, setIsRunning] = useState(false)
const handleClick = useCallback(async () => {
if (isRunning) {
return
}
setIsRunning(true)
await application.actionsManager.runAction(action, note)
setIsRunning(false)
reloadMenuGroup(group).catch(console.error)
}, [application, action, group, isRunning, note, reloadMenuGroup])
return (
<button
key={action.url}
onClick={handleClick}
className="sn-dropdown-item flex justify-between py-2 text-input focus:bg-info-backdrop focus:shadow-none"
>
<div className="flex flex-col">
<div className="font-semibold">{action.label}</div>
{action.access_type && (
<div className="text-xs mt-0.5 color-passive-0">
{'Uses '}
<strong>{action.access_type}</strong>
{' access to this note.'}
</div>
)}
</div>
{isRunning && <div className="sk-spinner spinner-info w-3 h-3" />}
</button>
)
}
type ListedActionsMenuProps = {
application: WebApplication
note: SNNote
recalculateMenuStyle: () => void
}
const ListedActionsMenu: FunctionComponent<ListedActionsMenuProps> = ({ application, note, recalculateMenuStyle }) => {
const [menuGroups, setMenuGroups] = useState<ListedMenuGroup[]>([])
const [isFetchingAccounts, setIsFetchingAccounts] = useState(true)
const reloadMenuGroup = useCallback(
async (group: ListedMenuGroup) => {
const updatedAccountInfo = await application.getListedAccountInfo(group.account, note.uuid)
if (!updatedAccountInfo) {
return
}
const updatedGroup: ListedMenuGroup = {
name: updatedAccountInfo.display_name,
account: group.account,
actions: updatedAccountInfo.actions as Action[],
}
const updatedGroups = menuGroups.map((group) => {
if (updatedGroup.account.authorId === group.account.authorId) {
return updatedGroup
} else {
return group
}
})
setMenuGroups(updatedGroups)
},
[application, menuGroups, note],
)
useEffect(() => {
const fetchListedAccounts = async () => {
if (!application.hasAccount()) {
setIsFetchingAccounts(false)
return
}
try {
const listedAccountEntries = await application.getListedAccounts()
if (!listedAccountEntries.length) {
throw new Error('No Listed accounts found')
}
const menuGroups: ListedMenuGroup[] = []
await Promise.all(
listedAccountEntries.map(async (account) => {
const accountInfo = await application.getListedAccountInfo(account, note.uuid)
if (accountInfo) {
menuGroups.push({
name: accountInfo.display_name,
account,
actions: accountInfo.actions as Action[],
})
} else {
menuGroups.push({
name: account.authorId,
account,
actions: [],
})
}
}),
)
setMenuGroups(
menuGroups.sort((a, b) => {
return a.name.toString().toLowerCase() < b.name.toString().toLowerCase() ? -1 : 1
}),
)
} catch (err) {
console.error(err)
} finally {
setIsFetchingAccounts(false)
setTimeout(() => {
recalculateMenuStyle()
})
}
}
void fetchListedAccounts()
}, [application, note.uuid, recalculateMenuStyle])
return (
<>
{isFetchingAccounts && (
<div className="w-full flex items-center justify-center p-4">
<div className="sk-spinner w-5 h-5 spinner-info" />
</div>
)}
{!isFetchingAccounts && menuGroups.length ? (
<>
{menuGroups.map((group, index) => (
<Fragment key={group.account.authorId}>
<div
className={`w-full flex items-center px-2.5 py-2 text-input font-semibold color-text border-0 border-y-1px border-solid border-main ${
index === 0 ? 'border-t-0 mb-1' : 'my-1'
}`}
>
<Icon type="notes" className="mr-2 color-info" /> {group.name}
</div>
{group.actions.length ? (
group.actions.map((action) => (
<ListedMenuItem
action={action}
note={note}
key={action.url}
group={group}
application={application}
reloadMenuGroup={reloadMenuGroup}
/>
))
) : (
<div className="px-3 py-2 color-passive-0 select-none">No actions available</div>
)}
</Fragment>
))}
</>
) : null}
{!isFetchingAccounts && !menuGroups.length ? (
<div className="w-full flex items-center justify-center px-4 py-6">
<div className="color-passive-0 select-none">No Listed accounts found</div>
</div>
) : null}
</>
)
}
const ListedActionsOption: FunctionComponent<Props> = ({ application, note }) => {
const menuContainerRef = useRef<HTMLDivElement>(null)
const menuRef = useRef<HTMLDivElement>(null)
@@ -249,12 +56,16 @@ const ListedActionsOption: FunctionComponent<Props> = ({ application, note }) =>
return (
<div ref={menuContainerRef}>
<Disclosure open={isMenuOpen} onChange={toggleListedMenu}>
<DisclosureButton ref={menuButtonRef} onBlur={closeOnBlur} className="sn-dropdown-item justify-between">
<DisclosureButton
ref={menuButtonRef}
onBlur={closeOnBlur}
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-menu-item justify-between"
>
<div className="flex items-center">
<Icon type="listed" className="color-neutral mr-2" />
<Icon type="listed" className="text-neutral mr-2" />
Listed actions
</div>
<Icon type="chevron-right" className="color-neutral" />
<Icon type="chevron-right" className="text-neutral" />
</DisclosureButton>
<DisclosurePanel
ref={menuRef}
@@ -262,7 +73,9 @@ const ListedActionsOption: FunctionComponent<Props> = ({ application, note }) =>
...menuStyle,
position: 'fixed',
}}
className="sn-dropdown flex flex-col max-h-120 min-w-68 pb-1 fixed overflow-y-auto"
className={`${
isMenuOpen ? 'flex' : 'hidden'
} flex-col bg-default rounded shadow-main max-h-120 min-w-68 pb-1 fixed overflow-y-auto`}
>
{isMenuOpen && (
<ListedActionsMenu application={application} note={note} recalculateMenuStyle={recalculateMenuStyle} />

View File

@@ -0,0 +1,7 @@
import { Action, ListedAccount } from '@standardnotes/snjs'
export type ListedMenuGroup = {
name: string
account: ListedAccount
actions: Action[]
}

View File

@@ -0,0 +1,59 @@
import { WebApplication } from '@/Application/Application'
import { Action, SNNote } from '@standardnotes/snjs'
import { FunctionComponent, useCallback, useState } from 'react'
import Spinner from '@/Components/Spinner/Spinner'
import { ListedMenuGroup } from './ListedMenuGroup'
type ListedMenuItemProps = {
action: Action
note: SNNote
group: ListedMenuGroup
application: WebApplication
reloadMenuGroup: (group: ListedMenuGroup) => Promise<void>
}
const ListedMenuItem: FunctionComponent<ListedMenuItemProps> = ({
action,
note,
application,
group,
reloadMenuGroup,
}) => {
const [isRunning, setIsRunning] = useState(false)
const handleClick = useCallback(async () => {
if (isRunning) {
return
}
setIsRunning(true)
await application.actionsManager.runAction(action, note)
setIsRunning(false)
reloadMenuGroup(group).catch(console.error)
}, [application, action, group, isRunning, note, reloadMenuGroup])
return (
<button
key={action.url}
onClick={handleClick}
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-2 text-left w-full focus:bg-info-backdrop focus:shadow-none text-sm"
>
<div className="flex flex-col">
<div className="font-semibold">{action.label}</div>
{action.access_type && (
<div className="text-xs mt-0.5 text-passive-0">
{'Uses '}
<strong>{action.access_type}</strong>
{' access to this note.'}
</div>
)}
</div>
{isRunning && <Spinner className="w-3 h-3" />}
</button>
)
}
export default ListedMenuItem

View File

@@ -11,6 +11,7 @@ import AddTagOption from './AddTagOption'
import { addToast, dismissToast, ToastType } from '@standardnotes/toast'
import { NotesOptionsProps } from './NotesOptionsProps'
import { NotesController } from '@/Controllers/NotesController'
import HorizontalSeparator from '../Shared/HorizontalSeparator'
type DeletePermanentlyButtonProps = {
closeOnBlur: NotesOptionsProps['closeOnBlur']
@@ -18,16 +19,20 @@ type DeletePermanentlyButtonProps = {
}
const DeletePermanentlyButton = ({ closeOnBlur, onClick }: DeletePermanentlyButtonProps) => (
<button onBlur={closeOnBlur} className="sn-dropdown-item" onClick={onClick}>
<Icon type="close" className="color-danger mr-2" />
<span className="color-danger">Delete permanently</span>
<button
onBlur={closeOnBlur}
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-menu-item"
onClick={onClick}
>
<Icon type="close" className="text-danger mr-2" />
<span className="text-danger">Delete permanently</span>
</button>
)
const iconClass = 'color-neutral mr-2'
const iconClassDanger = 'color-danger mr-2'
const iconClassWarning = 'color-warning mr-2'
const iconClassSuccess = 'color-success mr-2'
const iconClass = 'text-neutral mr-2'
const iconClassDanger = 'text-danger mr-2'
const iconClassWarning = 'text-warning mr-2'
const iconClassSuccess = 'text-success mr-2'
const getWordCount = (text: string) => {
if (text.trim().length === 0) {
@@ -96,7 +101,7 @@ const NoteAttributes: FunctionComponent<{
const format = editor?.package_info?.file_type || 'txt'
return (
<div className="px-3 pt-1.5 pb-2.5 text-xs color-neutral font-medium">
<div className="px-3 pt-1.5 pb-2.5 text-xs text-neutral font-medium">
{typeof words === 'number' && (format === 'txt' || format === 'md') ? (
<>
<div className="mb-1">
@@ -135,7 +140,7 @@ const SpellcheckOptions: FunctionComponent<{
return (
<div className="flex flex-col">
<button
className="sn-dropdown-item justify-between px-3 py-1"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1 text-left w-full focus:bg-info-backdrop focus:shadow-none text-menu-item justify-between"
onClick={() => {
notesController.toggleGlobalSpellcheckForNote(note).catch(console.error)
}}
@@ -161,8 +166,8 @@ const NoteSizeWarning: FunctionComponent<{
}> = ({ note }) => {
return new Blob([note.text]).size > NOTE_SIZE_WARNING_THRESHOLD ? (
<div className="flex items-center px-3 py-3.5 relative bg-warning-faded">
<Icon type="warning" className="color-accessory-tint-3 flex-shrink-0 mr-3" />
<div className="color-warning select-none leading-140% max-w-80%">
<Icon type="warning" className="text-accessory-tint-3 flex-shrink-0 mr-3" />
<div className="text-warning select-none leading-140% max-w-80%">
This note may have trouble syncing to the mobile application due to its size.
</div>
</div>
@@ -267,15 +272,19 @@ const NotesOptions = ({
<>
{notes.length === 1 && (
<>
<button onBlur={closeOnBlur} className="sn-dropdown-item" onClick={openRevisionHistoryModal}>
<button
onBlur={closeOnBlur}
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-menu-item"
onClick={openRevisionHistoryModal}
>
<Icon type="history" className={iconClass} />
Note history
</button>
<div className="min-h-1px my-2 bg-border"></div>
<HorizontalSeparator classes="my-2" />
</>
)}
<button
className="sn-dropdown-item justify-between"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-menu-item justify-between"
onClick={() => {
notesController.setLockSelectedNotes(!locked)
}}
@@ -288,7 +297,7 @@ const NotesOptions = ({
<Switch className="px-0" checked={locked} />
</button>
<button
className="sn-dropdown-item justify-between"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-menu-item justify-between"
onClick={() => {
notesController.setHideSelectedNotePreviews(!hidePreviews)
}}
@@ -301,7 +310,7 @@ const NotesOptions = ({
<Switch className="px-0" checked={!hidePreviews} />
</button>
<button
className="sn-dropdown-item justify-between"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-menu-item justify-between"
onClick={() => {
notesController.setProtectSelectedNotes(!protect).catch(console.error)
}}
@@ -315,11 +324,11 @@ const NotesOptions = ({
</button>
{notes.length === 1 && (
<>
<div className="min-h-1px my-2 bg-border"></div>
<HorizontalSeparator classes="my-2" />
<ChangeEditorOption application={application} note={notes[0]} />
</>
)}
<div className="min-h-1px my-2 bg-border"></div>
<HorizontalSeparator classes="my-2" />
{navigationController.tagsCount > 0 && (
<AddTagOption
navigationController={navigationController}
@@ -330,7 +339,7 @@ const NotesOptions = ({
{unpinned && (
<button
onBlur={closeOnBlur}
className="sn-dropdown-item"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-menu-item"
onClick={() => {
notesController.setPinSelectedNotes(true)
}}
@@ -342,7 +351,7 @@ const NotesOptions = ({
{pinned && (
<button
onBlur={closeOnBlur}
className="sn-dropdown-item"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-menu-item"
onClick={() => {
notesController.setPinSelectedNotes(false)
}}
@@ -351,36 +360,44 @@ const NotesOptions = ({
Unpin
</button>
)}
<button onBlur={closeOnBlur} className="sn-dropdown-item" onClick={downloadSelectedItems}>
<button
onBlur={closeOnBlur}
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-menu-item"
onClick={downloadSelectedItems}
>
<Icon type="download" className={iconClass} />
Export
</button>
<button onBlur={closeOnBlur} className="sn-dropdown-item" onClick={duplicateSelectedItems}>
<button
onBlur={closeOnBlur}
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-menu-item"
onClick={duplicateSelectedItems}
>
<Icon type="copy" className={iconClass} />
Duplicate
</button>
{unarchived && (
<button
onBlur={closeOnBlur}
className="sn-dropdown-item"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-menu-item"
onClick={() => {
notesController.setArchiveSelectedNotes(true).catch(console.error)
}}
>
<Icon type="archive" className={iconClassWarning} />
<span className="color-warning">Archive</span>
<span className="text-warning">Archive</span>
</button>
)}
{archived && (
<button
onBlur={closeOnBlur}
className="sn-dropdown-item"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-menu-item"
onClick={() => {
notesController.setArchiveSelectedNotes(false).catch(console.error)
}}
>
<Icon type="unarchive" className={iconClassWarning} />
<span className="color-warning">Unarchive</span>
<span className="text-warning">Unarchive</span>
</button>
)}
{notTrashed &&
@@ -394,26 +411,26 @@ const NotesOptions = ({
) : (
<button
onBlur={closeOnBlur}
className="sn-dropdown-item"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-menu-item"
onClick={async () => {
await notesController.setTrashSelectedNotes(true)
}}
>
<Icon type="trash" className={iconClassDanger} />
<span className="color-danger">Move to trash</span>
<span className="text-danger">Move to trash</span>
</button>
))}
{trashed && (
<>
<button
onBlur={closeOnBlur}
className="sn-dropdown-item"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-menu-item"
onClick={async () => {
await notesController.setTrashSelectedNotes(false)
}}
>
<Icon type="restore" className={iconClassSuccess} />
<span className="color-success">Restore</span>
<span className="text-success">Restore</span>
</button>
<DeletePermanentlyButton
closeOnBlur={closeOnBlur}
@@ -423,15 +440,15 @@ const NotesOptions = ({
/>
<button
onBlur={closeOnBlur}
className="sn-dropdown-item"
className="flex items-center border-0 cursor-pointer hover:bg-contrast hover:text-foreground text-text bg-transparent px-3 py-1.5 text-left w-full focus:bg-info-backdrop focus:shadow-none text-menu-item"
onClick={async () => {
await notesController.emptyTrash()
}}
>
<div className="flex items-start">
<Icon type="trash-sweep" className="color-danger mr-2" />
<Icon type="trash-sweep" className="text-danger mr-2" />
<div className="flex-row">
<div className="color-danger">Empty Trash</div>
<div className="text-danger">Empty Trash</div>
<div className="text-xs">{notesController.trashedNotesCount} notes in Trash</div>
</div>
</div>
@@ -440,11 +457,11 @@ const NotesOptions = ({
)}
{notes.length === 1 ? (
<>
<div className="min-h-1px my-2 bg-border"></div>
<HorizontalSeparator classes="my-2" />
<ListedActionsOption application={application} note={notes[0]} />
<div className="min-h-1px my-2 bg-border"></div>
<HorizontalSeparator classes="my-2" />
<SpellcheckOptions editorForNote={editorForNote} notesController={notesController} note={notes[0]} />
<div className="min-h-1px my-2 bg-border"></div>
<HorizontalSeparator classes="my-2" />
<NoteAttributes application={application} note={notes[0]} />
<NoteSizeWarning note={notes[0]} />
</>

View File

@@ -71,7 +71,7 @@ const NotesOptionsPanel = ({
}}
onBlur={closeOnBlur}
ref={buttonRef}
className="sn-icon-button border-contrast"
className="flex justify-center items-center min-w-8 h-8 bg-text-padding hover:bg-contrast focus:bg-contrast text-neutral border border-solid border-border rounded-full cursor-pointer"
>
<VisuallyHidden>Actions</VisuallyHidden>
<Icon type="more" className="block" />
@@ -88,7 +88,9 @@ const NotesOptionsPanel = ({
...position,
maxHeight,
}}
className="sn-dropdown sn-dropdown--animated min-w-80 max-h-120 max-w-xs flex flex-col pt-2 overflow-y-auto fixed"
className={`${
open ? 'flex' : 'hidden'
} flex-col min-w-80 max-h-120 max-w-xs pt-2 fixed bg-default rounded shadow-main transition-transform duration-150 slide-down-animation overflow-y-auto`}
onBlur={closeOnBlur}
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
>

View File

@@ -3,6 +3,7 @@ import { AlertDialog, AlertDialogDescription, AlertDialogLabel } from '@reach/al
import { WebApplication } from '@/Application/Application'
import { ViewControllerManager } from '@/Services/ViewControllerManager'
import { observer } from 'mobx-react-lite'
import Button from '@/Components/Button/Button'
type Props = {
application: WebApplication
@@ -17,7 +18,7 @@ const ConfirmOtherSessionsSignOut = observer(({ application, viewControllerManag
}, [viewControllerManager])
return (
<AlertDialog onDismiss={closeDialog} leastDestructiveRef={cancelRef}>
<AlertDialog onDismiss={closeDialog} leastDestructiveRef={cancelRef} className="p-0 max-w-[600px]">
<div className="sk-modal-content">
<div className="sn-component">
<div className="sk-panel">
@@ -27,18 +28,21 @@ const ConfirmOtherSessionsSignOut = observer(({ application, viewControllerManag
End all other sessions?
</AlertDialogLabel>
<AlertDialogDescription className="sk-panel-row">
<p className="color-foreground">
<p className="text-foreground">
This action will sign out all other devices signed into your account, and remove your data from
those devices when they next regain connection to the internet. You may sign back in on those
devices at any time.
</p>
</AlertDialogDescription>
<div className="flex my-1 mt-4">
<button className="sn-button small neutral" ref={cancelRef} onClick={closeDialog}>
<div className="flex my-1 mt-4 gap-2">
<Button primary small colorStyle="neutral" rounded={false} ref={cancelRef} onClick={closeDialog}>
Cancel
</button>
<button
className="sn-button small danger ml-2"
</Button>
<Button
primary
small
colorStyle="danger"
rounded={false}
onClick={() => {
application.revokeAllOtherSessions().catch(console.error)
closeDialog()
@@ -48,7 +52,7 @@ const ConfirmOtherSessionsSignOut = observer(({ application, viewControllerManag
}}
>
End Sessions
</button>
</Button>
</div>
</div>
</div>

View File

@@ -1,6 +1,8 @@
import { WebApplication } from '@/Application/Application'
import { ChangeEventHandler, createRef } from 'react'
import { createRef } from 'react'
import { PureComponent } from '@/Components/Abstract/PureComponent'
import Button from '@/Components/Button/Button'
import DecoratedPasswordInput from '../Input/DecoratedPasswordInput'
interface Props {
application: WebApplication
@@ -202,21 +204,21 @@ class PasswordWizard extends PureComponent<Props, State> {
})
}
handleCurrentPasswordInputChange: ChangeEventHandler<HTMLInputElement> = ({ currentTarget }) => {
handleCurrentPasswordInputChange = (currentPassword: string) => {
this.setFormDataState({
currentPassword: currentTarget.value,
currentPassword,
}).catch(console.error)
}
handleNewPasswordInputChange: ChangeEventHandler<HTMLInputElement> = ({ currentTarget }) => {
handleNewPasswordInputChange = (newPassword: string) => {
this.setFormDataState({
newPassword: currentTarget.value,
newPassword,
}).catch(console.error)
}
handleNewPasswordConfirmationInputChange: ChangeEventHandler<HTMLInputElement> = ({ currentTarget }) => {
handleNewPasswordConfirmationInputChange = (newPasswordConfirmation: string) => {
this.setFormDataState({
newPasswordConfirmation: currentTarget.value,
newPasswordConfirmation,
}).catch(console.error)
}
@@ -244,13 +246,12 @@ class PasswordWizard extends PureComponent<Props, State> {
Current Password
</label>
<input
<DecoratedPasswordInput
ref={this.currentPasswordInput}
id="password-wiz-current-password"
value={this.state.formData.currentPassword}
onChange={this.handleCurrentPasswordInputChange}
type="password"
className="sk-input contrast"
/>
<div className="sk-panel-row" />
@@ -259,12 +260,11 @@ class PasswordWizard extends PureComponent<Props, State> {
New Password
</label>
<input
<DecoratedPasswordInput
id="password-wiz-new-password"
value={this.state.formData.newPassword}
onChange={this.handleNewPasswordInputChange}
type="password"
className="sk-input contrast"
/>
<div className="sk-panel-row" />
@@ -272,12 +272,11 @@ class PasswordWizard extends PureComponent<Props, State> {
Confirm New Password
</label>
<input
<DecoratedPasswordInput
id="password-wiz-confirm-new-password"
value={this.state.formData.newPasswordConfirmation}
onChange={this.handleNewPasswordConfirmationInputChange}
type="password"
className="sk-input contrast"
/>
</form>
</div>
@@ -286,7 +285,7 @@ class PasswordWizard extends PureComponent<Props, State> {
)}
{this.state.step === Steps.FinishStep && (
<div className="sk-panel-section">
<div className="sk-label sk-bold info">Your password has been successfully changed.</div>
<div className="font-bold text-info mb-1">Your password has been successfully changed.</div>
<p className="sk-p">
Please ensure you are running the latest version of Standard Notes on all platforms to ensure
maximum compatibility.
@@ -295,13 +294,9 @@ class PasswordWizard extends PureComponent<Props, State> {
)}
</div>
<div className="sk-panel-footer">
<button
onClick={this.nextStep}
disabled={this.state.lockContinue}
className="sn-button min-w-20 info"
>
<Button primary onClick={this.nextStep} disabled={this.state.lockContinue} className="min-w-20">
{this.state.continueTitle}
</button>
</Button>
</div>
</div>
</div>

View File

@@ -1,6 +1,11 @@
import { WebApplication } from '@/Application/Application'
import { SNComponent } from '@standardnotes/snjs'
import { Component } from 'react'
import Button from '@/Components/Button/Button'
import ModalDialog from '../Shared/ModalDialog'
import ModalDialogLabel from '../Shared/ModalDialogLabel'
import ModalDialogDescription from '../Shared/ModalDialogDescription'
import ModalDialogButtons from '../Shared/ModalDialogButtons'
interface Props {
application: WebApplication
@@ -23,50 +28,29 @@ class PermissionsModal extends Component<Props> {
override render() {
return (
<div className="sk-modal">
<div onClick={this.deny} className="sk-modal-background" />
<div id="permissions-modal" className="sk-modal-content">
<div className="sn-component">
<div className="sk-panel">
<div className="sk-panel-header">
<div className="sk-panel-header-title">Activate Component</div>
<a onClick={this.deny} className="sk-a info close-button">
Cancel
</a>
</div>
<div className="sk-panel-content">
<div className="sk-panel-section">
<div className="sk-panel-row">
<div className="sk-h2">
<strong>{this.props.component.displayName}</strong>
{' would like to interact with your '}
{this.props.permissionsString}
</div>
</div>
<div className="sk-panel-row">
<p className="sk-p">
Components use an offline messaging system to communicate. Learn more at{' '}
<a
href="https://standardnotes.com/permissions"
rel="noopener"
target="_blank"
className="sk-a info"
>
https://standardnotes.com/permissions.
</a>
</p>
</div>
</div>
</div>
<div className="sk-panel-footer">
<button onClick={this.accept} className="sn-button info block w-full text-base py-3">
Continue
</button>
</div>
</div>
<ModalDialog className="w-[350px]">
<ModalDialogLabel closeDialog={this.deny}>Activate Component</ModalDialogLabel>
<ModalDialogDescription>
<div className="text-base">
<strong>{this.props.component.displayName}</strong>
{' would like to interact with your '}
{this.props.permissionsString}
</div>
</div>
</div>
<div className="sk-panel-row">
<p className="sk-p">
Components use an offline messaging system to communicate. Learn more at{' '}
<a href="https://standardnotes.com/permissions" rel="noopener" target="_blank" className="sk-a info">
https://standardnotes.com/permissions.
</a>
</p>
</div>
</ModalDialogDescription>
<ModalDialogButtons>
<Button primary fullWidth onClick={this.accept} className="block">
Continue
</Button>
</ModalDialogButtons>
</ModalDialog>
)
}
}

View File

@@ -26,7 +26,12 @@ const PinNoteButton: FunctionComponent<Props> = ({ className = '', notesControll
}, [onClickPreprocessing, pinned, notesController])
return (
<button className={`sn-icon-button border-contrast ${pinned ? 'toggled' : ''} ${className}`} onClick={togglePinned}>
<button
className={`sn-icon-button flex justify-center items-center min-w-8 h-8 hover:bg-contrast focus:bg-contrast text-neutral border border-solid border-border rounded-full cursor-pointer ${
pinned ? 'toggled' : ''
} ${className}`}
onClick={togglePinned}
>
<VisuallyHidden>Pin selected notes</VisuallyHidden>
<Icon type="pin" className="block" />
</button>

View File

@@ -36,10 +36,10 @@ const Authentication: FunctionComponent<Props> = ({ viewControllerManager }) =>
<Text className="text-center mb-3">
Sign in to sync your notes and preferences across all your devices and enable end-to-end encryption.
</Text>
<Button variant="primary" label="Create free account" onClick={clickRegister} className="mb-3" />
<div className="text-input">
<Button primary label="Create free account" onClick={clickRegister} className="mb-3" />
<div className="text-sm">
Already have an account?{' '}
<button className="border-0 p-0 bg-default color-info underline cursor-pointer" onClick={clickSignIn}>
<button className="border-0 p-0 bg-default text-info underline cursor-pointer" onClick={clickSignIn}>
Sign in
</button>
</div>

View File

@@ -142,14 +142,14 @@ const ChangeEmail: FunctionComponent<Props> = ({ onCloseDialog, application }) =
<div>
<ModalDialog>
<ModalDialogLabel closeDialog={handleDialogClose}>Change Email</ModalDialogLabel>
<ModalDialogDescription className="px-4.5">
<ModalDialogDescription className="px-4.5 flex flex-row items-center">
{currentStep === Steps.InitialStep && (
<ChangeEmailForm setNewEmail={setNewEmail} setCurrentPassword={setCurrentPassword} />
)}
{currentStep === Steps.FinishStep && <ChangeEmailSuccess />}
</ModalDialogDescription>
<ModalDialogButtons className="px-4.5">
<Button className="min-w-20" variant="primary" label={submitButtonTitle} onClick={handleSubmit} />
<Button className="min-w-20" primary label={submitButtonTitle} onClick={handleSubmit} />
</ModalDialogButtons>
</ModalDialog>
</div>

View File

@@ -1,3 +1,5 @@
import DecoratedInput from '@/Components/Input/DecoratedInput'
import DecoratedPasswordInput from '@/Components/Input/DecoratedPasswordInput'
import { Dispatch, SetStateAction, FunctionComponent } from 'react'
type Props = {
@@ -7,8 +9,6 @@ type Props = {
const labelClassName = 'block mb-1'
const inputClassName = 'sk-input contrast'
const ChangeEmailForm: FunctionComponent<Props> = ({ setNewEmail, setCurrentPassword }) => {
return (
<div className="w-full flex flex-col">
@@ -16,12 +16,11 @@ const ChangeEmailForm: FunctionComponent<Props> = ({ setNewEmail, setCurrentPass
<label className={labelClassName} htmlFor="change-email-email-input">
New Email:
</label>
<input
id="change-email-email-input"
className={inputClassName}
<DecoratedInput
type="email"
onChange={({ target }) => {
setNewEmail((target as HTMLInputElement).value)
id="change-email-email-input"
onChange={(newEmail) => {
setNewEmail(newEmail)
}}
/>
</div>
@@ -29,12 +28,11 @@ const ChangeEmailForm: FunctionComponent<Props> = ({ setNewEmail, setCurrentPass
<label className={labelClassName} htmlFor="change-email-password-input">
Current Password:
</label>
<input
<DecoratedPasswordInput
id="change-email-password-input"
className={inputClassName}
type="password"
onChange={({ target }) => {
setCurrentPassword((target as HTMLInputElement).value)
onChange={(currentPassword) => {
setCurrentPassword(currentPassword)
}}
/>
</div>

View File

@@ -3,8 +3,8 @@ import { FunctionComponent } from 'react'
const ChangeEmailSuccess: FunctionComponent = () => {
return (
<div>
<div className={'sk-label sk-bold info mt-2'}>Your email has been successfully changed.</div>
<p className={'sk-p'}>
<div className={'font-bold text-info mb-2'}>Your email has been successfully changed.</div>
<p>
Please ensure you are running the latest version of Standard Notes on all platforms to ensure maximum
compatibility.
</p>

View File

@@ -13,10 +13,9 @@ const ClearSessionDataView: FunctionComponent<{
<PreferencesGroup>
<PreferencesSegment>
<Title>Clear workspace</Title>
<Text>Remove all data related to the current workspace from the application.</Text>
<div className="min-h-3" />
<Text className="mb-3">Remove all data related to the current workspace from the application.</Text>
<Button
dangerStyle={true}
colorStyle="danger"
label="Clear workspace"
onClick={() => {
viewControllerManager.accountMenuController.setSigningOut(true)

View File

@@ -44,7 +44,6 @@ const Credentials: FunctionComponent<Props> = ({ application }: Props) => {
</Text>
<Button
className="min-w-20 mt-3"
variant="normal"
label="Change email"
onClick={() => {
setIsChangeEmailDialogOpen(true)
@@ -55,7 +54,7 @@ const Credentials: FunctionComponent<Props> = ({ application }: Props) => {
<Text>
Current password was set on <span className="font-bold">{passwordCreatedOn}</span>
</Text>
<Button className="min-w-20 mt-3" variant="normal" label="Change password" onClick={presentPasswordWizard} />
<Button className="min-w-20 mt-3" label="Change password" onClick={presentPasswordWizard} />
{isChangeEmailDialogOpen && (
<ChangeEmail onCloseDialog={() => setIsChangeEmailDialogOpen(false)} application={application} />
)}

Some files were not shown because too many files have changed in this diff Show More