mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-10 13:38:49 +08:00
Merge pull request #194 from toeverything/fix/experience
Fix/experience
This commit is contained in:
@@ -2,7 +2,7 @@ FROM node:16-alpine as builder
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN apk add g++ make python3 git libpng-dev
|
RUN apk add g++ make python3 git libpng-dev
|
||||||
RUN npm i -g pnpm@7 && pnpm i --frozen-lockfile --store=node_modules/.pnpm-store && pnpm run build:local
|
RUN npm i -g pnpm@7 && pnpm i --frozen-lockfile --store=node_modules/.pnpm-store && pnpm run build:local --skip-nx-cache
|
||||||
|
|
||||||
FROM node:16-alpine as relocate
|
FROM node:16-alpine as relocate
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|||||||
@@ -60,6 +60,9 @@ export const CardContext = (props: Props) => {
|
|||||||
|
|
||||||
const StyledCardContainer = styled('div')`
|
const StyledCardContainer = styled('div')`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
&:focus-within {
|
&:focus-within {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { styled } from '@toeverything/components/ui';
|
|||||||
import type { AsyncBlock } from '../editor';
|
import type { AsyncBlock } from '../editor';
|
||||||
import { PendantPopover } from './pendant-popover';
|
import { PendantPopover } from './pendant-popover';
|
||||||
import { PendantRender } from './pendant-render';
|
import { PendantRender } from './pendant-render';
|
||||||
|
import { useRef } from 'react';
|
||||||
|
import { getRecastItemValue, useRecastBlockMeta } from '../recast-block';
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
@@ -14,13 +16,27 @@ export const BlockPendantProvider: FC<PropsWithChildren<BlockTagProps>> = ({
|
|||||||
block,
|
block,
|
||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
|
const triggerRef = useRef<HTMLDivElement>();
|
||||||
|
const { getProperties } = useRecastBlockMeta();
|
||||||
|
const properties = getProperties();
|
||||||
|
const { getValue } = getRecastItemValue(block);
|
||||||
|
const showTriggerLine =
|
||||||
|
properties.filter(property => getValue(property.id)).length === 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
{children}
|
{children}
|
||||||
|
|
||||||
<PendantPopover block={block}>
|
{showTriggerLine ? (
|
||||||
<StyledTriggerLine />
|
<StyledPendantContainer ref={triggerRef}>
|
||||||
</PendantPopover>
|
<PendantPopover
|
||||||
|
block={block}
|
||||||
|
container={triggerRef.current}
|
||||||
|
>
|
||||||
|
<StyledTriggerLine />
|
||||||
|
</PendantPopover>
|
||||||
|
</StyledPendantContainer>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<PendantRender block={block} />
|
<PendantRender block={block} />
|
||||||
</Container>
|
</Container>
|
||||||
@@ -43,10 +59,12 @@ const StyledTriggerLine = styled('div')({
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
height: '2px',
|
height: '2px',
|
||||||
background: '#dadada',
|
background: '#dadada',
|
||||||
display: 'none',
|
display: 'flex',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: '0',
|
left: '0',
|
||||||
top: '4px',
|
top: '4px',
|
||||||
|
transition: 'opacity .2s',
|
||||||
|
opacity: '0',
|
||||||
},
|
},
|
||||||
'::after': {
|
'::after': {
|
||||||
content: "''",
|
content: "''",
|
||||||
@@ -60,18 +78,24 @@ const StyledTriggerLine = styled('div')({
|
|||||||
transition: 'width .3s',
|
transition: 'width .3s',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const StyledPendantContainer = styled('div')({
|
||||||
const Container = styled('div')({
|
width: '100px',
|
||||||
position: 'relative',
|
|
||||||
paddingBottom: `${LINE_GAP - TAG_GAP * 2}px`,
|
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
[StyledTriggerLine.toString()]: {
|
[`${StyledTriggerLine}`]: {
|
||||||
'&::before': {
|
|
||||||
display: 'flex',
|
|
||||||
},
|
|
||||||
'&::after': {
|
'&::after': {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const Container = styled('div')({
|
||||||
|
position: 'relative',
|
||||||
|
paddingBottom: `${LINE_GAP - TAG_GAP * 2}px`,
|
||||||
|
'&:hover': {
|
||||||
|
[`${StyledTriggerLine}`]: {
|
||||||
|
'&::before': {
|
||||||
|
opacity: '1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
+3
-1
@@ -29,6 +29,7 @@ export const PendantHistoryPanel = ({
|
|||||||
|
|
||||||
const [history, setHistory] = useState<RecastBlockValue[]>([]);
|
const [history, setHistory] = useState<RecastBlockValue[]>([]);
|
||||||
const popoverHandlerRef = useRef<{ [key: string]: PopperHandler }>({});
|
const popoverHandlerRef = useRef<{ [key: string]: PopperHandler }>({});
|
||||||
|
const historyPanelRef = useRef<HTMLDivElement>();
|
||||||
const { getValueHistory } = getRecastItemValue(block);
|
const { getValueHistory } = getRecastItemValue(block);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -84,7 +85,7 @@ export const PendantHistoryPanel = ({
|
|||||||
}, [block, getProperties, groupBlock, recastBlock]);
|
}, [block, getProperties, groupBlock, recastBlock]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledPendantHistoryPanel>
|
<StyledPendantHistoryPanel ref={historyPanelRef}>
|
||||||
{history.map(item => {
|
{history.map(item => {
|
||||||
const property = getProperty(item.id);
|
const property = getProperty(item.id);
|
||||||
return (
|
return (
|
||||||
@@ -116,6 +117,7 @@ export const PendantHistoryPanel = ({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
trigger="click"
|
trigger="click"
|
||||||
|
container={historyPanelRef.current}
|
||||||
>
|
>
|
||||||
<PendantTag
|
<PendantTag
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
+20
-2
@@ -1,5 +1,11 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Input, Option, Select, Tooltip } from '@toeverything/components/ui';
|
import {
|
||||||
|
Input,
|
||||||
|
message,
|
||||||
|
Option,
|
||||||
|
Select,
|
||||||
|
Tooltip,
|
||||||
|
} from '@toeverything/components/ui';
|
||||||
import { HelpCenterIcon } from '@toeverything/components/icons';
|
import { HelpCenterIcon } from '@toeverything/components/icons';
|
||||||
import { AsyncBlock } from '../../editor';
|
import { AsyncBlock } from '../../editor';
|
||||||
|
|
||||||
@@ -18,6 +24,7 @@ import {
|
|||||||
generateRandomFieldName,
|
generateRandomFieldName,
|
||||||
generateInitialOptions,
|
generateInitialOptions,
|
||||||
getPendantConfigByType,
|
getPendantConfigByType,
|
||||||
|
checkPendantForm,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import { useOnCreateSure } from './hooks';
|
import { useOnCreateSure } from './hooks';
|
||||||
|
|
||||||
@@ -74,7 +81,7 @@ export const CreatePendantPanel = ({
|
|||||||
setFieldName(e.target.value);
|
setFieldName(e.target.value);
|
||||||
}}
|
}}
|
||||||
endAdornment={
|
endAdornment={
|
||||||
<Tooltip content="Help info here">
|
<Tooltip content="Help info here" placement="top">
|
||||||
<StyledInputEndAdornment>
|
<StyledInputEndAdornment>
|
||||||
<HelpCenterIcon />
|
<HelpCenterIcon />
|
||||||
</StyledInputEndAdornment>
|
</StyledInputEndAdornment>
|
||||||
@@ -98,6 +105,17 @@ export const CreatePendantPanel = ({
|
|||||||
)}
|
)}
|
||||||
iconConfig={getPendantConfigByType(selectedOption.type)}
|
iconConfig={getPendantConfigByType(selectedOption.type)}
|
||||||
onSure={async (type, newPropertyItem, newValue) => {
|
onSure={async (type, newPropertyItem, newValue) => {
|
||||||
|
const checkResult = checkPendantForm(
|
||||||
|
type,
|
||||||
|
fieldName,
|
||||||
|
newPropertyItem,
|
||||||
|
newValue
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!checkResult.passed) {
|
||||||
|
await message.error(checkResult.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
await onCreateSure({
|
await onCreateSure({
|
||||||
type,
|
type,
|
||||||
newPropertyItem,
|
newPropertyItem,
|
||||||
|
|||||||
+14
-3
@@ -1,5 +1,5 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Input, Tooltip } from '@toeverything/components/ui';
|
import { Input, message, Tooltip } from '@toeverything/components/ui';
|
||||||
import { HelpCenterIcon } from '@toeverything/components/icons';
|
import { HelpCenterIcon } from '@toeverything/components/icons';
|
||||||
import { PendantModifyPanel } from '../pendant-modify-panel';
|
import { PendantModifyPanel } from '../pendant-modify-panel';
|
||||||
import type { AsyncBlock } from '../../editor';
|
import type { AsyncBlock } from '../../editor';
|
||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
type RecastBlockValue,
|
type RecastBlockValue,
|
||||||
type RecastMetaProperty,
|
type RecastMetaProperty,
|
||||||
} from '../../recast-block';
|
} from '../../recast-block';
|
||||||
import { getPendantConfigByType } from '../utils';
|
import { checkPendantForm, getPendantConfigByType } from '../utils';
|
||||||
import {
|
import {
|
||||||
StyledPopoverWrapper,
|
StyledPopoverWrapper,
|
||||||
StyledOperationLabel,
|
StyledOperationLabel,
|
||||||
@@ -70,7 +70,7 @@ export const UpdatePendantPanel = ({
|
|||||||
setFieldName(e.target.value);
|
setFieldName(e.target.value);
|
||||||
}}
|
}}
|
||||||
endAdornment={
|
endAdornment={
|
||||||
<Tooltip content="Help info here">
|
<Tooltip content="Help info here" placement="top">
|
||||||
<StyledInputEndAdornment>
|
<StyledInputEndAdornment>
|
||||||
<HelpCenterIcon />
|
<HelpCenterIcon />
|
||||||
</StyledInputEndAdornment>
|
</StyledInputEndAdornment>
|
||||||
@@ -98,6 +98,17 @@ export const UpdatePendantPanel = ({
|
|||||||
property={property}
|
property={property}
|
||||||
type={property.type}
|
type={property.type}
|
||||||
onSure={async (type, newPropertyItem, newValue) => {
|
onSure={async (type, newPropertyItem, newValue) => {
|
||||||
|
const checkResult = checkPendantForm(
|
||||||
|
type,
|
||||||
|
fieldName,
|
||||||
|
newPropertyItem,
|
||||||
|
newValue
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!checkResult.passed) {
|
||||||
|
await message.error(checkResult.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
await onUpdateSure({
|
await onUpdateSure({
|
||||||
type,
|
type,
|
||||||
newPropertyItem,
|
newPropertyItem,
|
||||||
|
|||||||
@@ -23,12 +23,7 @@ import {
|
|||||||
PendantTypes,
|
PendantTypes,
|
||||||
type TempInformationType,
|
type TempInformationType,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import {
|
import { getOfficialSelected, getPendantConfigByType } from '../utils';
|
||||||
checkPendantForm,
|
|
||||||
getOfficialSelected,
|
|
||||||
getPendantConfigByType,
|
|
||||||
} from '../utils';
|
|
||||||
import { message } from '@toeverything/components/ui';
|
|
||||||
|
|
||||||
type SelectPropertyType = MultiSelectProperty | SelectProperty;
|
type SelectPropertyType = MultiSelectProperty | SelectProperty;
|
||||||
type SureParams = {
|
type SureParams = {
|
||||||
@@ -56,18 +51,6 @@ export const useOnCreateSure = ({ block }: { block: AsyncBlock }) => {
|
|||||||
newPropertyItem,
|
newPropertyItem,
|
||||||
newValue,
|
newValue,
|
||||||
}: SureParams) => {
|
}: SureParams) => {
|
||||||
const checkResult = checkPendantForm(
|
|
||||||
type,
|
|
||||||
fieldName,
|
|
||||||
newPropertyItem,
|
|
||||||
newValue
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!checkResult.passed) {
|
|
||||||
await message.error(checkResult.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
type === PendantTypes.MultiSelect ||
|
type === PendantTypes.MultiSelect ||
|
||||||
type === PendantTypes.Select ||
|
type === PendantTypes.Select ||
|
||||||
@@ -181,18 +164,6 @@ export const useOnUpdateSure = ({
|
|||||||
newPropertyItem,
|
newPropertyItem,
|
||||||
newValue,
|
newValue,
|
||||||
}: SureParams) => {
|
}: SureParams) => {
|
||||||
const checkResult = checkPendantForm(
|
|
||||||
type,
|
|
||||||
fieldName,
|
|
||||||
newPropertyItem,
|
|
||||||
newValue
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!checkResult.passed) {
|
|
||||||
await message.error(checkResult.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
type === PendantTypes.MultiSelect ||
|
type === PendantTypes.MultiSelect ||
|
||||||
type === PendantTypes.Select ||
|
type === PendantTypes.Select ||
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export const PendantPopover: FC<
|
|||||||
block={block}
|
block={block}
|
||||||
endElement={
|
endElement={
|
||||||
<AddPendantPopover
|
<AddPendantPopover
|
||||||
|
container={popoverProps.container}
|
||||||
block={block}
|
block={block}
|
||||||
onSure={() => {
|
onSure={() => {
|
||||||
popoverHandlerRef.current?.setVisible(false);
|
popoverHandlerRef.current?.setVisible(false);
|
||||||
|
|||||||
@@ -105,6 +105,8 @@ export const PendantRender = ({ block }: { block: AsyncBlock }) => {
|
|||||||
<AddPendantPopover
|
<AddPendantPopover
|
||||||
block={block}
|
block={block}
|
||||||
iconStyle={{ marginTop: 4 }}
|
iconStyle={{ marginTop: 4 }}
|
||||||
|
trigger="click"
|
||||||
|
// trigger={isKanbanView ? 'hover' : 'click'}
|
||||||
container={blockRenderContainerRef.current}
|
container={blockRenderContainerRef.current}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { message } from '@toeverything/components/ui';
|
||||||
import { useSettingFlags, type SettingFlags } from './use-setting-flags';
|
import { useSettingFlags, type SettingFlags } from './use-setting-flags';
|
||||||
import { copyToClipboard } from '@toeverything/utils';
|
import { copyToClipboard } from '@toeverything/utils';
|
||||||
import {
|
import {
|
||||||
@@ -91,7 +92,10 @@ export const useSettings = (): SettingItem[] => {
|
|||||||
{
|
{
|
||||||
type: 'button',
|
type: 'button',
|
||||||
name: 'Copy Page Link',
|
name: 'Copy Page Link',
|
||||||
onClick: () => copyToClipboard(window.location.href),
|
onClick: () => {
|
||||||
|
copyToClipboard(window.location.href);
|
||||||
|
message.success('Page link copied successfully');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'separator',
|
type: 'separator',
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import SelectUnstyled, {
|
|||||||
} from '@mui/base/SelectUnstyled';
|
} from '@mui/base/SelectUnstyled';
|
||||||
/* eslint-disable no-restricted-imports */
|
/* eslint-disable no-restricted-imports */
|
||||||
import PopperUnstyled from '@mui/base/PopperUnstyled';
|
import PopperUnstyled from '@mui/base/PopperUnstyled';
|
||||||
|
import { ArrowDropDownIcon } from '@toeverything/components/icons';
|
||||||
import { styled } from '../styled';
|
import { styled } from '../styled';
|
||||||
|
|
||||||
type ExtendSelectProps = {
|
type ExtendSelectProps = {
|
||||||
@@ -41,20 +42,29 @@ export const Select = forwardRef(function CustomSelect<TValue>(
|
|||||||
const { width = '100%', style, listboxStyle, placeholder } = props;
|
const { width = '100%', style, listboxStyle, placeholder } = props;
|
||||||
const components: SelectUnstyledProps<TValue>['components'] = {
|
const components: SelectUnstyledProps<TValue>['components'] = {
|
||||||
// Root: generateStyledRoot({ width, ...style }),
|
// Root: generateStyledRoot({ width, ...style }),
|
||||||
Root: forwardRef((rootProps, rootRef) => (
|
Root: forwardRef((rootProps, rootRef) => {
|
||||||
<StyledRoot
|
const {
|
||||||
ref={rootRef}
|
ownerState: { open },
|
||||||
{...rootProps}
|
} = rootProps;
|
||||||
style={{
|
|
||||||
width,
|
return (
|
||||||
...style,
|
<StyledRoot
|
||||||
}}
|
ref={rootRef}
|
||||||
>
|
{...rootProps}
|
||||||
{rootProps.children || (
|
style={{
|
||||||
<StyledPlaceholder>{placeholder}</StyledPlaceholder>
|
width,
|
||||||
)}
|
...style,
|
||||||
</StyledRoot>
|
}}
|
||||||
)),
|
>
|
||||||
|
{rootProps.children || (
|
||||||
|
<StyledPlaceholder>{placeholder}</StyledPlaceholder>
|
||||||
|
)}
|
||||||
|
<StyledSelectedArrowWrapper open={open}>
|
||||||
|
<ArrowDropDownIcon />
|
||||||
|
</StyledSelectedArrowWrapper>
|
||||||
|
</StyledRoot>
|
||||||
|
);
|
||||||
|
}),
|
||||||
Listbox: forwardRef((listboxProps, listboxRef) => (
|
Listbox: forwardRef((listboxProps, listboxRef) => (
|
||||||
<StyledListbox
|
<StyledListbox
|
||||||
ref={listboxRef}
|
ref={listboxRef}
|
||||||
@@ -73,6 +83,20 @@ export const Select = forwardRef(function CustomSelect<TValue>(
|
|||||||
RefAttributes<HTMLUListElement>
|
RefAttributes<HTMLUListElement>
|
||||||
) => JSX.Element;
|
) => JSX.Element;
|
||||||
|
|
||||||
|
const StyledSelectedArrowWrapper = styled('div')<{ open: boolean }>(
|
||||||
|
({ open }) => ({
|
||||||
|
position: 'absolute',
|
||||||
|
top: '0',
|
||||||
|
bottom: '0',
|
||||||
|
right: '12px',
|
||||||
|
margin: 'auto',
|
||||||
|
lineHeight: '32px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
transform: `rotate(${open ? '180deg' : '0'})`,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const StyledRoot = styled('div')(({ theme }) => ({
|
const StyledRoot = styled('div')(({ theme }) => ({
|
||||||
height: '32px',
|
height: '32px',
|
||||||
border: `1px solid ${theme.affine.palette.borderColor}`,
|
border: `1px solid ${theme.affine.palette.borderColor}`,
|
||||||
@@ -95,18 +119,6 @@ const StyledRoot = styled('div')(({ theme }) => ({
|
|||||||
|
|
||||||
[`&.${selectUnstyledClasses.expanded}`]: {
|
[`&.${selectUnstyledClasses.expanded}`]: {
|
||||||
borderColor: `${theme.affine.palette.primary}`,
|
borderColor: `${theme.affine.palette.primary}`,
|
||||||
'&::after': {
|
|
||||||
content: '"▴"',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'&::after': {
|
|
||||||
content: '"▾"',
|
|
||||||
position: ' absolute',
|
|
||||||
top: '0',
|
|
||||||
bottom: '0',
|
|
||||||
right: '12px',
|
|
||||||
margin: 'auto',
|
|
||||||
lineHeight: '32px',
|
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user