Skip to content

Commit 7bce025

Browse files
authored
feat: Added image alignment options and preference in Super notes (#2903)
1 parent 57cb844 commit 7bce025

19 files changed

Lines changed: 442 additions & 131 deletions

File tree

packages/models/src/Domain/Syncable/UserPrefs/PrefDefaults.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export const PrefDefaults = {
4343
[PrefKey.SuperNoteExportEmbedBehavior]: 'reference',
4444
[PrefKey.SuperNoteExportUseMDFrontmatter]: true,
4545
[PrefKey.SuperNoteExportPDFPageSize]: 'A4',
46+
[PrefKey.SuperNoteImageAlignment]: 'left',
4647
[PrefKey.SystemViewPreferences]: {},
4748
[PrefKey.AuthenticatorNames]: '',
4849
[PrefKey.ComponentPreferences]: {},

packages/models/src/Domain/Syncable/UserPrefs/PrefKey.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export enum PrefKey {
3636
SuperNoteExportEmbedBehavior = 'superNoteExportEmbedBehavior',
3737
SuperNoteExportUseMDFrontmatter = 'superNoteExportUseMDFrontmatter',
3838
SuperNoteExportPDFPageSize = 'superNoteExportPDFPageSize',
39+
SuperNoteImageAlignment = 'superNoteImageAlignment',
3940
AuthenticatorNames = 'authenticatorNames',
4041
PaneGesturesEnabled = 'paneGesturesEnabled',
4142
ComponentPreferences = 'componentPreferences',
@@ -101,4 +102,5 @@ export type PrefValue = {
101102
[PrefKey.AddImportsToTag]: boolean
102103
[PrefKey.AlwaysCreateNewTagForImports]: boolean
103104
[PrefKey.ExistingTagForImports]: string | undefined
105+
[PrefKey.SuperNoteImageAlignment]: 'left' | 'center' | 'right'
104106
}
Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { FunctionComponent, MouseEventHandler } from 'react'
1+
import { ComponentPropsWithoutRef, ForwardedRef, forwardRef, MouseEventHandler } from 'react'
22
import Icon from '@/Components/Icon/Icon'
33
import { IconType } from '@standardnotes/snjs'
44

5-
type Props = {
5+
interface Props extends ComponentPropsWithoutRef<'button'> {
66
onClick: MouseEventHandler<HTMLButtonElement>
77
className?: string
88
icon: IconType
@@ -12,32 +12,31 @@ type Props = {
1212
disabled?: boolean
1313
}
1414

15-
const IconButton: FunctionComponent<Props> = ({
16-
onClick,
17-
className = '',
18-
icon,
19-
title,
20-
focusable,
21-
iconClassName = '',
22-
disabled = false,
23-
}) => {
24-
const click: MouseEventHandler<HTMLButtonElement> = (e) => {
25-
e.preventDefault()
26-
onClick(e)
27-
}
28-
const focusableClass = focusable ? '' : 'focus:shadow-none'
29-
return (
30-
<button
31-
type="button"
32-
title={title}
33-
className={`no-border flex cursor-pointer flex-row items-center bg-transparent ${focusableClass} ${className}`}
34-
onClick={click}
35-
disabled={disabled}
36-
aria-label={title}
37-
>
38-
<Icon type={icon} className={iconClassName} />
39-
</button>
40-
)
41-
}
15+
const IconButton = forwardRef(
16+
(
17+
{ onClick, className = '', icon, title, focusable, iconClassName = '', disabled = false, ...rest }: Props,
18+
ref: ForwardedRef<HTMLButtonElement>,
19+
) => {
20+
const click: MouseEventHandler<HTMLButtonElement> = (e) => {
21+
e.preventDefault()
22+
onClick(e)
23+
}
24+
const focusableClass = focusable ? '' : 'focus:shadow-none'
25+
return (
26+
<button
27+
{...rest}
28+
type="button"
29+
title={title}
30+
className={`no-border flex cursor-pointer flex-row items-center bg-transparent ${focusableClass} ${className}`}
31+
onClick={click}
32+
disabled={disabled}
33+
aria-label={title}
34+
ref={ref}
35+
>
36+
<Icon type={icon} className={iconClassName} />
37+
</button>
38+
)
39+
},
40+
)
4241

4342
export default IconButton

packages/web/src/javascripts/Components/FilePreview/FilePreview.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,23 @@ import { isFileTypePreviewable } from './isFilePreviewable'
1313
import PreviewComponent from './PreviewComponent'
1414
import Button from '../Button/Button'
1515
import { ProtectedIllustration } from '@standardnotes/icons'
16-
import { ImageZoomLevelProps } from './ImageZoomLevelProps'
16+
import { OptionalSuperEmbeddedImageProps } from './OptionalSuperEmbeddedImageProps'
1717

1818
type Props = {
1919
application: WebApplication
2020
file: FileItem
2121
isEmbeddedInSuper?: boolean
22-
} & ImageZoomLevelProps
23-
24-
const FilePreview = ({ file, application, isEmbeddedInSuper = false, imageZoomLevel, setImageZoomLevel }: Props) => {
22+
} & OptionalSuperEmbeddedImageProps
23+
24+
const FilePreview = ({
25+
file,
26+
application,
27+
isEmbeddedInSuper = false,
28+
imageZoomLevel,
29+
setImageZoomLevel,
30+
alignment,
31+
changeAlignment,
32+
}: Props) => {
2533
const [isAuthorized, setIsAuthorized] = useState(application.isAuthorizedToRenderItem(file))
2634

2735
const isFilePreviewable = useMemo(() => {
@@ -137,6 +145,8 @@ const FilePreview = ({ file, application, isEmbeddedInSuper = false, imageZoomLe
137145
isEmbeddedInSuper={isEmbeddedInSuper}
138146
imageZoomLevel={imageZoomLevel}
139147
setImageZoomLevel={setImageZoomLevel}
148+
alignment={alignment}
149+
changeAlignment={changeAlignment}
140150
/>
141151
) : (
142152
<FilePreviewError
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { classNames, IconType } from '@standardnotes/snjs'
2+
import IconButton from '@/Components/Button/IconButton'
3+
import StyledTooltip from '@/Components/StyledTooltip/StyledTooltip'
4+
import { ElementFormatType } from 'lexical'
5+
6+
export function getCSSValueFromAlignment(format: ElementFormatType) {
7+
switch (format) {
8+
case 'start':
9+
case 'left':
10+
return 'start'
11+
case 'right':
12+
case 'end':
13+
return 'end'
14+
default:
15+
return 'center'
16+
}
17+
}
18+
19+
const Options = [
20+
{
21+
alignment: 'left',
22+
label: 'Left align',
23+
},
24+
{
25+
alignment: 'center',
26+
label: 'Center align',
27+
},
28+
{
29+
alignment: 'right',
30+
label: 'Right align',
31+
},
32+
]
33+
34+
export function ImageAlignmentOptions({
35+
alignment: currentAlignment,
36+
changeAlignment,
37+
}: {
38+
alignment: ElementFormatType
39+
changeAlignment: (format: ElementFormatType) => void
40+
}) {
41+
return Options.map(({ alignment, label }) => (
42+
<StyledTooltip label={label} key={alignment}>
43+
<IconButton
44+
className={classNames(
45+
alignment === currentAlignment && '!bg-info text-info-contrast',
46+
'rounded p-1 hover:bg-contrast',
47+
)}
48+
icon={`format-align-${alignment}` as IconType}
49+
title={label}
50+
focusable={true}
51+
onClick={(e) => {
52+
// the preventDefault and stopPropagation for these events are required
53+
// so that the keyboard doesn't jump when you select another option
54+
e.preventDefault()
55+
e.stopPropagation()
56+
changeAlignment(alignment as ElementFormatType)
57+
}}
58+
onMouseDown={(e) => {
59+
e.preventDefault()
60+
}}
61+
/>
62+
</StyledTooltip>
63+
))
64+
}

0 commit comments

Comments
 (0)