mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-18 10:36:22 +08:00
refactor: workspace header (#1880)
This commit is contained in:
+58
@@ -0,0 +1,58 @@
|
||||
import { atom, useAtomValue } from 'jotai';
|
||||
import type { FC } from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
const lottieAtom = atom(async () => import('lottie-web').then(m => m.default));
|
||||
|
||||
type CustomLottieProps = {
|
||||
options: {
|
||||
loop?: boolean | number | undefined;
|
||||
autoplay?: boolean | undefined;
|
||||
animationData: any;
|
||||
rendererSettings?: {
|
||||
preserveAspectRatio?: string | undefined;
|
||||
};
|
||||
};
|
||||
isStopped?: boolean | undefined;
|
||||
speed?: number | undefined;
|
||||
width?: number | string | undefined;
|
||||
height?: number | string | undefined;
|
||||
};
|
||||
|
||||
const CustomLottie: FC<CustomLottieProps> = ({
|
||||
options,
|
||||
isStopped,
|
||||
speed,
|
||||
width,
|
||||
height,
|
||||
}) => {
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
const lottieInstance = useRef<any>();
|
||||
const lottie = useAtomValue(lottieAtom);
|
||||
|
||||
useEffect(() => {
|
||||
if (element.current) {
|
||||
lottieInstance.current = lottie.loadAnimation({
|
||||
...options,
|
||||
container: element.current,
|
||||
});
|
||||
}
|
||||
return () => {
|
||||
lottieInstance.current?.destroy();
|
||||
};
|
||||
}, [lottie, options]);
|
||||
|
||||
useEffect(() => {
|
||||
if (speed) {
|
||||
lottieInstance.current?.setSpeed(speed);
|
||||
}
|
||||
if (isStopped) {
|
||||
lottieInstance.current?.stop();
|
||||
} else {
|
||||
lottieInstance.current?.play();
|
||||
}
|
||||
}, [isStopped, speed]);
|
||||
|
||||
return <div ref={element} style={{ width, height }}></div>;
|
||||
};
|
||||
export default CustomLottie;
|
||||
+669
@@ -0,0 +1,669 @@
|
||||
{
|
||||
"v": "5.9.0",
|
||||
"fr": 29.9700012207031,
|
||||
"ip": 0,
|
||||
"op": 60.0000024438501,
|
||||
"w": 500,
|
||||
"h": 500,
|
||||
"nm": "edgeless-hover",
|
||||
"ddd": 0,
|
||||
"assets": [],
|
||||
"layers": [
|
||||
{
|
||||
"ddd": 0,
|
||||
"ind": 1,
|
||||
"ty": 4,
|
||||
"nm": "Layer 3/paper-edgeless-icons Outlines",
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 100, "ix": 11 },
|
||||
"r": { "a": 0, "k": 0, "ix": 10 },
|
||||
"p": { "a": 0, "k": [250, 250, 0], "ix": 2, "l": 2 },
|
||||
"a": { "a": 0, "k": [183.5, 183.5, 0], "ix": 1, "l": 2 },
|
||||
"s": { "a": 0, "k": [100, 100, 100], "ix": 6, "l": 2 }
|
||||
},
|
||||
"ao": 0,
|
||||
"shapes": [
|
||||
{
|
||||
"ty": "gr",
|
||||
"it": [
|
||||
{
|
||||
"ind": 0,
|
||||
"ty": "sh",
|
||||
"ix": 1,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[0, 0],
|
||||
[0, -5.604],
|
||||
[0, 0],
|
||||
[-5.614, 0],
|
||||
[0, 0],
|
||||
[0, 5.605],
|
||||
[0, 0],
|
||||
[5.615, 0]
|
||||
],
|
||||
"o": [
|
||||
[-5.614, 0],
|
||||
[0, 0],
|
||||
[0, 5.605],
|
||||
[0, 0],
|
||||
[5.615, 0],
|
||||
[0, 0],
|
||||
[0, -5.604],
|
||||
[0, 0]
|
||||
],
|
||||
"v": [
|
||||
[-30.525, -40.7],
|
||||
[-40.699, -30.525],
|
||||
[-40.699, 30.524],
|
||||
[-30.525, 40.699],
|
||||
[30.525, 40.699],
|
||||
[40.699, 30.524],
|
||||
[40.699, -30.525],
|
||||
[30.525, -40.7]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ind": 1,
|
||||
"ty": "sh",
|
||||
"ix": 2,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[22.447, 0],
|
||||
[0, 0],
|
||||
[0, 22.437],
|
||||
[0, 0],
|
||||
[-22.446, 0],
|
||||
[0, 0],
|
||||
[0, -22.437],
|
||||
[0, 0]
|
||||
],
|
||||
"o": [
|
||||
[0, 0],
|
||||
[-22.446, 0],
|
||||
[0, 0],
|
||||
[0, -22.437],
|
||||
[0, 0],
|
||||
[22.447, 0],
|
||||
[0, 0],
|
||||
[0, 22.437]
|
||||
],
|
||||
"v": [
|
||||
[30.525, 71.224],
|
||||
[-30.525, 71.224],
|
||||
[-71.225, 30.524],
|
||||
[-71.225, -30.525],
|
||||
[-30.525, -71.225],
|
||||
[30.525, -71.225],
|
||||
[71.224, -30.525],
|
||||
[71.224, 30.524]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 2",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "mm",
|
||||
"mm": 1,
|
||||
"nm": "Merge Paths 1",
|
||||
"mn": "ADBE Vector Filter - Merge",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "fl",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.466666696586, 0.458823559331, 0.490196108351, 1],
|
||||
"ix": 4
|
||||
},
|
||||
"o": { "a": 0, "k": 100, "ix": 5 },
|
||||
"r": 1,
|
||||
"bm": 0,
|
||||
"nm": "Fill 1",
|
||||
"mn": "ADBE Vector Graphic - Fill",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "tr",
|
||||
"p": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": 0.833, "y": 0.833 },
|
||||
"o": { "x": 0.167, "y": 0.167 },
|
||||
"t": 0,
|
||||
"s": [295.322, 295.323],
|
||||
"to": [0, 0],
|
||||
"ti": [0, 0]
|
||||
},
|
||||
{
|
||||
"i": { "x": 0.833, "y": 0.833 },
|
||||
"o": { "x": 0.167, "y": 0.167 },
|
||||
"t": 30,
|
||||
"s": [315.322, 315.323],
|
||||
"to": [0, 0],
|
||||
"ti": [0, 0]
|
||||
},
|
||||
{ "t": 58.0000023623884, "s": [295.322, 295.323] }
|
||||
],
|
||||
"ix": 2
|
||||
},
|
||||
"a": { "a": 0, "k": [0, 0], "ix": 1 },
|
||||
"s": { "a": 0, "k": [100, 100], "ix": 3 },
|
||||
"r": { "a": 0, "k": 0, "ix": 6 },
|
||||
"o": { "a": 0, "k": 100, "ix": 7 },
|
||||
"sk": { "a": 0, "k": 0, "ix": 4 },
|
||||
"sa": { "a": 0, "k": 0, "ix": 5 },
|
||||
"nm": "Transform"
|
||||
}
|
||||
],
|
||||
"nm": "Group 1",
|
||||
"np": 4,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 1,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "gr",
|
||||
"it": [
|
||||
{
|
||||
"ind": 0,
|
||||
"ty": "sh",
|
||||
"ix": 1,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[8.426, 0],
|
||||
[0, 8.426],
|
||||
[0, 0],
|
||||
[-8.426, 0],
|
||||
[0, -8.426],
|
||||
[0, 0]
|
||||
],
|
||||
"o": [
|
||||
[-8.426, 0],
|
||||
[0, 0],
|
||||
[0, -8.426],
|
||||
[8.426, 0],
|
||||
[0, 0],
|
||||
[0, 8.426]
|
||||
],
|
||||
"v": [
|
||||
[0, 30.525],
|
||||
[-15.262, 15.263],
|
||||
[-15.262, -15.262],
|
||||
[0, -30.525],
|
||||
[15.262, -15.262],
|
||||
[15.262, 15.263]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "fl",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.466666696586, 0.458823559331, 0.490196108351, 1],
|
||||
"ix": 4
|
||||
},
|
||||
"o": { "a": 0, "k": 100, "ix": 5 },
|
||||
"r": 1,
|
||||
"bm": 0,
|
||||
"nm": "Fill 1",
|
||||
"mn": "ADBE Vector Graphic - Fill",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "tr",
|
||||
"p": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": 0.833, "y": 0.833 },
|
||||
"o": { "x": 0.167, "y": 0.167 },
|
||||
"t": 0,
|
||||
"s": [76.561, 183.399],
|
||||
"to": [0, 0],
|
||||
"ti": [0, 0]
|
||||
},
|
||||
{
|
||||
"i": { "x": 0.833, "y": 0.833 },
|
||||
"o": { "x": 0.167, "y": 0.167 },
|
||||
"t": 30,
|
||||
"s": [56.561, 183.399],
|
||||
"to": [0, 0],
|
||||
"ti": [0, 0]
|
||||
},
|
||||
{ "t": 58.0000023623884, "s": [76.561, 183.399] }
|
||||
],
|
||||
"ix": 2
|
||||
},
|
||||
"a": { "a": 0, "k": [0, 0], "ix": 1 },
|
||||
"s": { "a": 0, "k": [100, 100], "ix": 3 },
|
||||
"r": { "a": 0, "k": 0, "ix": 6 },
|
||||
"o": { "a": 0, "k": 100, "ix": 7 },
|
||||
"sk": { "a": 0, "k": 0, "ix": 4 },
|
||||
"sa": { "a": 0, "k": 0, "ix": 5 },
|
||||
"nm": "Transform"
|
||||
}
|
||||
],
|
||||
"nm": "Group 2",
|
||||
"np": 2,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 2,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "gr",
|
||||
"it": [
|
||||
{
|
||||
"ind": 0,
|
||||
"ty": "sh",
|
||||
"ix": 1,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[8.426, 0],
|
||||
[0, 8.426],
|
||||
[5.395, 13.057],
|
||||
[9.956, 9.956],
|
||||
[13.037, 5.406],
|
||||
[14.1, 0],
|
||||
[0, 8.426],
|
||||
[-8.426, 0],
|
||||
[-16.753, -6.955],
|
||||
[-12.808, -12.818],
|
||||
[-6.955, -16.773],
|
||||
[0, -18.105]
|
||||
],
|
||||
"o": [
|
||||
[-8.426, 0],
|
||||
[0, -14.09],
|
||||
[-5.416, -13.016],
|
||||
[-9.957, -9.956],
|
||||
[-13.026, -5.385],
|
||||
[-8.426, 0],
|
||||
[0, -8.426],
|
||||
[18.134, 0],
|
||||
[16.763, 6.936],
|
||||
[12.788, 12.778],
|
||||
[6.936, 16.773],
|
||||
[0, 8.426]
|
||||
],
|
||||
"v": [
|
||||
[61.049, 76.312],
|
||||
[45.788, 61.05],
|
||||
[37.66, 20.151],
|
||||
[14.497, -14.487],
|
||||
[-20.161, -37.659],
|
||||
[-61.049, -45.787],
|
||||
[-76.311, -61.049],
|
||||
[-61.049, -76.312],
|
||||
[-8.475, -65.839],
|
||||
[36.09, -36.069],
|
||||
[65.858, 8.486],
|
||||
[76.311, 61.05]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "fl",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.466666696586, 0.458823559331, 0.490196108351, 1],
|
||||
"ix": 4
|
||||
},
|
||||
"o": { "a": 0, "k": 100, "ix": 5 },
|
||||
"r": 1,
|
||||
"bm": 0,
|
||||
"nm": "Fill 1",
|
||||
"mn": "ADBE Vector Graphic - Fill",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "tr",
|
||||
"p": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": 0.833, "y": 0.833 },
|
||||
"o": { "x": 0.167, "y": 0.167 },
|
||||
"t": 0,
|
||||
"s": [254.447, 112.349],
|
||||
"to": [0, 0],
|
||||
"ti": [0, 0]
|
||||
},
|
||||
{
|
||||
"i": { "x": 0.833, "y": 0.833 },
|
||||
"o": { "x": 0.167, "y": 0.167 },
|
||||
"t": 30,
|
||||
"s": [274.447, 92.349],
|
||||
"to": [0, 0],
|
||||
"ti": [0, 0]
|
||||
},
|
||||
{ "t": 58.0000023623884, "s": [254.447, 112.349] }
|
||||
],
|
||||
"ix": 2
|
||||
},
|
||||
"a": { "a": 0, "k": [0, 0], "ix": 1 },
|
||||
"s": { "a": 0, "k": [100, 100], "ix": 3 },
|
||||
"r": { "a": 0, "k": 0, "ix": 6 },
|
||||
"o": { "a": 0, "k": 100, "ix": 7 },
|
||||
"sk": { "a": 0, "k": 0, "ix": 4 },
|
||||
"sa": { "a": 0, "k": 0, "ix": 5 },
|
||||
"nm": "Transform"
|
||||
}
|
||||
],
|
||||
"nm": "Group 3",
|
||||
"np": 2,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 3,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "gr",
|
||||
"it": [
|
||||
{
|
||||
"ind": 0,
|
||||
"ty": "sh",
|
||||
"ix": 1,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[22.446, 0],
|
||||
[0, -22.437],
|
||||
[-22.446, 0],
|
||||
[0, 22.436]
|
||||
],
|
||||
"o": [
|
||||
[-22.446, 0],
|
||||
[0, 22.436],
|
||||
[22.446, 0],
|
||||
[0, -22.437]
|
||||
],
|
||||
"v": [
|
||||
[0, -40.7],
|
||||
[-40.7, 0],
|
||||
[0, 40.699],
|
||||
[40.7, 0]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ind": 1,
|
||||
"ty": "sh",
|
||||
"ix": 2,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[39.269, 0],
|
||||
[0, 39.268],
|
||||
[-39.269, 0],
|
||||
[0, -39.269]
|
||||
],
|
||||
"o": [
|
||||
[-39.269, 0],
|
||||
[0, -39.269],
|
||||
[39.269, 0],
|
||||
[0, 39.268]
|
||||
],
|
||||
"v": [
|
||||
[0, 71.224],
|
||||
[-71.224, 0],
|
||||
[0, -71.225],
|
||||
[71.224, 0]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 2",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "mm",
|
||||
"mm": 1,
|
||||
"nm": "Merge Paths 1",
|
||||
"mn": "ADBE Vector Filter - Merge",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "fl",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.466666696586, 0.458823559331, 0.490196108351, 1],
|
||||
"ix": 4
|
||||
},
|
||||
"o": { "a": 0, "k": 100, "ix": 5 },
|
||||
"r": 1,
|
||||
"bm": 0,
|
||||
"nm": "Fill 1",
|
||||
"mn": "ADBE Vector Graphic - Fill",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "tr",
|
||||
"p": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": 0.833, "y": 0.833 },
|
||||
"o": { "x": 0.167, "y": 0.167 },
|
||||
"t": 0,
|
||||
"s": [71.474, 295.323],
|
||||
"to": [0, 0],
|
||||
"ti": [0, 0]
|
||||
},
|
||||
{
|
||||
"i": { "x": 0.833, "y": 0.833 },
|
||||
"o": { "x": 0.167, "y": 0.167 },
|
||||
"t": 30,
|
||||
"s": [51.474, 315.323],
|
||||
"to": [0, 0],
|
||||
"ti": [0, 0]
|
||||
},
|
||||
{ "t": 58.0000023623884, "s": [71.474, 295.323] }
|
||||
],
|
||||
"ix": 2
|
||||
},
|
||||
"a": { "a": 0, "k": [0, 0], "ix": 1 },
|
||||
"s": { "a": 0, "k": [100, 100], "ix": 3 },
|
||||
"r": { "a": 0, "k": 0, "ix": 6 },
|
||||
"o": { "a": 0, "k": 100, "ix": 7 },
|
||||
"sk": { "a": 0, "k": 0, "ix": 4 },
|
||||
"sa": { "a": 0, "k": 0, "ix": 5 },
|
||||
"nm": "Transform"
|
||||
}
|
||||
],
|
||||
"nm": "Group 4",
|
||||
"np": 4,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 4,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "gr",
|
||||
"it": [
|
||||
{
|
||||
"ind": 0,
|
||||
"ty": "sh",
|
||||
"ix": 1,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[22.446, 0],
|
||||
[0, -22.437],
|
||||
[-22.446, 0],
|
||||
[0, 22.436]
|
||||
],
|
||||
"o": [
|
||||
[-22.446, 0],
|
||||
[0, 22.436],
|
||||
[22.446, 0],
|
||||
[0, -22.437]
|
||||
],
|
||||
"v": [
|
||||
[0, -40.7],
|
||||
[-40.7, 0.001],
|
||||
[0, 40.7],
|
||||
[40.7, 0.001]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ind": 1,
|
||||
"ty": "sh",
|
||||
"ix": 2,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[39.269, 0],
|
||||
[0, 39.268],
|
||||
[-39.269, 0],
|
||||
[0, -39.269]
|
||||
],
|
||||
"o": [
|
||||
[-39.269, 0],
|
||||
[0, -39.269],
|
||||
[39.269, 0],
|
||||
[0, 39.268]
|
||||
],
|
||||
"v": [
|
||||
[0, 71.225],
|
||||
[-71.224, 0.001],
|
||||
[0, -71.225],
|
||||
[71.224, 0.001]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 2",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "mm",
|
||||
"mm": 1,
|
||||
"nm": "Merge Paths 1",
|
||||
"mn": "ADBE Vector Filter - Merge",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "fl",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.466666696586, 0.458823559331, 0.490196108351, 1],
|
||||
"ix": 4
|
||||
},
|
||||
"o": { "a": 0, "k": 100, "ix": 5 },
|
||||
"r": 1,
|
||||
"bm": 0,
|
||||
"nm": "Fill 1",
|
||||
"mn": "ADBE Vector Graphic - Fill",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "tr",
|
||||
"p": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": 0.833, "y": 0.833 },
|
||||
"o": { "x": 0.167, "y": 0.167 },
|
||||
"t": 0,
|
||||
"s": [71.474, 71.475],
|
||||
"to": [0, 0],
|
||||
"ti": [0, 0]
|
||||
},
|
||||
{
|
||||
"i": { "x": 0.833, "y": 0.833 },
|
||||
"o": { "x": 0.167, "y": 0.167 },
|
||||
"t": 30,
|
||||
"s": [51.474, 51.475],
|
||||
"to": [0, 0],
|
||||
"ti": [0, 0]
|
||||
},
|
||||
{ "t": 58.0000023623884, "s": [71.474, 71.475] }
|
||||
],
|
||||
"ix": 2
|
||||
},
|
||||
"a": { "a": 0, "k": [0, 0], "ix": 1 },
|
||||
"s": { "a": 0, "k": [100, 100], "ix": 3 },
|
||||
"r": { "a": 0, "k": 0, "ix": 6 },
|
||||
"o": { "a": 0, "k": 100, "ix": 7 },
|
||||
"sk": { "a": 0, "k": 0, "ix": 4 },
|
||||
"sa": { "a": 0, "k": 0, "ix": 5 },
|
||||
"nm": "Transform"
|
||||
}
|
||||
],
|
||||
"nm": "Group 5",
|
||||
"np": 4,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 5,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
}
|
||||
],
|
||||
"ip": 0,
|
||||
"op": 60.0000024438501,
|
||||
"st": 0,
|
||||
"bm": 0
|
||||
}
|
||||
],
|
||||
"markers": []
|
||||
}
|
||||
+521
@@ -0,0 +1,521 @@
|
||||
{
|
||||
"v": "5.9.0",
|
||||
"fr": 29.9700012207031,
|
||||
"ip": 0,
|
||||
"op": 60.0000024438501,
|
||||
"w": 500,
|
||||
"h": 500,
|
||||
"nm": "page-hover",
|
||||
"ddd": 0,
|
||||
"assets": [],
|
||||
"layers": [
|
||||
{
|
||||
"ddd": 0,
|
||||
"ind": 1,
|
||||
"ty": 4,
|
||||
"nm": "Layer 1/paper-edgeless-icons Outlines",
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": { "a": 0, "k": 100, "ix": 11 },
|
||||
"r": { "a": 0, "k": 0, "ix": 10 },
|
||||
"p": { "a": 0, "k": [250, 250, 0], "ix": 2, "l": 2 },
|
||||
"a": { "a": 0, "k": [183, 183, 0], "ix": 1, "l": 2 },
|
||||
"s": { "a": 0, "k": [100, 100, 100], "ix": 6, "l": 2 }
|
||||
},
|
||||
"ao": 0,
|
||||
"shapes": [
|
||||
{
|
||||
"ty": "gr",
|
||||
"it": [
|
||||
{
|
||||
"ind": 0,
|
||||
"ty": "sh",
|
||||
"ix": 1,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[22.557, 0],
|
||||
[0, 0],
|
||||
[0, 8.639],
|
||||
[-8.64, 0],
|
||||
[0, 0],
|
||||
[-2.201, 1.651],
|
||||
[0, 9.536],
|
||||
[0, 0],
|
||||
[-8.64, 0],
|
||||
[0, -8.64],
|
||||
[0, 0],
|
||||
[16.892, -16.892]
|
||||
],
|
||||
"o": [
|
||||
[0, 0],
|
||||
[-8.64, 0],
|
||||
[0, -8.64],
|
||||
[0, 0],
|
||||
[17.442, 0],
|
||||
[10.412, -10.453],
|
||||
[0, 0],
|
||||
[0, -8.64],
|
||||
[8.639, 0],
|
||||
[0, 0],
|
||||
[0, 18.155],
|
||||
[-9.027, 8.987]
|
||||
],
|
||||
"v": [
|
||||
[46.947, 182.579],
|
||||
[-109.545, 182.579],
|
||||
[-125.194, 166.93],
|
||||
[-109.545, 151.281],
|
||||
[46.947, 151.281],
|
||||
[78.001, 145.086],
|
||||
[93.895, 114.766],
|
||||
[93.895, -166.93],
|
||||
[109.545, -182.579],
|
||||
[125.194, -166.93],
|
||||
[125.194, 114.766],
|
||||
[99.743, 167.561]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "fl",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.466666696586, 0.458823559331, 0.490196108351, 1],
|
||||
"ix": 4
|
||||
},
|
||||
"o": { "a": 0, "k": 100, "ix": 5 },
|
||||
"r": 1,
|
||||
"bm": 0,
|
||||
"nm": "Fill 1",
|
||||
"mn": "ADBE Vector Graphic - Fill",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "tr",
|
||||
"p": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": 0.833, "y": 0.833 },
|
||||
"o": { "x": 0.167, "y": 0.167 },
|
||||
"t": 0,
|
||||
"s": [240.204, 182.829],
|
||||
"to": [0, 0],
|
||||
"ti": [0, 0]
|
||||
},
|
||||
{
|
||||
"i": { "x": 0.833, "y": 0.833 },
|
||||
"o": { "x": 0.167, "y": 0.167 },
|
||||
"t": 30,
|
||||
"s": [260.204, 202.829],
|
||||
"to": [0, 0],
|
||||
"ti": [0, 0]
|
||||
},
|
||||
{ "t": 58.0000023623884, "s": [240.204, 182.829] }
|
||||
],
|
||||
"ix": 2
|
||||
},
|
||||
"a": { "a": 0, "k": [0, 0], "ix": 1 },
|
||||
"s": { "a": 0, "k": [100, 100], "ix": 3 },
|
||||
"r": { "a": 0, "k": 0, "ix": 6 },
|
||||
"o": { "a": 0, "k": 100, "ix": 7 },
|
||||
"sk": { "a": 0, "k": 0, "ix": 4 },
|
||||
"sa": { "a": 0, "k": 0, "ix": 5 },
|
||||
"nm": "Transform"
|
||||
}
|
||||
],
|
||||
"nm": "Group 1",
|
||||
"np": 2,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 1,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "gr",
|
||||
"it": [
|
||||
{
|
||||
"ind": 0,
|
||||
"ty": "sh",
|
||||
"ix": 1,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[8.64, 0],
|
||||
[0, 8.639],
|
||||
[0, 0],
|
||||
[-16.892, 16.882],
|
||||
[-22.598, 0],
|
||||
[0, 0],
|
||||
[0, -8.64],
|
||||
[8.639, 0],
|
||||
[0, 0],
|
||||
[2.171, -1.65],
|
||||
[0, -9.546],
|
||||
[0, 0]
|
||||
],
|
||||
"o": [
|
||||
[-8.64, 0],
|
||||
[0, 0],
|
||||
[0, -18.145],
|
||||
[8.976, -8.986],
|
||||
[0, 0],
|
||||
[8.639, 0],
|
||||
[0, 8.64],
|
||||
[0, 0],
|
||||
[-17.453, 0],
|
||||
[-10.443, 10.484],
|
||||
[0, 0],
|
||||
[0, 8.639]
|
||||
],
|
||||
"v": [
|
||||
[-109.545, 182.579],
|
||||
[-125.194, 166.93],
|
||||
[-125.194, -114.766],
|
||||
[-99.743, -167.562],
|
||||
[-46.948, -182.579],
|
||||
[109.545, -182.579],
|
||||
[125.194, -166.93],
|
||||
[109.545, -151.281],
|
||||
[-46.948, -151.281],
|
||||
[-77.972, -145.107],
|
||||
[-93.896, -114.766],
|
||||
[-93.896, 166.93]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "fl",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.466666696586, 0.458823559331, 0.490196108351, 1],
|
||||
"ix": 4
|
||||
},
|
||||
"o": { "a": 0, "k": 100, "ix": 5 },
|
||||
"r": 1,
|
||||
"bm": 0,
|
||||
"nm": "Fill 1",
|
||||
"mn": "ADBE Vector Graphic - Fill",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "tr",
|
||||
"p": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": 0.833, "y": 0.833 },
|
||||
"o": { "x": 0.167, "y": 0.167 },
|
||||
"t": 0,
|
||||
"s": [125.443, 182.829],
|
||||
"to": [0, 0],
|
||||
"ti": [0, 0]
|
||||
},
|
||||
{
|
||||
"i": { "x": 0.833, "y": 0.833 },
|
||||
"o": { "x": 0.167, "y": 0.167 },
|
||||
"t": 30,
|
||||
"s": [105.443, 162.829],
|
||||
"to": [0, 0],
|
||||
"ti": [0, 0]
|
||||
},
|
||||
{ "t": 58.0000023623884, "s": [125.443, 182.829] }
|
||||
],
|
||||
"ix": 2
|
||||
},
|
||||
"a": { "a": 0, "k": [0, 0], "ix": 1 },
|
||||
"s": { "a": 0, "k": [100, 100], "ix": 3 },
|
||||
"r": { "a": 0, "k": 0, "ix": 6 },
|
||||
"o": { "a": 0, "k": 100, "ix": 7 },
|
||||
"sk": { "a": 0, "k": 0, "ix": 4 },
|
||||
"sa": { "a": 0, "k": 0, "ix": 5 },
|
||||
"nm": "Transform"
|
||||
}
|
||||
],
|
||||
"nm": "Group 2",
|
||||
"np": 2,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 2,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "gr",
|
||||
"it": [
|
||||
{
|
||||
"ind": 0,
|
||||
"ty": "sh",
|
||||
"ix": 1,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[8.64, 0],
|
||||
[0, 0],
|
||||
[0, 8.639],
|
||||
[-8.64, 0],
|
||||
[0, 0],
|
||||
[0, -8.64]
|
||||
],
|
||||
"o": [
|
||||
[0, 0],
|
||||
[-8.64, 0],
|
||||
[0, -8.64],
|
||||
[0, 0],
|
||||
[8.64, 0],
|
||||
[0, 8.639]
|
||||
],
|
||||
"v": [
|
||||
[67.813, 15.649],
|
||||
[-67.813, 15.649],
|
||||
[-83.463, 0],
|
||||
[-67.813, -15.649],
|
||||
[67.813, -15.649],
|
||||
[83.462, 0]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "fl",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.466666696586, 0.458823559331, 0.490196108351, 1],
|
||||
"ix": 4
|
||||
},
|
||||
"o": { "a": 0, "k": 100, "ix": 5 },
|
||||
"r": 1,
|
||||
"bm": 0,
|
||||
"nm": "Fill 1",
|
||||
"mn": "ADBE Vector Graphic - Fill",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "tr",
|
||||
"p": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": 0.833, "y": 0.833 },
|
||||
"o": { "x": 0.167, "y": 0.167 },
|
||||
"t": 0,
|
||||
"s": [182.824, 271.513],
|
||||
"to": [0, 0],
|
||||
"ti": [0, 0]
|
||||
},
|
||||
{
|
||||
"i": { "x": 0.833, "y": 0.833 },
|
||||
"o": { "x": 0.167, "y": 0.167 },
|
||||
"t": 30,
|
||||
"s": [212.824, 281.513],
|
||||
"to": [0, 0],
|
||||
"ti": [0, 0]
|
||||
},
|
||||
{ "t": 58.0000023623884, "s": [182.824, 271.513] }
|
||||
],
|
||||
"ix": 2
|
||||
},
|
||||
"a": { "a": 0, "k": [0, 0], "ix": 1 },
|
||||
"s": { "a": 0, "k": [100, 100], "ix": 3 },
|
||||
"r": { "a": 0, "k": 0, "ix": 6 },
|
||||
"o": { "a": 0, "k": 100, "ix": 7 },
|
||||
"sk": { "a": 0, "k": 0, "ix": 4 },
|
||||
"sa": { "a": 0, "k": 0, "ix": 5 },
|
||||
"nm": "Transform"
|
||||
}
|
||||
],
|
||||
"nm": "Group 3",
|
||||
"np": 2,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 3,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "gr",
|
||||
"it": [
|
||||
{
|
||||
"ind": 0,
|
||||
"ty": "sh",
|
||||
"ix": 1,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[0, 0],
|
||||
[0, -5.757],
|
||||
[0, 0],
|
||||
[-5.756, 0],
|
||||
[0, 0],
|
||||
[0, 5.756],
|
||||
[0, 0],
|
||||
[5.747, 0]
|
||||
],
|
||||
"o": [
|
||||
[-5.756, 0],
|
||||
[0, 0],
|
||||
[0, 5.756],
|
||||
[0, 0],
|
||||
[5.747, 0],
|
||||
[0, 0],
|
||||
[0, -5.757],
|
||||
[0, 0]
|
||||
],
|
||||
"v": [
|
||||
[-41.732, -31.294],
|
||||
[-52.165, -20.86],
|
||||
[-52.165, 20.87],
|
||||
[-41.732, 31.303],
|
||||
[41.73, 31.303],
|
||||
[52.163, 20.87],
|
||||
[52.163, -20.86],
|
||||
[41.73, -31.294]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 1",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ind": 1,
|
||||
"ty": "sh",
|
||||
"ix": 2,
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [
|
||||
[23.006, 0],
|
||||
[0, 0],
|
||||
[0, 23.015],
|
||||
[0, 0],
|
||||
[-23.015, 0],
|
||||
[0, 0],
|
||||
[0, -23.016],
|
||||
[0, 0]
|
||||
],
|
||||
"o": [
|
||||
[0, 0],
|
||||
[-23.015, 0],
|
||||
[0, 0],
|
||||
[0, -23.016],
|
||||
[0, 0],
|
||||
[23.006, 0],
|
||||
[0, 0],
|
||||
[0, 23.015]
|
||||
],
|
||||
"v": [
|
||||
[41.73, 62.592],
|
||||
[-41.732, 62.592],
|
||||
[-83.463, 20.87],
|
||||
[-83.463, -20.86],
|
||||
[-41.732, -62.592],
|
||||
[41.73, -62.592],
|
||||
[83.463, -20.86],
|
||||
[83.463, 20.87]
|
||||
],
|
||||
"c": true
|
||||
},
|
||||
"ix": 2
|
||||
},
|
||||
"nm": "Path 2",
|
||||
"mn": "ADBE Vector Shape - Group",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "mm",
|
||||
"mm": 1,
|
||||
"nm": "Merge Paths 1",
|
||||
"mn": "ADBE Vector Filter - Merge",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "fl",
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [0.466666696586, 0.458823559331, 0.490196108351, 1],
|
||||
"ix": 4
|
||||
},
|
||||
"o": { "a": 0, "k": 100, "ix": 5 },
|
||||
"r": 1,
|
||||
"bm": 0,
|
||||
"nm": "Fill 1",
|
||||
"mn": "ADBE Vector Graphic - Fill",
|
||||
"hd": false
|
||||
},
|
||||
{
|
||||
"ty": "tr",
|
||||
"p": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{
|
||||
"i": { "x": 0.833, "y": 0.833 },
|
||||
"o": { "x": 0.167, "y": 0.167 },
|
||||
"t": 0,
|
||||
"s": [182.824, 141.088],
|
||||
"to": [0, 0],
|
||||
"ti": [0, 0]
|
||||
},
|
||||
{
|
||||
"i": { "x": 0.833, "y": 0.833 },
|
||||
"o": { "x": 0.167, "y": 0.167 },
|
||||
"t": 30,
|
||||
"s": [152.824, 131.088],
|
||||
"to": [0, 0],
|
||||
"ti": [0, 0]
|
||||
},
|
||||
{ "t": 58.0000023623884, "s": [182.824, 141.088] }
|
||||
],
|
||||
"ix": 2
|
||||
},
|
||||
"a": { "a": 0, "k": [0, 0], "ix": 1 },
|
||||
"s": { "a": 0, "k": [100, 100], "ix": 3 },
|
||||
"r": { "a": 0, "k": 0, "ix": 6 },
|
||||
"o": { "a": 0, "k": 100, "ix": 7 },
|
||||
"sk": { "a": 0, "k": 0, "ix": 4 },
|
||||
"sa": { "a": 0, "k": 0, "ix": 5 },
|
||||
"nm": "Transform"
|
||||
}
|
||||
],
|
||||
"nm": "Group 4",
|
||||
"np": 4,
|
||||
"cix": 2,
|
||||
"bm": 0,
|
||||
"ix": 4,
|
||||
"mn": "ADBE Vector Group",
|
||||
"hd": false
|
||||
}
|
||||
],
|
||||
"ip": 0,
|
||||
"op": 60.0000024438501,
|
||||
"st": 0,
|
||||
"bm": 0
|
||||
}
|
||||
],
|
||||
"markers": []
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { assertExists } from '@blocksuite/store';
|
||||
import { useAtomValue, useSetAtom } from 'jotai';
|
||||
import type { CSSProperties } from 'react';
|
||||
|
||||
import { workspacePreferredModeAtom } from '../../../../atoms';
|
||||
import { usePageMeta } from '../../../../hooks/use-page-meta';
|
||||
import type { BlockSuiteWorkspace } from '../../../../shared';
|
||||
import { toast } from '../../../../utils';
|
||||
import { StyledEditorModeSwitch } from './style';
|
||||
import { EdgelessSwitchItem, PageSwitchItem } from './switch-items';
|
||||
|
||||
export type EditorModeSwitchProps = {
|
||||
// todo(himself65): combine these two properties
|
||||
blockSuiteWorkspace: BlockSuiteWorkspace;
|
||||
pageId: string;
|
||||
style?: CSSProperties;
|
||||
};
|
||||
|
||||
export const EditorModeSwitch = ({
|
||||
style,
|
||||
blockSuiteWorkspace,
|
||||
pageId,
|
||||
}: EditorModeSwitchProps) => {
|
||||
const currentMode =
|
||||
useAtomValue(workspacePreferredModeAtom)[pageId] ?? 'page';
|
||||
const setMode = useSetAtom(workspacePreferredModeAtom);
|
||||
const pageMeta = usePageMeta(blockSuiteWorkspace).find(
|
||||
meta => meta.id === pageId
|
||||
);
|
||||
assertExists(pageMeta);
|
||||
const { trash } = pageMeta;
|
||||
|
||||
return (
|
||||
<StyledEditorModeSwitch
|
||||
style={style}
|
||||
switchLeft={currentMode === 'page'}
|
||||
showAlone={trash}
|
||||
>
|
||||
<PageSwitchItem
|
||||
data-testid="switch-page-mode-button"
|
||||
active={currentMode === 'page'}
|
||||
hide={trash && currentMode !== 'page'}
|
||||
onClick={() => {
|
||||
setMode(mode => ({ ...mode, [pageMeta.id]: 'page' }));
|
||||
toast('Page mode');
|
||||
}}
|
||||
/>
|
||||
<EdgelessSwitchItem
|
||||
data-testid="switch-edgeless-mode-button"
|
||||
active={currentMode === 'edgeless'}
|
||||
hide={trash && currentMode !== 'edgeless'}
|
||||
onClick={() => {
|
||||
setMode(mode => ({ ...mode, [pageMeta.id]: 'edgeless' }));
|
||||
toast('Edgeless mode');
|
||||
}}
|
||||
/>
|
||||
</StyledEditorModeSwitch>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
import { displayFlex, styled } from '@affine/component';
|
||||
|
||||
export const StyledEditorModeSwitch = styled('div')<{
|
||||
switchLeft: boolean;
|
||||
showAlone?: boolean;
|
||||
}>(({ theme, switchLeft, showAlone }) => {
|
||||
const {
|
||||
palette: { mode },
|
||||
} = theme;
|
||||
return {
|
||||
width: showAlone ? '40px' : '78px',
|
||||
height: '32px',
|
||||
background: showAlone
|
||||
? 'transparent'
|
||||
: mode === 'dark'
|
||||
? '#242424'
|
||||
: '#F9F9FB',
|
||||
borderRadius: '12px',
|
||||
...displayFlex('space-between', 'center'),
|
||||
padding: '0 8px',
|
||||
position: 'relative',
|
||||
|
||||
'::after': {
|
||||
content: '""',
|
||||
display: showAlone ? 'none' : 'block',
|
||||
width: '24px',
|
||||
height: '24px',
|
||||
background: theme.colors.pageBackground,
|
||||
boxShadow:
|
||||
mode === 'dark'
|
||||
? '0px 0px 6px rgba(22, 22, 22, 0.6)'
|
||||
: '0px 0px 6px #E2E2E2',
|
||||
borderRadius: '8px',
|
||||
zIndex: 1,
|
||||
position: 'absolute',
|
||||
transform: `translateX(${switchLeft ? '0' : '38px'})`,
|
||||
transition: 'all .15s',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledSwitchItem = styled('button')<{
|
||||
active?: boolean;
|
||||
hide?: boolean;
|
||||
}>(({ theme, active = false, hide = false }) => {
|
||||
return {
|
||||
width: '24px',
|
||||
height: '24px',
|
||||
borderRadius: '8px',
|
||||
color: active ? theme.colors.primaryColor : theme.colors.iconColor,
|
||||
display: hide ? 'none' : 'inline-flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'relative',
|
||||
zIndex: 2,
|
||||
fontSize: '20px',
|
||||
path: {
|
||||
fill: 'currentColor',
|
||||
},
|
||||
};
|
||||
});
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
import type { HTMLAttributes } from 'react';
|
||||
import type React from 'react';
|
||||
import { cloneElement, useState } from 'react';
|
||||
|
||||
import Lottie from './CustomLottie';
|
||||
import { StyledSwitchItem } from './style';
|
||||
|
||||
type HoverAnimateControllerProps = {
|
||||
active?: boolean;
|
||||
hide?: boolean;
|
||||
children: React.ReactElement;
|
||||
} & HTMLAttributes<HTMLButtonElement>;
|
||||
|
||||
const HoverAnimateController = ({
|
||||
active,
|
||||
hide,
|
||||
children,
|
||||
...props
|
||||
}: HoverAnimateControllerProps) => {
|
||||
const [startAnimate, setStartAnimate] = useState(false);
|
||||
return (
|
||||
<StyledSwitchItem
|
||||
hide={hide}
|
||||
active={active}
|
||||
onMouseEnter={() => {
|
||||
setStartAnimate(true);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setStartAnimate(false);
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{cloneElement(children, {
|
||||
isStopped: !startAnimate,
|
||||
speed: 5,
|
||||
width: 20,
|
||||
height: 20,
|
||||
})}
|
||||
</StyledSwitchItem>
|
||||
);
|
||||
};
|
||||
|
||||
export const PageSwitchItem = (
|
||||
props: Omit<HoverAnimateControllerProps, 'children'>
|
||||
) => {
|
||||
return (
|
||||
<HoverAnimateController {...props}>
|
||||
<Lottie
|
||||
options={{
|
||||
loop: false,
|
||||
autoplay: false,
|
||||
animationData: require('./animation-data/page-hover.json'),
|
||||
rendererSettings: {
|
||||
preserveAspectRatio: 'xMidYMid slice',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</HoverAnimateController>
|
||||
);
|
||||
};
|
||||
|
||||
export const EdgelessSwitchItem = (
|
||||
props: Omit<HoverAnimateControllerProps, 'children'>
|
||||
) => {
|
||||
return (
|
||||
<HoverAnimateController {...props}>
|
||||
<Lottie
|
||||
options={{
|
||||
loop: false,
|
||||
autoplay: false,
|
||||
animationData: require('./animation-data/edgeless-hover.json'),
|
||||
rendererSettings: {
|
||||
preserveAspectRatio: 'xMidYMid slice',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</HoverAnimateController>
|
||||
);
|
||||
};
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
// fixme(himself65): refactor this file
|
||||
import { FlexWrapper, IconButton, Menu, MenuItem } from '@affine/component';
|
||||
import { useTranslation } from '@affine/i18n';
|
||||
import {
|
||||
EdgelessIcon,
|
||||
FavoritedIcon,
|
||||
FavoriteIcon,
|
||||
MoreVerticalIcon,
|
||||
PageIcon,
|
||||
} from '@blocksuite/icons';
|
||||
import { assertExists } from '@blocksuite/store';
|
||||
import { useTheme } from '@mui/material';
|
||||
import { useAtom } from 'jotai';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { workspacePreferredModeAtom } from '../../../../atoms';
|
||||
import { useCurrentPageId } from '../../../../hooks/current/use-current-page-id';
|
||||
import { useCurrentWorkspace } from '../../../../hooks/current/use-current-workspace';
|
||||
import {
|
||||
usePageMeta,
|
||||
usePageMetaHelper,
|
||||
} from '../../../../hooks/use-page-meta';
|
||||
import { toast } from '../../../../utils';
|
||||
import {
|
||||
Export,
|
||||
MoveTo,
|
||||
MoveToTrash,
|
||||
} from '../../../affine/operation-menu-items';
|
||||
|
||||
export const EditorOptionMenu = () => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
|
||||
// fixme(himself65): remove these hooks ASAP
|
||||
const [workspace] = useCurrentWorkspace();
|
||||
const [pageId] = useCurrentPageId();
|
||||
assertExists(workspace);
|
||||
assertExists(pageId);
|
||||
const blockSuiteWorkspace = workspace.blockSuiteWorkspace;
|
||||
const pageMeta = usePageMeta(blockSuiteWorkspace).find(
|
||||
meta => meta.id === pageId
|
||||
);
|
||||
const allMetas = usePageMeta(blockSuiteWorkspace);
|
||||
const [record, set] = useAtom(workspacePreferredModeAtom);
|
||||
const mode = record[pageId] ?? 'page';
|
||||
assertExists(pageMeta);
|
||||
const { favorite } = pageMeta;
|
||||
const { setPageMeta } = usePageMetaHelper(blockSuiteWorkspace);
|
||||
const [openConfirm, setOpenConfirm] = useState(false);
|
||||
|
||||
const EditMenu = (
|
||||
<>
|
||||
<MenuItem
|
||||
data-testid="editor-option-menu-favorite"
|
||||
onClick={() => {
|
||||
setPageMeta(pageId, { favorite: !favorite });
|
||||
toast(
|
||||
favorite ? t('Removed from Favorites') : t('Added to Favorites')
|
||||
);
|
||||
}}
|
||||
icon={
|
||||
favorite ? (
|
||||
<FavoritedIcon style={{ color: theme.colors.primaryColor }} />
|
||||
) : (
|
||||
<FavoriteIcon />
|
||||
)
|
||||
}
|
||||
>
|
||||
{favorite ? t('Remove from favorites') : t('Add to Favorites')}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={mode === 'page' ? <EdgelessIcon /> : <PageIcon />}
|
||||
data-testid="editor-option-menu-edgeless"
|
||||
onClick={() => {
|
||||
set(record => ({
|
||||
...record,
|
||||
[pageId]: mode === 'page' ? 'edgeless' : 'page',
|
||||
}));
|
||||
}}
|
||||
>
|
||||
{t('Convert to ')}
|
||||
{mode === 'page' ? t('Edgeless') : t('Page')}
|
||||
</MenuItem>
|
||||
<Export />
|
||||
{!pageMeta.isRootPinboard && (
|
||||
<MoveTo
|
||||
metas={allMetas}
|
||||
currentMeta={pageMeta}
|
||||
blockSuiteWorkspace={blockSuiteWorkspace}
|
||||
/>
|
||||
)}
|
||||
{!pageMeta.isRootPinboard && (
|
||||
<MoveToTrash
|
||||
testId="editor-option-menu-delete"
|
||||
onItemClick={() => {
|
||||
setOpenConfirm(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FlexWrapper alignItems="center" justifyContent="center">
|
||||
<Menu
|
||||
width={276}
|
||||
content={EditMenu}
|
||||
placement="bottom-end"
|
||||
disablePortal={true}
|
||||
trigger="click"
|
||||
>
|
||||
<IconButton data-testid="editor-option-menu" iconSize={[24, 24]}>
|
||||
<MoreVerticalIcon />
|
||||
</IconButton>
|
||||
</Menu>
|
||||
<MoveToTrash.ConfirmModal
|
||||
open={openConfirm}
|
||||
meta={pageMeta}
|
||||
onConfirm={() => {
|
||||
toast(t('Moved to Trash'));
|
||||
setPageMeta(pageMeta.id, {
|
||||
trash: true,
|
||||
trashDate: +new Date(),
|
||||
});
|
||||
}}
|
||||
onCancel={() => {
|
||||
setOpenConfirm(false);
|
||||
}}
|
||||
/>
|
||||
</FlexWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,161 @@
|
||||
import { displayFlex, IconButton, styled, Tooltip } from '@affine/component';
|
||||
import { useTranslation } from '@affine/i18n';
|
||||
import {
|
||||
getLoginStorage,
|
||||
setLoginStorage,
|
||||
SignMethod,
|
||||
} from '@affine/workspace/affine/login';
|
||||
import type { LocalWorkspace } from '@affine/workspace/type';
|
||||
import { WorkspaceFlavour } from '@affine/workspace/type';
|
||||
import {
|
||||
CloudWorkspaceIcon,
|
||||
LocalWorkspaceIcon,
|
||||
NoNetworkIcon,
|
||||
} from '@blocksuite/icons';
|
||||
import { assertEquals, assertExists } from '@blocksuite/store';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { useCurrentWorkspace } from '../../../../hooks/current/use-current-workspace';
|
||||
import { useTransformWorkspace } from '../../../../hooks/use-transform-workspace';
|
||||
import { affineAuth } from '../../../../plugins/affine';
|
||||
import type { AffineOfficialWorkspace } from '../../../../shared';
|
||||
import { TransformWorkspaceToAffineModal } from '../../../affine/transform-workspace-to-affine-modal';
|
||||
|
||||
const IconWrapper = styled('div')(({ theme }) => {
|
||||
return {
|
||||
width: '32px',
|
||||
height: '32px',
|
||||
marginRight: '12px',
|
||||
fontSize: '24px',
|
||||
color: theme.colors.iconColor,
|
||||
...displayFlex('center', 'center'),
|
||||
};
|
||||
});
|
||||
|
||||
const getStatus = (workspace: AffineOfficialWorkspace) => {
|
||||
if (!navigator.onLine) {
|
||||
return 'offline';
|
||||
}
|
||||
if (workspace.flavour === 'local') {
|
||||
return 'local';
|
||||
}
|
||||
return 'cloud';
|
||||
};
|
||||
|
||||
export const SyncUser = () => {
|
||||
//#region fixme(himself65): remove these hooks ASAP
|
||||
const [workspace] = useCurrentWorkspace();
|
||||
assertExists(workspace);
|
||||
const router = useRouter();
|
||||
|
||||
const [status, setStatus] = useState<'offline' | 'local' | 'cloud'>(
|
||||
getStatus(workspace)
|
||||
);
|
||||
const [prevWorkspace, setPrevWorkspace] = useState(workspace);
|
||||
if (prevWorkspace !== workspace) {
|
||||
setPrevWorkspace(workspace);
|
||||
setStatus(getStatus(workspace));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const online = () => {
|
||||
setStatus(getStatus(workspace));
|
||||
};
|
||||
|
||||
const offline = () => {
|
||||
setStatus('offline');
|
||||
};
|
||||
window.addEventListener('online', online);
|
||||
window.addEventListener('offline', offline);
|
||||
return () => {
|
||||
window.removeEventListener('online', online);
|
||||
window.removeEventListener('offline', offline);
|
||||
};
|
||||
}, [workspace]);
|
||||
//#endregion
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const transformWorkspace = useTransformWorkspace();
|
||||
|
||||
if (status === 'offline') {
|
||||
return (
|
||||
<Tooltip
|
||||
content={t('Please make sure you are online')}
|
||||
placement="bottom-end"
|
||||
>
|
||||
<IconWrapper>
|
||||
<NoNetworkIcon />
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
if (status === 'local') {
|
||||
return (
|
||||
<>
|
||||
<Tooltip
|
||||
content={t('Saved then enable AFFiNE Cloud')}
|
||||
placement="bottom-end"
|
||||
>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setOpen(true);
|
||||
}}
|
||||
style={{ marginRight: '12px' }}
|
||||
>
|
||||
<LocalWorkspaceIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<TransformWorkspaceToAffineModal
|
||||
open={open}
|
||||
onClose={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
onConform={async () => {
|
||||
if (!getLoginStorage()) {
|
||||
const response = await affineAuth.generateToken(
|
||||
SignMethod.Google
|
||||
);
|
||||
if (response) {
|
||||
setLoginStorage(response);
|
||||
}
|
||||
router.reload();
|
||||
return;
|
||||
}
|
||||
assertEquals(workspace.flavour, WorkspaceFlavour.LOCAL);
|
||||
const id = await transformWorkspace(
|
||||
WorkspaceFlavour.LOCAL,
|
||||
WorkspaceFlavour.AFFINE,
|
||||
workspace as LocalWorkspace
|
||||
);
|
||||
// fixme(himself65): refactor this
|
||||
router
|
||||
.replace({
|
||||
pathname: `/workspace/[workspaceId]/all`,
|
||||
query: {
|
||||
workspaceId: id,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
router.reload();
|
||||
});
|
||||
setOpen(false);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip content={t('Synced with AFFiNE Cloud')} placement="bottom-end">
|
||||
<IconWrapper>
|
||||
<CloudWorkspaceIcon />
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export default SyncUser;
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
import { Button, Confirm } from '@affine/component';
|
||||
import { useTranslation } from '@affine/i18n';
|
||||
import { assertExists } from '@blocksuite/store';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { useCurrentPageId } from '../../../../hooks/current/use-current-page-id';
|
||||
import { useCurrentWorkspace } from '../../../../hooks/current/use-current-workspace';
|
||||
import {
|
||||
usePageMeta,
|
||||
usePageMetaHelper,
|
||||
} from '../../../../hooks/use-page-meta';
|
||||
|
||||
export const TrashButtonGroup = () => {
|
||||
// fixme(himself65): remove these hooks ASAP
|
||||
const [workspace] = useCurrentWorkspace();
|
||||
const [pageId] = useCurrentPageId();
|
||||
assertExists(workspace);
|
||||
assertExists(pageId);
|
||||
const blockSuiteWorkspace = workspace.blockSuiteWorkspace;
|
||||
const pageMeta = usePageMeta(blockSuiteWorkspace).find(
|
||||
meta => meta.id === pageId
|
||||
);
|
||||
assertExists(pageMeta);
|
||||
const { setPageMeta } = usePageMetaHelper(blockSuiteWorkspace);
|
||||
const router = useRouter();
|
||||
//
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
bold={true}
|
||||
shape="round"
|
||||
style={{ marginRight: '24px' }}
|
||||
onClick={() => {
|
||||
setPageMeta(pageId, { trash: false });
|
||||
}}
|
||||
>
|
||||
{t('Restore it')}
|
||||
</Button>
|
||||
<Button
|
||||
bold={true}
|
||||
shape="round"
|
||||
type="danger"
|
||||
onClick={() => {
|
||||
setOpen(true);
|
||||
}}
|
||||
>
|
||||
{t('Delete permanently')}
|
||||
</Button>
|
||||
<Confirm
|
||||
title={t('TrashButtonGroupTitle')}
|
||||
content={t('TrashButtonGroupDescription')}
|
||||
confirmText={t('Delete')}
|
||||
confirmType="danger"
|
||||
open={open}
|
||||
onConfirm={() => {
|
||||
// fixme(himself65): remove these hooks ASAP
|
||||
router
|
||||
.push({
|
||||
pathname: '/workspace/[workspaceId]/all',
|
||||
query: {
|
||||
workspaceId: workspace.id,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
blockSuiteWorkspace.removePage(pageId);
|
||||
});
|
||||
}}
|
||||
onCancel={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
onClose={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TrashButtonGroup;
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import type { CSSProperties, DOMAttributes } from 'react';
|
||||
type IconProps = {
|
||||
style?: CSSProperties;
|
||||
} & DOMAttributes<SVGElement>;
|
||||
|
||||
export const MoonIcon = ({ style = {}, ...props }: IconProps) => {
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
style={style}
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M9.54893 3.31407C9.33328 3.08158 9.27962 2.74521 9.41255 2.45912C9.54547 2.17302 9.83936 1.99233 10.1595 1.99986C13.4456 2.07712 16.5114 4.08044 17.7359 7.29071C19.3437 11.5057 17.1672 16.2024 12.8744 17.781C9.60251 18.9843 6.04745 18.0285 3.82974 15.6428C3.61375 15.4104 3.55978 15.0739 3.69257 14.7876C3.82537 14.5014 4.11931 14.3205 4.43962 14.3279C5.27228 14.3474 6.12412 14.2171 6.94979 13.9135C10.415 12.6391 12.172 8.84782 10.8741 5.44537C10.5657 4.63692 10.1061 3.91474 9.54893 3.31407Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const SunIcon = ({ style = {}, ...props }: IconProps) => {
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
style={style}
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M10.8002 2.5002C10.8002 2.05837 10.442 1.7002 10.0002 1.7002C9.55837 1.7002 9.2002 2.05837 9.2002 2.5002V3.33353C9.2002 3.77536 9.55837 4.13353 10.0002 4.13353C10.442 4.13353 10.8002 3.77536 10.8002 3.33353V2.5002ZM5.14921 4.01784C4.83679 3.70542 4.33026 3.70542 4.01784 4.01784C3.70542 4.33026 3.70542 4.83679 4.01784 5.14921L4.69627 5.82764C5.00869 6.14006 5.51522 6.14006 5.82764 5.82764C6.14006 5.51522 6.14006 5.00869 5.82764 4.69627L5.14921 4.01784ZM15.9825 5.1492C16.2949 4.83678 16.2949 4.33025 15.9825 4.01783C15.6701 3.70542 15.1636 3.70543 14.8511 4.01785L14.1727 4.69628C13.8603 5.00871 13.8603 5.51524 14.1728 5.82765C14.4852 6.14007 14.9917 6.14006 15.3041 5.82763L15.9825 5.1492ZM10.0002 5.86686C7.71742 5.86686 5.86686 7.71742 5.86686 10.0002C5.86686 12.283 7.71742 14.1335 10.0002 14.1335C12.283 14.1335 14.1335 12.283 14.1335 10.0002C14.1335 7.71742 12.283 5.86686 10.0002 5.86686ZM2.5002 9.2002C2.05837 9.2002 1.7002 9.55837 1.7002 10.0002C1.7002 10.442 2.05837 10.8002 2.5002 10.8002H3.33353C3.77536 10.8002 4.13353 10.442 4.13353 10.0002C4.13353 9.55837 3.77536 9.2002 3.33353 9.2002H2.5002ZM16.6669 9.2002C16.225 9.2002 15.8669 9.55837 15.8669 10.0002C15.8669 10.442 16.225 10.8002 16.6669 10.8002H17.5002C17.942 10.8002 18.3002 10.442 18.3002 10.0002C18.3002 9.55837 17.942 9.2002 17.5002 9.2002H16.6669ZM5.82623 15.309C6.13943 14.9973 6.14069 14.4908 5.82906 14.1776C5.51742 13.8644 5.01089 13.8631 4.69769 14.1748L4.01926 14.8498C3.70606 15.1615 3.70479 15.668 4.01643 15.9812C4.32807 16.2944 4.8346 16.2956 5.1478 15.984L5.82623 15.309ZM15.3027 14.1748C14.9895 13.8631 14.483 13.8644 14.1713 14.1776C13.8597 14.4908 13.861 14.9973 14.1742 15.3089L14.8526 15.984C15.1658 16.2956 15.6723 16.2944 15.9839 15.9812C16.2956 15.668 16.2943 15.1615 15.9811 14.8498L15.3027 14.1748ZM10.8002 16.6669C10.8002 16.225 10.442 15.8669 10.0002 15.8669C9.55837 15.8669 9.2002 16.225 9.2002 16.6669V17.5002C9.2002 17.942 9.55837 18.3002 10.0002 18.3002C10.442 18.3002 10.8002 17.942 10.8002 17.5002V16.6669Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
import { DarkModeIcon, LightModeIcon } from '@blocksuite/icons';
|
||||
import { useTheme } from 'next-themes';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { StyledSwitchItem, StyledThemeModeSwitch } from './style';
|
||||
export const ThemeModeSwitch = () => {
|
||||
const { setTheme, resolvedTheme } = useTheme();
|
||||
const [isHover, setIsHover] = useState(false);
|
||||
return (
|
||||
<StyledThemeModeSwitch
|
||||
data-testid="change-theme-container"
|
||||
onMouseEnter={() => {
|
||||
setIsHover(true);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setIsHover(false);
|
||||
}}
|
||||
>
|
||||
<StyledSwitchItem
|
||||
data-testid="change-theme-light"
|
||||
active={resolvedTheme === 'light'}
|
||||
isHover={isHover}
|
||||
onClick={() => {
|
||||
setTheme('light');
|
||||
}}
|
||||
>
|
||||
<LightModeIcon />
|
||||
</StyledSwitchItem>
|
||||
<StyledSwitchItem
|
||||
data-testid="change-theme-dark"
|
||||
active={resolvedTheme === 'dark'}
|
||||
isHover={isHover}
|
||||
onClick={() => {
|
||||
setTheme('dark');
|
||||
}}
|
||||
>
|
||||
<DarkModeIcon />
|
||||
</StyledSwitchItem>
|
||||
</StyledThemeModeSwitch>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThemeModeSwitch;
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
import { css, displayFlex, keyframes, styled } from '@affine/component';
|
||||
// @ts-ignore
|
||||
import spring, { toString } from 'css-spring';
|
||||
|
||||
const ANIMATE_DURATION = 400;
|
||||
|
||||
export const StyledThemeModeSwitch = styled('button')(({ theme }) => {
|
||||
return {
|
||||
width: '32px',
|
||||
height: '32px',
|
||||
borderRadius: '6px',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: 'transparent',
|
||||
position: 'relative',
|
||||
color: theme.colors.iconColor,
|
||||
fontSize: '24px',
|
||||
};
|
||||
});
|
||||
export const StyledSwitchItem = styled('div')<{
|
||||
active: boolean;
|
||||
isHover: boolean;
|
||||
}>(({ active, isHover, theme }) => {
|
||||
const activeRaiseAnimate = toString(
|
||||
spring({ top: '0' }, { top: '-100%' }, { preset: 'gentle' })
|
||||
);
|
||||
const raiseAnimate = toString(
|
||||
spring({ top: '100%' }, { top: '0' }, { preset: 'gentle' })
|
||||
);
|
||||
const activeDeclineAnimate = toString(
|
||||
spring({ top: '-100%' }, { top: '0' }, { preset: 'gentle' })
|
||||
);
|
||||
const declineAnimate = toString(
|
||||
spring({ top: '0' }, { top: '100%' }, { preset: 'gentle' })
|
||||
);
|
||||
|
||||
const activeStyle = active
|
||||
? {
|
||||
color: theme.colors.iconColor,
|
||||
top: '0',
|
||||
animation: css`
|
||||
${keyframes`${
|
||||
isHover ? activeRaiseAnimate : activeDeclineAnimate
|
||||
}`} ${ANIMATE_DURATION}ms forwards
|
||||
`,
|
||||
animationDirection: isHover ? 'normal' : 'alternate',
|
||||
}
|
||||
: {
|
||||
top: '100%',
|
||||
color: theme.colors.primaryColor,
|
||||
backgroundColor: theme.colors.hoverBackground,
|
||||
animation: css`
|
||||
${keyframes`${
|
||||
isHover ? raiseAnimate : declineAnimate
|
||||
}`} ${ANIMATE_DURATION}ms forwards
|
||||
`,
|
||||
animationDirection: isHover ? 'normal' : 'alternate',
|
||||
};
|
||||
return css`
|
||||
${css(displayFlex('center', 'center'))}
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
cursor: pointer;
|
||||
color: ${activeStyle.color}
|
||||
top: ${activeStyle.top};
|
||||
background-color: ${activeStyle.backgroundColor};
|
||||
animation: ${activeStyle.animation};
|
||||
animation-direction: ${activeStyle.animationDirection};
|
||||
//svg {
|
||||
// width: 24px;
|
||||
// height: 24px;
|
||||
//},
|
||||
`;
|
||||
});
|
||||
@@ -0,0 +1,177 @@
|
||||
import { useTranslation } from '@affine/i18n';
|
||||
import { CloseIcon } from '@blocksuite/icons';
|
||||
import type { Page } from '@blocksuite/store';
|
||||
import type { HTMLAttributes, PropsWithChildren } from 'react';
|
||||
import type React from 'react';
|
||||
import { forwardRef, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import {
|
||||
useSidebarFloating,
|
||||
useSidebarStatus,
|
||||
} from '../../../hooks/use-sidebar-status';
|
||||
import type { AffineOfficialWorkspace } from '../../../shared';
|
||||
import { SidebarSwitch } from '../../affine/sidebar-switch';
|
||||
import { EditorOptionMenu } from './header-right-items/EditorOptionMenu';
|
||||
import SyncUser from './header-right-items/SyncUser';
|
||||
import ThemeModeSwitch from './header-right-items/theme-mode-switch';
|
||||
import TrashButtonGroup from './header-right-items/TrashButtonGroup';
|
||||
import {
|
||||
StyledBrowserWarning,
|
||||
StyledCloseButton,
|
||||
StyledHeader,
|
||||
StyledHeaderContainer,
|
||||
StyledHeaderRightSide,
|
||||
} from './styles';
|
||||
import { OSWarningMessage, shouldShowWarning } from './utils';
|
||||
|
||||
const BrowserWarning = ({
|
||||
show,
|
||||
onClose,
|
||||
}: {
|
||||
show: boolean;
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
return (
|
||||
<StyledBrowserWarning show={show}>
|
||||
<OSWarningMessage />
|
||||
<StyledCloseButton onClick={onClose}>
|
||||
<CloseIcon />
|
||||
</StyledCloseButton>
|
||||
</StyledBrowserWarning>
|
||||
);
|
||||
};
|
||||
|
||||
export type BaseHeaderProps<
|
||||
Workspace extends AffineOfficialWorkspace = AffineOfficialWorkspace
|
||||
> = {
|
||||
workspace: Workspace;
|
||||
currentPage: Page | null;
|
||||
isPublic: boolean;
|
||||
isPreview: boolean;
|
||||
};
|
||||
|
||||
export const enum HeaderRightItemName {
|
||||
EditorOptionMenu = 'editorOptionMenu',
|
||||
TrashButtonGroup = 'trashButtonGroup',
|
||||
ThemeModeSwitch = 'themeModeSwitch',
|
||||
SyncUser = 'syncUser',
|
||||
ShareMenu = 'shareMenu',
|
||||
}
|
||||
|
||||
type HeaderItem = {
|
||||
Component: React.FC<BaseHeaderProps>;
|
||||
// todo: public workspace should be one of the flavour
|
||||
availableWhen: (
|
||||
workspace: AffineOfficialWorkspace,
|
||||
currentPage: Page | null,
|
||||
status: {
|
||||
isPublic: boolean;
|
||||
isPreview: boolean;
|
||||
}
|
||||
) => boolean;
|
||||
};
|
||||
|
||||
const HeaderRightItems: Record<HeaderRightItemName, HeaderItem> = {
|
||||
[HeaderRightItemName.TrashButtonGroup]: {
|
||||
Component: TrashButtonGroup,
|
||||
availableWhen: (_, currentPage) => {
|
||||
return currentPage?.meta.trash === true;
|
||||
},
|
||||
},
|
||||
[HeaderRightItemName.SyncUser]: {
|
||||
Component: SyncUser,
|
||||
availableWhen: (_, currentPage, { isPublic, isPreview }) => {
|
||||
return !isPublic && !isPreview;
|
||||
},
|
||||
},
|
||||
[HeaderRightItemName.ThemeModeSwitch]: {
|
||||
Component: ThemeModeSwitch,
|
||||
availableWhen: (_, currentPage) => {
|
||||
return currentPage?.meta.trash !== true;
|
||||
},
|
||||
},
|
||||
[HeaderRightItemName.EditorOptionMenu]: {
|
||||
Component: EditorOptionMenu,
|
||||
availableWhen: (_, currentPage, { isPublic, isPreview }) => {
|
||||
return !!currentPage && !isPublic && !isPreview;
|
||||
},
|
||||
},
|
||||
[HeaderRightItemName.ShareMenu]: {
|
||||
Component: () => null,
|
||||
availableWhen: (_, currentPage, { isPublic, isPreview }) => {
|
||||
return false;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export type HeaderProps = BaseHeaderProps;
|
||||
|
||||
export const Header = forwardRef<
|
||||
HTMLDivElement,
|
||||
PropsWithChildren<HeaderProps> & HTMLAttributes<HTMLDivElement>
|
||||
>((props, ref) => {
|
||||
const [showWarning, setShowWarning] = useState(false);
|
||||
useEffect(() => {
|
||||
setShowWarning(shouldShowWarning());
|
||||
}, []);
|
||||
const [open] = useSidebarStatus();
|
||||
const sidebarFloating = useSidebarFloating();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<StyledHeaderContainer
|
||||
sidebarFloating={sidebarFloating && open}
|
||||
ref={ref}
|
||||
hasWarning={showWarning}
|
||||
{...props}
|
||||
>
|
||||
<BrowserWarning
|
||||
show={showWarning}
|
||||
onClose={() => {
|
||||
setShowWarning(false);
|
||||
}}
|
||||
/>
|
||||
<StyledHeader
|
||||
hasWarning={showWarning}
|
||||
data-testid="editor-header-items"
|
||||
data-tauri-drag-region
|
||||
>
|
||||
<SidebarSwitch
|
||||
visible={!open}
|
||||
tooltipContent={t('Expand sidebar')}
|
||||
testid="sliderBar-arrowButton-expand"
|
||||
/>
|
||||
|
||||
{props.children}
|
||||
<StyledHeaderRightSide>
|
||||
{useMemo(() => {
|
||||
return Object.entries(HeaderRightItems).map(
|
||||
([name, { availableWhen, Component }]) => {
|
||||
if (
|
||||
availableWhen(props.workspace, props.currentPage, {
|
||||
isPreview: props.isPreview,
|
||||
isPublic: props.isPublic,
|
||||
})
|
||||
) {
|
||||
return (
|
||||
<Component
|
||||
workspace={props.workspace}
|
||||
currentPage={props.currentPage}
|
||||
isPreview={props.isPreview}
|
||||
isPublic={props.isPublic}
|
||||
key={name}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
}, [props])}
|
||||
{/*<ShareMenu />*/}
|
||||
</StyledHeaderRightSide>
|
||||
</StyledHeader>
|
||||
</StyledHeaderContainer>
|
||||
);
|
||||
});
|
||||
|
||||
Header.displayName = 'Header';
|
||||
@@ -0,0 +1,128 @@
|
||||
import type { PopperProps } from '@affine/component';
|
||||
import { QuickSearchTips } from '@affine/component';
|
||||
import { getEnvironment } from '@affine/env';
|
||||
import { ArrowDownSmallIcon } from '@blocksuite/icons';
|
||||
import { assertExists } from '@blocksuite/store';
|
||||
import { useAtomValue, useSetAtom } from 'jotai';
|
||||
import type { HTMLAttributes, PropsWithChildren } from 'react';
|
||||
import { forwardRef, useCallback, useRef } from 'react';
|
||||
|
||||
import { currentEditorAtom, openQuickSearchModalAtom } from '../../../atoms';
|
||||
import { useGuideHidden } from '../../../hooks/use-is-first-load';
|
||||
import { usePageMeta } from '../../../hooks/use-page-meta';
|
||||
import { useElementResizeEffect } from '../../../hooks/use-workspaces';
|
||||
import { QuickSearchButton } from '../../pure/quick-search-button';
|
||||
import { EditorModeSwitch } from './editor-mode-switch';
|
||||
import type { BaseHeaderProps } from './header';
|
||||
import { Header } from './header';
|
||||
import {
|
||||
StyledQuickSearchTipButton,
|
||||
StyledQuickSearchTipContent,
|
||||
StyledSearchArrowWrapper,
|
||||
StyledSwitchWrapper,
|
||||
StyledTitle,
|
||||
StyledTitleContainer,
|
||||
StyledTitleWrapper,
|
||||
} from './styles';
|
||||
|
||||
export type WorkspaceHeaderProps = BaseHeaderProps;
|
||||
|
||||
export const WorkspaceHeader = forwardRef<
|
||||
HTMLDivElement,
|
||||
PropsWithChildren<WorkspaceHeaderProps> & HTMLAttributes<HTMLDivElement>
|
||||
>((props, ref) => {
|
||||
const { workspace, currentPage, children, isPublic } = props;
|
||||
// fixme(himself65): remove this atom and move it to props
|
||||
const setOpenQuickSearch = useSetAtom(openQuickSearchModalAtom);
|
||||
const pageMeta = usePageMeta(workspace.blockSuiteWorkspace).find(
|
||||
meta => meta.id === currentPage?.id
|
||||
);
|
||||
assertExists(pageMeta);
|
||||
const title = pageMeta.title;
|
||||
const [isTipsHidden, setTipsHidden] = useGuideHidden();
|
||||
const isMac = () => {
|
||||
const env = getEnvironment();
|
||||
return env.isBrowser && env.isMacOs;
|
||||
};
|
||||
|
||||
const popperRef: PopperProps['popperRef'] = useRef(null);
|
||||
|
||||
useElementResizeEffect(
|
||||
useAtomValue(currentEditorAtom),
|
||||
useCallback(() => {
|
||||
if (isTipsHidden.quickSearchTips || !popperRef.current) {
|
||||
return;
|
||||
}
|
||||
popperRef.current.update();
|
||||
}, [isTipsHidden.quickSearchTips])
|
||||
);
|
||||
|
||||
const TipsContent = (
|
||||
<StyledQuickSearchTipContent>
|
||||
<div>
|
||||
Click button
|
||||
{
|
||||
<span
|
||||
style={{
|
||||
fontSize: '24px',
|
||||
verticalAlign: 'middle',
|
||||
}}
|
||||
>
|
||||
<ArrowDownSmallIcon />
|
||||
</span>
|
||||
}
|
||||
or use
|
||||
{isMac() ? ' ⌘ + K' : ' Ctrl + K'} to activate Quick Search. Then you
|
||||
can search keywords or quickly open recently viewed pages.
|
||||
</div>
|
||||
<StyledQuickSearchTipButton
|
||||
data-testid="quick-search-got-it"
|
||||
onClick={() =>
|
||||
setTipsHidden({ ...isTipsHidden, quickSearchTips: true })
|
||||
}
|
||||
>
|
||||
Got it
|
||||
</StyledQuickSearchTipButton>
|
||||
</StyledQuickSearchTipContent>
|
||||
);
|
||||
|
||||
return (
|
||||
<Header ref={ref} {...props}>
|
||||
{children}
|
||||
{!isPublic && currentPage && (
|
||||
<StyledTitleContainer data-tauri-drag-region>
|
||||
<StyledTitleWrapper>
|
||||
<StyledSwitchWrapper>
|
||||
<EditorModeSwitch
|
||||
blockSuiteWorkspace={workspace.blockSuiteWorkspace}
|
||||
pageId={currentPage.id}
|
||||
style={{
|
||||
marginRight: '12px',
|
||||
}}
|
||||
/>
|
||||
</StyledSwitchWrapper>
|
||||
<StyledTitle>{title || 'Untitled'}</StyledTitle>
|
||||
<QuickSearchTips
|
||||
data-testid="quick-search-tips"
|
||||
content={TipsContent}
|
||||
placement="bottom"
|
||||
popperRef={popperRef}
|
||||
open={!isTipsHidden.quickSearchTips}
|
||||
offset={[0, -5]}
|
||||
>
|
||||
<StyledSearchArrowWrapper>
|
||||
<QuickSearchButton
|
||||
onClick={() => {
|
||||
setOpenQuickSearch(true);
|
||||
}}
|
||||
/>
|
||||
</StyledSearchArrowWrapper>
|
||||
</QuickSearchTips>
|
||||
</StyledTitleWrapper>
|
||||
</StyledTitleContainer>
|
||||
)}
|
||||
</Header>
|
||||
);
|
||||
});
|
||||
|
||||
WorkspaceHeader.displayName = 'BlockSuiteEditorHeader';
|
||||
@@ -0,0 +1,170 @@
|
||||
import {
|
||||
absoluteCenter,
|
||||
displayFlex,
|
||||
styled,
|
||||
textEllipsis,
|
||||
} from '@affine/component';
|
||||
|
||||
export const StyledHeaderContainer = styled('div')<{
|
||||
hasWarning: boolean;
|
||||
sidebarFloating: boolean;
|
||||
}>(({ theme, hasWarning, sidebarFloating }) => {
|
||||
return {
|
||||
height: hasWarning ? '96px' : '52px',
|
||||
flexShrink: 0,
|
||||
position: 'sticky',
|
||||
top: 0,
|
||||
background: theme.colors.pageBackground,
|
||||
zIndex: 1,
|
||||
WebkitAppRegion: sidebarFloating ? '' : 'drag',
|
||||
button: {
|
||||
WebkitAppRegion: 'no-drag',
|
||||
},
|
||||
};
|
||||
});
|
||||
export const StyledHeader = styled('div')<{ hasWarning: boolean }>(
|
||||
({ theme }) => {
|
||||
return {
|
||||
flexShrink: 0,
|
||||
height: '52px',
|
||||
width: '100%',
|
||||
padding: '0 20px',
|
||||
...displayFlex('space-between', 'center'),
|
||||
background: theme.colors.pageBackground,
|
||||
zIndex: 99,
|
||||
position: 'relative',
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
export const StyledTitleContainer = styled('div')(({ theme }) => ({
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
|
||||
margin: 'auto',
|
||||
...absoluteCenter({ horizontal: true, position: { top: 0 } }),
|
||||
...displayFlex('center', 'center'),
|
||||
fontSize: theme.font.base,
|
||||
}));
|
||||
|
||||
export const StyledTitle = styled('div')(({ theme }) => {
|
||||
return {
|
||||
maxWidth: '620px',
|
||||
[theme.breakpoints.down('lg')]: {
|
||||
maxWidth: '480px',
|
||||
},
|
||||
[theme.breakpoints.down('md')]: {
|
||||
maxWidth: '240px',
|
||||
},
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
maxWidth: '180px',
|
||||
},
|
||||
transition: 'max-width .15s',
|
||||
userSelect: 'none',
|
||||
...textEllipsis(1),
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledTitleWrapper = styled('div')({
|
||||
height: '100%',
|
||||
position: 'relative',
|
||||
...displayFlex('center', 'center'),
|
||||
});
|
||||
|
||||
export const StyledHeaderRightSide = styled('div')({
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
'>*:not(:last-child)': {
|
||||
marginRight: '12px',
|
||||
},
|
||||
});
|
||||
|
||||
export const StyledBrowserWarning = styled('div')<{ show: boolean }>(
|
||||
({ theme, show }) => {
|
||||
return {
|
||||
backgroundColor: theme.colors.warningBackground,
|
||||
color: theme.colors.warningColor,
|
||||
height: '36px',
|
||||
fontSize: theme.font.sm,
|
||||
display: show ? 'flex' : 'none',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
export const StyledCloseButton = styled('div')(({ theme }) => {
|
||||
return {
|
||||
width: '36px',
|
||||
height: '36px',
|
||||
color: theme.colors.iconColor,
|
||||
cursor: 'pointer',
|
||||
...displayFlex('center', 'center'),
|
||||
position: 'absolute',
|
||||
right: '15px',
|
||||
top: '0',
|
||||
|
||||
svg: {
|
||||
width: '15px',
|
||||
height: '15px',
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledSwitchWrapper = styled('div')(() => {
|
||||
return {
|
||||
position: 'absolute',
|
||||
right: '100%',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
margin: 'auto',
|
||||
...displayFlex('center', 'center'),
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledSearchArrowWrapper = styled('div')(() => {
|
||||
return {
|
||||
position: 'absolute',
|
||||
left: 'calc(100% + 4px)',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
margin: 'auto',
|
||||
...displayFlex('center', 'center'),
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledPageListTittleWrapper = styled(StyledTitle)(({ theme }) => {
|
||||
return {
|
||||
fontSize: theme.font.base,
|
||||
color: theme.colors.textColor,
|
||||
...displayFlex('center', 'center'),
|
||||
'>svg': {
|
||||
fontSize: '20px',
|
||||
marginRight: '12px',
|
||||
},
|
||||
};
|
||||
});
|
||||
export const StyledQuickSearchTipButton = styled('div')(({ theme }) => {
|
||||
return {
|
||||
...displayFlex('center', 'center'),
|
||||
marginTop: '12px',
|
||||
color: '#FFFFFF',
|
||||
width: '48px',
|
||||
height: ' 26px',
|
||||
fontSize: theme.font.sm,
|
||||
lineHeight: '22px',
|
||||
background: theme.colors.primaryColor,
|
||||
borderRadius: '8px',
|
||||
textAlign: 'center',
|
||||
cursor: 'pointer',
|
||||
};
|
||||
});
|
||||
export const StyledQuickSearchTipContent = styled('div')(() => {
|
||||
return {
|
||||
...displayFlex('center', 'flex-end'),
|
||||
flexDirection: 'column',
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,50 @@
|
||||
import { getEnvironment } from '@affine/env';
|
||||
import { Trans, useTranslation } from '@affine/i18n';
|
||||
import type React from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
const minimumChromeVersion = 102;
|
||||
|
||||
export const shouldShowWarning = () => {
|
||||
const env = getEnvironment();
|
||||
if (env.isDesktop) {
|
||||
// even though desktop have compatibility issues, we don't want to show the warning
|
||||
return false;
|
||||
}
|
||||
if (!env.isBrowser) {
|
||||
// disable in SSR
|
||||
return false;
|
||||
}
|
||||
if (env.isChrome) {
|
||||
return env.chromeVersion < minimumChromeVersion;
|
||||
} else {
|
||||
return !env.isMobile;
|
||||
}
|
||||
};
|
||||
|
||||
export const OSWarningMessage: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const [notChrome, setNotChrome] = useState(false);
|
||||
const [notGoodVersion, setNotGoodVersion] = useState(false);
|
||||
useEffect(() => {
|
||||
const env = getEnvironment();
|
||||
setNotChrome(env.isBrowser && !env.isChrome);
|
||||
setNotGoodVersion(
|
||||
env.isBrowser && env.isChrome && env.chromeVersion < minimumChromeVersion
|
||||
);
|
||||
}, []);
|
||||
|
||||
if (notChrome) {
|
||||
return (
|
||||
<span>
|
||||
<Trans i18nKey="recommendBrowser">
|
||||
We recommend the <strong>Chrome</strong> browser for optimal
|
||||
experience.
|
||||
</Trans>
|
||||
</span>
|
||||
);
|
||||
} else if (notGoodVersion) {
|
||||
return <span>{t('upgradeBrowser')}</span>;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
Reference in New Issue
Block a user