diff --git a/packages/frontend/component/src/ui/button/dropdown-button.tsx b/packages/frontend/component/src/ui/button/dropdown-button.tsx index c20dfb6647..3c4a4fd085 100644 --- a/packages/frontend/component/src/ui/button/dropdown-button.tsx +++ b/packages/frontend/component/src/ui/button/dropdown-button.tsx @@ -1,4 +1,5 @@ import { ArrowDownSmallIcon } from '@blocksuite/icons/rc'; +import clsx from 'clsx'; import type { ButtonHTMLAttributes, MouseEventHandler } from 'react'; import { forwardRef } from 'react'; @@ -12,28 +13,33 @@ type DropdownButtonProps = { export const DropdownButton = forwardRef< HTMLButtonElement, DropdownButtonProps ->(({ onClickDropDown, children, size = 'default', ...props }, ref) => { - const handleClickDropDown: MouseEventHandler = e => { - e.stopPropagation(); - onClickDropDown?.(e); - }; - return ( - - ); -}); +>( + ( + { onClickDropDown, children, size = 'default', className, ...props }, + ref + ) => { + const handleClickDropDown: MouseEventHandler = e => { + e.stopPropagation(); + onClickDropDown?.(e); + }; + return ( + + ); + } +); DropdownButton.displayName = 'DropdownButton'; diff --git a/packages/frontend/component/src/ui/masonry/masonry.stories.tsx b/packages/frontend/component/src/ui/masonry/masonry.stories.tsx index 0561f4b868..82775b886b 100644 --- a/packages/frontend/component/src/ui/masonry/masonry.stories.tsx +++ b/packages/frontend/component/src/ui/masonry/masonry.stories.tsx @@ -279,3 +279,34 @@ export const MultiViewTransition = () => { ); }; + +const availableRatios = [0.8, 1.2, 1.4, 1.5, 1.6, 1.7, 1.8]; +const ratioItems = Array.from({ length: 10000 }, (_, i) => { + const ratio = + availableRatios[Math.floor(Math.random() * availableRatios.length)]; + return { + id: i.toString(), + ratio, + children: ( + + {i}
ratio: {ratio} +
+ ), + }; +}); +export const HeightByRatio = () => { + return ( + + + + ); +}; diff --git a/packages/frontend/component/src/ui/masonry/masonry.tsx b/packages/frontend/component/src/ui/masonry/masonry.tsx index a99e054e7e..cb49362772 100644 --- a/packages/frontend/component/src/ui/masonry/masonry.tsx +++ b/packages/frontend/component/src/ui/masonry/masonry.tsx @@ -286,7 +286,6 @@ export const Masonry = ({ })}
- {stickyGroup ? (
) : null} + ); }; -interface MasonryItemProps - extends MasonryItem, - Omit, 'id' | 'height'> { - locateMode?: 'transform' | 'leftTop' | 'transform3d'; - xywh?: MasonryItemXYWH; -} +type MasonryItemProps = MasonryItem & + Omit, 'id' | 'height'> & { + locateMode?: 'transform' | 'leftTop' | 'transform3d'; + xywh?: MasonryItemXYWH; + }; const MasonryGroupHeader = memo(function MasonryGroupHeader({ id, @@ -330,7 +329,6 @@ const MasonryGroupHeader = memo(function MasonryGroupHeader({ groupId, itemCount, collapsed, - height, paddingX, ...props }: Omit & { @@ -356,7 +354,6 @@ const MasonryGroupHeader = memo(function MasonryGroupHeader({ return ( ) { const style = useMemo(() => { if (!xywh) return { display: 'none' }; diff --git a/packages/frontend/component/src/ui/masonry/styles.css.ts b/packages/frontend/component/src/ui/masonry/styles.css.ts index c67dcd1772..c61415524d 100644 --- a/packages/frontend/component/src/ui/masonry/styles.css.ts +++ b/packages/frontend/component/src/ui/masonry/styles.css.ts @@ -20,6 +20,9 @@ export const stickyGroupHeader = style({ top: 0, width: '100%', }); +export const scrollbar = style({ + zIndex: 1, +}); export const item = style({ position: 'absolute', diff --git a/packages/frontend/component/src/ui/masonry/type.ts b/packages/frontend/component/src/ui/masonry/type.ts index c10d281be8..460c394863 100644 --- a/packages/frontend/component/src/ui/masonry/type.ts +++ b/packages/frontend/component/src/ui/masonry/type.ts @@ -1,8 +1,7 @@ -export interface MasonryItem extends React.HTMLAttributes { +export type MasonryItem = React.HTMLAttributes & { id: string; - height: number; Component?: React.ComponentType<{ groupId: string; itemId: string }>; -} +} & ({ height: number } | { ratio: number }); export interface MasonryGroup extends React.HTMLAttributes { id: string; diff --git a/packages/frontend/component/src/ui/masonry/utils.ts b/packages/frontend/component/src/ui/masonry/utils.ts index 13da3b036d..5f17bf3202 100644 --- a/packages/frontend/component/src/ui/masonry/utils.ts +++ b/packages/frontend/component/src/ui/masonry/utils.ts @@ -82,6 +82,7 @@ export const calcLayout = ( groups.forEach((group, index) => { const heightStack = Array.from({ length: columns }, () => 0); + const ratioStack = Array.from({ length: columns }, () => 0); if (index !== 0) { finalHeight += groupsGap; } @@ -101,24 +102,52 @@ export const calcLayout = ( return; } - finalHeight += groupHeaderLayout.h + groupHeaderGapWithItems; + finalHeight += + groupHeaderLayout.h + + // if group header is empty, don't add gap + (groupHeaderLayout.h > 0 ? groupHeaderGapWithItems : 0); // calculate group items group.items.forEach(item => { const itemId = group.id ? `${group.id}:${item.id}` : item.id; - const minHeight = Math.min(...heightStack); - const minHeightIndex = heightStack.indexOf(minHeight); - const hasGap = heightStack[minHeightIndex] ? gapY : 0; - const x = minHeightIndex * (width + gapX) + paddingX; - const y = finalHeight + minHeight + hasGap; + const ratioMode = 'ratio' in item; + const height = ratioMode ? item.ratio * width : item.height; - heightStack[minHeightIndex] += item.height + hasGap; - layout.set(itemId, { - type: 'item', - x, - y, - w: width, - h: item.height, - }); + if (ratioMode) { + const minRatio = Math.min(...ratioStack); + const minRatioIndex = ratioStack.indexOf(minRatio); + const minHeight = heightStack[minRatioIndex]; + const hasGap = heightStack[minRatioIndex] ? gapY : 0; + const x = minRatioIndex * (width + gapX) + paddingX; + const y = finalHeight + minHeight + hasGap; + + ratioStack[minRatioIndex] += item.ratio * 10000; + heightStack[minRatioIndex] += height + hasGap; + layout.set(itemId, { + type: 'item', + x, + y, + w: width, + h: height, + }); + } else { + const minHeight = Math.min(...heightStack); + const minHeightIndex = heightStack.indexOf(minHeight); + const hasGap = heightStack[minHeightIndex] ? gapY : 0; + const x = minHeightIndex * (width + gapX) + paddingX; + const y = finalHeight + minHeight + hasGap; + + const ratio = height / width; + heightStack[minHeightIndex] += height + hasGap; + ratioStack[minHeightIndex] += ratio * 10000; + + layout.set(itemId, { + type: 'item', + x, + y, + w: width, + h: height, + }); + } }); const groupHeight = Math.max(...heightStack) + paddingY; diff --git a/packages/frontend/core/src/components/explorer/context.ts b/packages/frontend/core/src/components/explorer/context.ts index cdf8106d5c..12599145dc 100644 --- a/packages/frontend/core/src/components/explorer/context.ts +++ b/packages/frontend/core/src/components/explorer/context.ts @@ -49,6 +49,9 @@ export const createDocExplorerContext = ( selectedDocIds$: new LiveData([]), prevCheckAnchorId$: new LiveData(null), displayPreference$: displayPreference$, + showDragHandle$: displayPreference$.selector( + displayPreference => displayPreference.showDragHandle + ), view$: displayPreference$.selector( displayPreference => displayPreference.view ), @@ -85,9 +88,6 @@ export const createDocExplorerContext = ( showMoreOperation$: displayPreference$.selector( displayPreference => displayPreference.showMoreOperation ), - showDragHandle$: displayPreference$.selector( - displayPreference => displayPreference.showDragHandle - ), quickDeletePermanently$: displayPreference$.selector( displayPreference => displayPreference.quickDeletePermanently ), diff --git a/packages/frontend/core/src/components/explorer/display-menu/group.tsx b/packages/frontend/core/src/components/explorer/display-menu/group.tsx index 377e1e4173..f97bf0daef 100644 --- a/packages/frontend/core/src/components/explorer/display-menu/group.tsx +++ b/packages/frontend/core/src/components/explorer/display-menu/group.tsx @@ -1,4 +1,4 @@ -import { MenuItem } from '@affine/component'; +import { Divider, MenuItem } from '@affine/component'; import type { GroupByParams } from '@affine/core/modules/collection-rules/types'; import { WorkspacePropertyService } from '@affine/core/modules/workspace-property'; import { useI18n } from '@affine/i18n'; @@ -48,8 +48,9 @@ export const GroupByList = ({ onChange, }: { groupBy?: GroupByParams; - onChange?: (next: GroupByParams) => void; + onChange?: (next: GroupByParams | undefined) => void; }) => { + const t = useI18n(); const workspacePropertyService = useService(WorkspacePropertyService); const propertyList = useLiveData(workspacePropertyService.sortedProperties$); const explorerPropertyList = useMemo(() => { @@ -66,6 +67,10 @@ export const GroupByList = ({ onChange={onChange} /> ))} + + onChange?.(undefined)}> + {t['com.affine.explorer.display-menu.grouping.remove']()} + ); }; diff --git a/packages/frontend/core/src/components/explorer/display-menu/index.tsx b/packages/frontend/core/src/components/explorer/display-menu/index.tsx index 3f896a7e1f..00347510be 100644 --- a/packages/frontend/core/src/components/explorer/display-menu/index.tsx +++ b/packages/frontend/core/src/components/explorer/display-menu/index.tsx @@ -32,7 +32,7 @@ const ExplorerDisplayMenu = ({ const t = useI18n(); const handleGroupByChange = useCallback( - (groupBy: GroupByParams) => { + (groupBy: GroupByParams | undefined) => { onDisplayPreferenceChange({ ...displayPreference, groupBy }); }, [displayPreference, onDisplayPreferenceChange] diff --git a/packages/frontend/core/src/components/explorer/docs-view/doc-list-item.css.ts b/packages/frontend/core/src/components/explorer/docs-view/doc-list-item.css.ts index f6be756660..b2914a2105 100644 --- a/packages/frontend/core/src/components/explorer/docs-view/doc-list-item.css.ts +++ b/packages/frontend/core/src/components/explorer/docs-view/doc-list-item.css.ts @@ -159,6 +159,14 @@ export const listHide560 = style({ export const cardViewRoot = style({ vars: { '--ring-color': 'transparent', + '--light-shadow': + '0px 0px 0px 1px var(--ring-color), 0px 2px 3px rgba(0,0,0,.05)', + '--dark-shadow': + '0px 0px 0px 1px var(--ring-color), 0px 2px 3px rgba(0,0,0,.05)', + '--light-shadow-hover': + '0px 0px 0px 1px var(--ring-color), 0px 4px 6px rgba(0,0,0,.1)', + '--dark-shadow-hover': + '0px 0px 0px 1px var(--ring-color), 0px 4px 6px rgba(0,0,0,.1)', }, width: '100%', height: '100%', @@ -170,14 +178,30 @@ export const cardViewRoot = style({ backgroundColor: cssVarV2.layer.background.mobile.secondary, border: `0.5px solid ${cssVarV2.layer.insideBorder.border}`, // TODO: use variable - boxShadow: '0px 0px 0px 1px var(--ring-color),0px 0px 3px rgba(0,0,0,.05)', + boxShadow: '0px 0px 0px 1px var(--ring-color), 0px 0px 3px rgba(0,0,0,.05)', overflow: 'hidden', + transition: 'box-shadow 0.23s ease, border-color 0.23s ease', selectors: { [`${root}[data-selected="true"] &`]: { vars: { '--ring-color': cssVarV2.layer.insideBorder.primaryBorder, }, }, + '&:hover': { + borderColor: cssVarV2.pagelist.hoverBorder, + }, + '[data-theme="light"] &': { + boxShadow: 'var(--light-shadow)', + }, + '[data-theme="light"] &:hover': { + boxShadow: 'var(--light-shadow-hover)', + }, + '[data-theme="dark"] &': { + boxShadow: 'var(--dark-shadow)', + }, + '[data-theme="dark"] &:hover': { + boxShadow: 'var(--dark-shadow-hover)', + }, }, }); export const cardViewHeader = style({ @@ -224,3 +248,17 @@ export const cardViewCheckbox = style({ color: cssVarV2.icon.primary, pointerEvents: 'none', }); +export const cardDragHandle = style([ + dragHandle, + { + left: -4, + top: 0, + transform: 'translateX(-100%)', + opacity: 0, + selectors: { + [`${cardViewRoot}:hover &`]: { + opacity: 1, + }, + }, + }, +]); diff --git a/packages/frontend/core/src/components/explorer/docs-view/doc-list-item.tsx b/packages/frontend/core/src/components/explorer/docs-view/doc-list-item.tsx index 543a6ae0f7..d405fbb748 100644 --- a/packages/frontend/core/src/components/explorer/docs-view/doc-list-item.tsx +++ b/packages/frontend/core/src/components/explorer/docs-view/doc-list-item.tsx @@ -1,9 +1,9 @@ import { Checkbox, DragHandle as DragHandleIcon, - Skeleton, Tooltip, useDraggable, + Wrapper, } from '@affine/component'; import { DocsService } from '@affine/core/modules/doc'; import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta'; @@ -23,7 +23,6 @@ import { type SVGProps, useCallback, useContext, - useState, } from 'react'; import { PagePreview } from '../../page-list/page-content-preview'; @@ -312,9 +311,6 @@ export const ListViewDoc = ({ docId }: DocListItemProps) => { const t = useI18n(); const docsService = useService(DocsService); const doc = useLiveData(docsService.list.doc$(docId)); - const [previewSkeletonWidth] = useState( - Math.round(100 + Math.random() * 100) - ); if (!doc) { return null; @@ -334,7 +330,7 @@ export const ListViewDoc = ({ docId }: DocListItemProps) => { } + loading={} />
@@ -360,19 +356,13 @@ const cardMoreMenuContentOptions = { sideOffset: 12, alignOffset: -4, } as const; -const randomPreviewSkeleton = () => { - return Array.from({ length: Math.floor(Math.random() * 2) + 1 }, (_, i) => ({ - id: i, - width: Math.round(30 + Math.random() * 70), - })); -}; + export const CardViewDoc = ({ docId }: DocListItemProps) => { const t = useI18n(); const contextValue = useContext(DocExplorerContext); const selectMode = useLiveData(contextValue.selectMode$); const docsService = useService(DocsService); const doc = useLiveData(docsService.list.doc$(docId)); - const [previewSkeleton] = useState(randomPreviewSkeleton); if (!doc) { return null; @@ -380,6 +370,7 @@ export const CardViewDoc = ({ docId }: DocListItemProps) => { return (
  • +
    { /> )}
    - - {previewSkeleton.map(({ id, width }) => ( - - ))} -
  • - } - /> + ); diff --git a/packages/frontend/core/src/components/explorer/docs-view/docs-list.tsx b/packages/frontend/core/src/components/explorer/docs-view/docs-list.tsx index d0f8a09081..30964f73b7 100644 --- a/packages/frontend/core/src/components/explorer/docs-view/docs-list.tsx +++ b/packages/frontend/core/src/components/explorer/docs-view/docs-list.tsx @@ -1,4 +1,9 @@ -import { Masonry, type MasonryGroup, useConfirmModal } from '@affine/component'; +import { + Masonry, + type MasonryGroup, + type MasonryItem, + useConfirmModal, +} from '@affine/component'; import { DocsService } from '@affine/core/modules/doc'; import { WorkspacePropertyService } from '@affine/core/modules/workspace-property'; import { Trans, useI18n } from '@affine/i18n'; @@ -69,15 +74,13 @@ const GroupHeader = memo(function GroupHeader({ return header; }); -const calcCardHeightById = (id: string, base = 250, scale = 10) => { +const ratios = [1.26, 1.304, 1.13, 1.391, 1.521]; +const calcCardRatioById = (id: string) => { if (!id) { - return base; + return ratios[0]; } - const max = 5; - const min = 1; const code = id.charCodeAt(0); - const value = Math.floor((code % (max - min)) + min); - return base + value * scale; + return ratios[code % ratios.length]; }; export const DocListItemComponent = memo(function DocListItemComponent({ @@ -94,16 +97,12 @@ export const DocsExplorer = ({ className, disableMultiDelete, masonryItemWidthMin, - heightBase, - heightScale, onRestore, onDelete, }: { className?: string; disableMultiDelete?: boolean; masonryItemWidthMin?: number; - heightBase?: number; - heightScale?: number; onRestore?: (ids: string[]) => void; /** Override the default delete action */ onDelete?: (ids: string[]) => void; @@ -129,23 +128,23 @@ export const DocsExplorer = ({ height: groupBy ? 24 : 0, className: styles.groupHeader, items: group.items.map((docId: string) => { + if (view === 'list') { + return { + id: docId, + Component: DocListItemComponent, + height: 42, + } satisfies MasonryItem; + } return { id: docId, Component: DocListItemComponent, - height: - view === 'list' - ? 42 - : view === 'grid' - ? 280 - : calcCardHeightById(docId, heightBase, heightScale), - 'data-view': view, - className: styles.docItem, - }; + ratio: view === 'grid' ? ratios[0] : calcCardRatioById(docId), + } satisfies MasonryItem; }), } satisfies MasonryGroup; }); return items; - }, [groupBy, groups, heightBase, heightScale, view]); + }, [groupBy, groups, view]); const handleCloseFloatingToolbar = useCallback(() => { contextValue.selectMode$?.next(false); @@ -222,13 +221,18 @@ export const DocsExplorer = ({ }; }, [contextValue]); + const responsivePaddingX = useCallback( + (w: number) => (w > 500 ? 24 : w > 393 ? 20 : 16), + [] + ); + return ( <> (w > 500 ? 24 : w > 393 ? 20 : 16), - [] - )} + paddingY={BUILD_CONFIG.isMobileEdition ? 12 : 0} + paddingX={BUILD_CONFIG.isMobileEdition ? 16 : responsivePaddingX} /> {!disableMultiDelete || onRestore ? ( { + if (!contextValue.selectMode$?.value) { + contextValue.selectMode$?.next(true); + } const prev = contextValue.selectedDocIds$.value; if (isGroupAllSelected) { contextValue.selectedDocIds$.next( diff --git a/packages/frontend/core/src/components/filter/add-filter.tsx b/packages/frontend/core/src/components/filter/add-filter.tsx index 021388d07b..b431d2969d 100644 --- a/packages/frontend/core/src/components/filter/add-filter.tsx +++ b/packages/frontend/core/src/components/filter/add-filter.tsx @@ -2,7 +2,12 @@ import { IconButton, Menu, MenuItem, MenuSeparator } from '@affine/component'; import type { FilterParams } from '@affine/core/modules/collection-rules'; import { WorkspacePropertyService } from '@affine/core/modules/workspace-property'; import { useI18n } from '@affine/i18n'; -import { ArrowLeftBigIcon, FavoriteIcon, PlusIcon } from '@blocksuite/icons/rc'; +import { + ArrowLeftBigIcon, + CloudWorkspaceIcon, + FavoriteIcon, + PlusIcon, +} from '@blocksuite/icons/rc'; import { useLiveData, useService } from '@toeverything/infra'; import { useMemo } from 'react'; @@ -57,7 +62,9 @@ export const AddFilterMenu = ({ {t['Favorited']()} } + prefixIcon={ + + } key={'shared'} onClick={() => { onAdd({ diff --git a/packages/frontend/core/src/components/page-list/components/new-page-button.css.ts b/packages/frontend/core/src/components/page-list/components/new-page-button.css.ts index b0518a9f5d..6141ca46f4 100644 --- a/packages/frontend/core/src/components/page-list/components/new-page-button.css.ts +++ b/packages/frontend/core/src/components/page-list/components/new-page-button.css.ts @@ -1,5 +1,9 @@ import { cssVar } from '@toeverything/theme'; +import { cssVarV2 } from '@toeverything/theme/v2'; import { style } from '@vanilla-extract/css'; export const menuContent = style({ backgroundColor: cssVar('backgroundOverlayPanelColor'), }); +export const button = style({ + backgroundColor: cssVarV2.button.secondary, +}); diff --git a/packages/frontend/core/src/components/page-list/components/new-page-button.tsx b/packages/frontend/core/src/components/page-list/components/new-page-button.tsx index 8da20da51d..642be12724 100644 --- a/packages/frontend/core/src/components/page-list/components/new-page-button.tsx +++ b/packages/frontend/core/src/components/page-list/components/new-page-button.tsx @@ -6,7 +6,7 @@ import { EdgelessIcon, ImportIcon, PageIcon } from '@blocksuite/icons/rc'; import type { MouseEvent, PropsWithChildren } from 'react'; import { useCallback, useState } from 'react'; -import { menuContent } from './new-page-button.css'; +import * as styles from './new-page-button.css'; type NewPageButtonProps = { createNewDoc: (e?: MouseEvent) => void; @@ -120,7 +120,7 @@ export const NewPageButton = ({ open, }} contentOptions={{ - className: menuContent, + className: styles.menuContent, align: 'end', hideWhenDetached: true, onInteractOutside: useCallback(() => { @@ -133,6 +133,7 @@ export const NewPageButton = ({ onClick={handleCreateNewDoc} onAuxClick={handleCreateNewPage} onClickDropDown={useCallback(() => setOpen(open => !open), [])} + className={styles.button} > {children} diff --git a/packages/frontend/core/src/components/workspace-property-types/created-updated-by.tsx b/packages/frontend/core/src/components/workspace-property-types/created-updated-by.tsx index 5c061868e5..3fdd9a450c 100644 --- a/packages/frontend/core/src/components/workspace-property-types/created-updated-by.tsx +++ b/packages/frontend/core/src/components/workspace-property-types/created-updated-by.tsx @@ -13,6 +13,19 @@ import type { GroupHeaderProps } from '../explorer/types'; import { MemberSelectorInline } from '../member-selector'; import * as styles from './created-updated-by.css'; +const CreatedUsernameTip = ({ userName }: { userName: string }) => { + const i18n = useI18n(); + return i18n.t('com.affine.page-properties.property.createdBy.tip', { + userName, + }); +}; +const UpdatedUsernameTip = ({ userName }: { userName: string }) => { + const i18n = useI18n(); + return i18n.t('com.affine.page-properties.property.updatedBy.tip', { + userName, + }); +}; + const CreatedByUpdatedByAvatar = (props: { type: 'CreatedBy' | 'UpdatedBy'; doc: DocRecord; @@ -33,6 +46,9 @@ const CreatedByUpdatedByAvatar = (props: { id={userId} size={props.size} showName={props.showName} + tooltip={ + props.type === 'CreatedBy' ? CreatedUsernameTip : UpdatedUsernameTip + } />
    ); diff --git a/packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.css.ts b/packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.css.ts index c15adfe572..5e58607bae 100644 --- a/packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.css.ts +++ b/packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.css.ts @@ -33,7 +33,7 @@ export const body = style({ export const scrollArea = style({ height: 0, flex: 1, - paddingTop: '24px', + paddingTop: '12px', }); // group @@ -43,7 +43,7 @@ export const pinnedCollection = style({ flexDirection: 'column', gap: 8, padding: '0 24px', - paddingTop: '24px', + paddingTop: '12px', '@container': { 'docs-body (width <= 500px)': { padding: '0 20px', diff --git a/packages/frontend/core/src/mobile/pages/workspace/all.tsx b/packages/frontend/core/src/mobile/pages/workspace/all.tsx index 312c01349e..c96cafeef4 100644 --- a/packages/frontend/core/src/mobile/pages/workspace/all.tsx +++ b/packages/frontend/core/src/mobile/pages/workspace/all.tsx @@ -68,11 +68,7 @@ const AllDocs = () => { return ( - + ); }; diff --git a/packages/frontend/core/src/mobile/views/all-docs/collection/detail.tsx b/packages/frontend/core/src/mobile/views/all-docs/collection/detail.tsx index 1fb6c8ed7a..85ad5ffe79 100644 --- a/packages/frontend/core/src/mobile/views/all-docs/collection/detail.tsx +++ b/packages/frontend/core/src/mobile/views/all-docs/collection/detail.tsx @@ -69,11 +69,7 @@ const CollectionDocs = ({ collection }: { collection: Collection }) => { return ( - + ); }; diff --git a/packages/frontend/core/src/mobile/views/all-docs/tag/detail.tsx b/packages/frontend/core/src/mobile/views/all-docs/tag/detail.tsx index 7110a17511..403b029e14 100644 --- a/packages/frontend/core/src/mobile/views/all-docs/tag/detail.tsx +++ b/packages/frontend/core/src/mobile/views/all-docs/tag/detail.tsx @@ -75,11 +75,7 @@ const TagDocs = ({ tag }: { tag: Tag }) => { return ( - + ); }; diff --git a/packages/frontend/core/src/modules/cloud/views/public-user.tsx b/packages/frontend/core/src/modules/cloud/views/public-user.tsx index 180ed00c0d..6007658516 100644 --- a/packages/frontend/core/src/modules/cloud/views/public-user.tsx +++ b/packages/frontend/core/src/modules/cloud/views/public-user.tsx @@ -1,8 +1,8 @@ -import { Avatar } from '@affine/component'; +import { Avatar, Tooltip } from '@affine/component'; import { useCurrentServerService } from '@affine/core/components/providers/current-server-scope'; import { useI18n } from '@affine/i18n'; import { useLiveData } from '@toeverything/infra'; -import { useLayoutEffect, useMemo } from 'react'; +import { type ComponentType, useLayoutEffect, useMemo } from 'react'; import { PublicUserService } from '../services/public-user'; import * as styles from './public-user.css'; @@ -11,10 +11,12 @@ export const PublicUserLabel = ({ id, size = 20, showName = true, + tooltip: NameTip, }: { id: string; size?: number; showName?: boolean; + tooltip?: ComponentType<{ userName: string }>; }) => { const serverService = useCurrentServerService(); const publicUser = useMemo(() => { @@ -50,15 +52,23 @@ export const PublicUserLabel = ({ } return ( - - - {showName && user?.name} - + + ) : null + } + > + + + {showName && user?.name} + + ); }; diff --git a/packages/frontend/i18n/src/i18n.gen.ts b/packages/frontend/i18n/src/i18n.gen.ts index 1eb8b8bbc3..f1ed531a7a 100644 --- a/packages/frontend/i18n/src/i18n.gen.ts +++ b/packages/frontend/i18n/src/i18n.gen.ts @@ -2189,6 +2189,10 @@ export function useAFFiNEI18N(): { * `Grouping` */ ["com.affine.explorer.display-menu.grouping"](): string; + /** + * `Remove group` + */ + ["com.affine.explorer.display-menu.grouping.remove"](): string; /** * `Ordering` */ @@ -3141,6 +3145,18 @@ export function useAFFiNEI18N(): { * `Mark this doc as a template, which can be used to create new docs.` */ ["com.affine.page-properties.property.template.tooltips"](): string; + /** + * `Created by {{userName}}` + */ + ["com.affine.page-properties.property.createdBy.tip"](options: { + readonly userName: string; + }): string; + /** + * `Last edited by {{userName}}` + */ + ["com.affine.page-properties.property.updatedBy.tip"](options: { + readonly userName: string; + }): string; /** * `Properties` */ diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json index 4a16c0887c..364cd0ccf6 100644 --- a/packages/frontend/i18n/src/resources/en.json +++ b/packages/frontend/i18n/src/resources/en.json @@ -546,6 +546,7 @@ "com.affine.filterList.button.add": "Add filter", "com.affine.explorer.display-menu.button": "Display", "com.affine.explorer.display-menu.grouping": "Grouping", + "com.affine.explorer.display-menu.grouping.remove": "Remove group", "com.affine.explorer.display-menu.ordering": "Ordering", "com.affine.header.mode-switch.page": "View in Page mode", "com.affine.header.mode-switch.edgeless": "View in Edgeless Canvas", @@ -780,6 +781,8 @@ "com.affine.page-properties.property.edgelessTheme.tooltips": "Select the doc theme from Light, Dark, or System. Useful for precise control over content viewing style.", "com.affine.page-properties.property.pageWidth.tooltips": "Control the width of this page to fit content display needs.", "com.affine.page-properties.property.template.tooltips": "Mark this doc as a template, which can be used to create new docs.", + "com.affine.page-properties.property.createdBy.tip": "Created by {{userName}}", + "com.affine.page-properties.property.updatedBy.tip": "Last edited by {{userName}}", "com.affine.propertySidebar.property-list.section": "Properties", "com.affine.propertySidebar.add-more.section": "Add more properties", "com.affine.page-properties.settings.title": "customize properties",