@netless/fastboard 0.0.8 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/LICENSE.txt +1 -1
  2. package/dist/index.js +426 -0
  3. package/dist/index.js.map +1 -0
  4. package/dist/index.mjs +393 -0
  5. package/dist/index.mjs.map +1 -0
  6. package/package.json +16 -71
  7. package/src/base.ts +55 -0
  8. package/src/core.ts +307 -0
  9. package/src/emitter.ts +21 -0
  10. package/src/index.ts +70 -24
  11. package/src/{behaviors/register-apps.ts → register-apps.ts} +6 -14
  12. package/src/utils.ts +74 -0
  13. package/src/value.ts +74 -0
  14. package/README.md +0 -134
  15. package/dist/index.cjs.js +0 -14
  16. package/dist/index.cjs.js.map +0 -1
  17. package/dist/index.es.js +0 -2538
  18. package/dist/index.es.js.map +0 -1
  19. package/dist/svelte.cjs.js +0 -2
  20. package/dist/svelte.cjs.js.map +0 -1
  21. package/dist/svelte.es.js +0 -31
  22. package/dist/svelte.es.js.map +0 -1
  23. package/dist/vue.cjs.js +0 -2
  24. package/dist/vue.cjs.js.map +0 -1
  25. package/dist/vue.es.js +0 -42
  26. package/dist/vue.es.js.map +0 -1
  27. package/src/WhiteboardApp.ts +0 -80
  28. package/src/behaviors/style.ts +0 -17
  29. package/src/components/PageControl.scss +0 -80
  30. package/src/components/PageControl.tsx +0 -181
  31. package/src/components/PlayerControl/PlayerControl.scss +0 -145
  32. package/src/components/PlayerControl/PlayerControl.tsx +0 -158
  33. package/src/components/PlayerControl/components/Button.tsx +0 -55
  34. package/src/components/PlayerControl/hooks.ts +0 -95
  35. package/src/components/PlayerControl/icons/Loading.tsx +0 -13
  36. package/src/components/PlayerControl/icons/Pause.tsx +0 -13
  37. package/src/components/PlayerControl/icons/Play.tsx +0 -13
  38. package/src/components/PlayerControl/icons/index.ts +0 -10
  39. package/src/components/PlayerControl/index.ts +0 -1
  40. package/src/components/RedoUndo.scss +0 -56
  41. package/src/components/RedoUndo.tsx +0 -95
  42. package/src/components/Root.scss +0 -55
  43. package/src/components/Root.tsx +0 -61
  44. package/src/components/Toolbar/Content.tsx +0 -93
  45. package/src/components/Toolbar/Toolbar.scss +0 -247
  46. package/src/components/Toolbar/Toolbar.tsx +0 -82
  47. package/src/components/Toolbar/components/ApplianceButtons.tsx +0 -132
  48. package/src/components/Toolbar/components/AppsButton.tsx +0 -106
  49. package/src/components/Toolbar/components/Button.tsx +0 -54
  50. package/src/components/Toolbar/components/ColorBox.tsx +0 -56
  51. package/src/components/Toolbar/components/CutLine.tsx +0 -8
  52. package/src/components/Toolbar/components/PencilButton.tsx +0 -70
  53. package/src/components/Toolbar/components/ShapesButton.tsx +0 -143
  54. package/src/components/Toolbar/components/Slider.tsx +0 -27
  55. package/src/components/Toolbar/components/TextButton.tsx +0 -66
  56. package/src/components/Toolbar/components/UpDownButtons.tsx +0 -49
  57. package/src/components/Toolbar/components/assets/cocos.png +0 -0
  58. package/src/components/Toolbar/components/assets/countdown.png +0 -0
  59. package/src/components/Toolbar/components/assets/geogebra.png +0 -0
  60. package/src/components/Toolbar/components/assets/vscode.png +0 -0
  61. package/src/components/Toolbar/const.ts +0 -32
  62. package/src/components/Toolbar/hooks.ts +0 -113
  63. package/src/components/Toolbar/icons/Apps.tsx +0 -16
  64. package/src/components/Toolbar/icons/Arrow.tsx +0 -16
  65. package/src/components/Toolbar/icons/Circle.tsx +0 -21
  66. package/src/components/Toolbar/icons/Clean.tsx +0 -16
  67. package/src/components/Toolbar/icons/Clicker.tsx +0 -19
  68. package/src/components/Toolbar/icons/Collapse.tsx +0 -17
  69. package/src/components/Toolbar/icons/Diamond.tsx +0 -17
  70. package/src/components/Toolbar/icons/Down.tsx +0 -17
  71. package/src/components/Toolbar/icons/Eraser.tsx +0 -16
  72. package/src/components/Toolbar/icons/Expand.tsx +0 -17
  73. package/src/components/Toolbar/icons/Line.tsx +0 -13
  74. package/src/components/Toolbar/icons/Pencil.tsx +0 -16
  75. package/src/components/Toolbar/icons/Rectangle.tsx +0 -13
  76. package/src/components/Toolbar/icons/Selector.tsx +0 -16
  77. package/src/components/Toolbar/icons/SpeechBalloon.tsx +0 -17
  78. package/src/components/Toolbar/icons/Star.tsx +0 -17
  79. package/src/components/Toolbar/icons/Text.tsx +0 -16
  80. package/src/components/Toolbar/icons/Triangle.tsx +0 -17
  81. package/src/components/Toolbar/icons/Up.tsx +0 -17
  82. package/src/components/Toolbar/icons/index.ts +0 -42
  83. package/src/components/Toolbar/index.ts +0 -1
  84. package/src/components/ZoomControl.scss +0 -80
  85. package/src/components/ZoomControl.tsx +0 -221
  86. package/src/hooks.ts +0 -53
  87. package/src/i18n/en.json +0 -31
  88. package/src/i18n/index.ts +0 -22
  89. package/src/i18n/zh-CN.json +0 -32
  90. package/src/icons/ChevronLeft.tsx +0 -21
  91. package/src/icons/ChevronRight.tsx +0 -21
  92. package/src/icons/FilePlus.tsx +0 -18
  93. package/src/icons/Minus.tsx +0 -21
  94. package/src/icons/Plus.tsx +0 -21
  95. package/src/icons/Redo.tsx +0 -24
  96. package/src/icons/Reset.tsx +0 -23
  97. package/src/icons/Undo.tsx +0 -24
  98. package/src/icons/index.tsx +0 -11
  99. package/src/internal/Instance.tsx +0 -251
  100. package/src/internal/helpers.ts +0 -42
  101. package/src/internal/index.ts +0 -3
  102. package/src/internal/mount-whiteboard.ts +0 -90
  103. package/src/style.scss +0 -29
  104. package/src/svelte.ts +0 -45
  105. package/src/theme/index.ts +0 -36
  106. package/src/types/index.ts +0 -22
  107. package/src/vue.ts +0 -74
@@ -1,13 +0,0 @@
1
- import type { IconProps } from "../../../types";
2
-
3
- import React from "react";
4
- import { getStroke } from "../../../theme";
5
-
6
- export const Loading = (props: IconProps) => {
7
- const stroke = getStroke(props);
8
- return (
9
- <svg viewBox="0 0 24 24">
10
- <path d="M12 4V2A10 10 0 0 0 2 12h2a8 8 0 0 1 8-8z" fill={stroke}></path>
11
- </svg>
12
- );
13
- };
@@ -1,13 +0,0 @@
1
- import type { IconProps } from "../../../types";
2
-
3
- import React from "react";
4
- import { getStroke } from "../../../theme";
5
-
6
- export const Pause = (props: IconProps) => {
7
- const stroke = getStroke(props);
8
- return (
9
- <svg viewBox="0 0 24 24">
10
- <path d="M14 19h4V5h-4M6 19h4V5H6v14z" fill={stroke}></path>
11
- </svg>
12
- );
13
- };
@@ -1,13 +0,0 @@
1
- import type { IconProps } from "../../../types";
2
-
3
- import React from "react";
4
- import { getStroke } from "../../../theme";
5
-
6
- export const Play = (props: IconProps) => {
7
- const stroke = getStroke(props);
8
- return (
9
- <svg viewBox="0 0 24 24">
10
- <path d="M8 5.14v14l11-7l-11-7z" fill={stroke}></path>
11
- </svg>
12
- );
13
- };
@@ -1,10 +0,0 @@
1
- import { memo } from "react";
2
- import { Loading } from "./Loading";
3
- import { Pause } from "./Pause";
4
- import { Play } from "./Play";
5
-
6
- export const Icons = {
7
- Play: memo(Play),
8
- Pause: memo(Pause),
9
- Loading: memo(Loading),
10
- };
@@ -1 +0,0 @@
1
- export { PlayerControl, name, type PlayerControlProps } from "./PlayerControl";
@@ -1,56 +0,0 @@
1
- $name: "fastboard-redo-undo";
2
-
3
- .#{$name} {
4
- display: inline-flex;
5
- align-items: center;
6
- gap: 4px;
7
- padding: 4px;
8
- border-radius: 4px;
9
- backdrop-filter: blur(2px);
10
- -webkit-backdrop-filter: blur(2px);
11
-
12
- &.light {
13
- color: #333;
14
- background-color: rgba($color: #fff, $alpha: 0.85);
15
- border: 1px solid rgba(0, 0, 0, 0.15);
16
- }
17
-
18
- &.dark {
19
- color: #ddd;
20
- background-color: rgba($color: #333, $alpha: 0.85);
21
- border: 1px solid rgba(0, 0, 0, 0.45);
22
- }
23
- }
24
-
25
- .#{$name}-btn {
26
- appearance: none;
27
- cursor: pointer;
28
- margin: 0;
29
- border: 0;
30
- padding: 0;
31
- width: 24px;
32
- height: 24px;
33
- background-color: transparent;
34
- border-radius: 4px;
35
- font-size: 24px;
36
- line-height: 1;
37
-
38
- svg,
39
- img {
40
- width: 1em;
41
- height: 1em;
42
- }
43
-
44
- &:disabled {
45
- opacity: 0.5;
46
- cursor: not-allowed;
47
- }
48
-
49
- &.light:not(:disabled):hover {
50
- background-color: rgba(51, 129, 255, 0.1);
51
- }
52
-
53
- &.dark:not(:disabled):hover {
54
- background-color: rgba(51, 129, 255, 0.25);
55
- }
56
- }
@@ -1,95 +0,0 @@
1
- import type { CommonProps, GenericIcon } from "../types";
2
-
3
- import clsx from "clsx";
4
- import React, { useCallback, useEffect, useState } from "react";
5
- import Tippy from "@tippyjs/react";
6
-
7
- import { Icon } from "../icons";
8
- import { Undo } from "../icons/Undo";
9
- import { Redo } from "../icons/Redo";
10
- import { TopOffset } from "../theme";
11
-
12
- export const name = "fastboard-redo-undo";
13
-
14
- export type RedoUndoProps = CommonProps & GenericIcon<"undo" | "redo">;
15
-
16
- export function RedoUndo({
17
- room,
18
- theme = "light",
19
- undoIcon,
20
- undoIconDisable,
21
- redoIcon,
22
- redoIconDisable,
23
- i18n,
24
- }: RedoUndoProps) {
25
- const [writable, setWritable] = useState(false);
26
- const [undoSteps, setUndoSteps] = useState(0);
27
- const [redoSteps, setRedoSteps] = useState(0);
28
-
29
- useEffect(() => {
30
- if (room) {
31
- setWritable(room.isWritable);
32
- room.isWritable && (room.disableSerialization = false);
33
-
34
- const updateWritable = () => setWritable(room?.isWritable || false);
35
-
36
- room.callbacks.on("onEnableWriteNowChanged", updateWritable);
37
- room.callbacks.on("onCanUndoStepsUpdate", setUndoSteps);
38
- room.callbacks.on("onCanRedoStepsUpdate", setRedoSteps);
39
- return () => {
40
- room.callbacks.off("onEnableWriteNowChanged", updateWritable);
41
- room.callbacks.off("onCanUndoStepsUpdate", setUndoSteps);
42
- room.callbacks.off("onCanRedoStepsUpdate", setRedoSteps);
43
- };
44
- }
45
- }, [room]);
46
-
47
- const disabled = !writable;
48
-
49
- return (
50
- <div className={clsx(name, theme)}>
51
- <Tippy
52
- className="fastboard-tip"
53
- content={i18n?.t("undo")}
54
- theme={theme}
55
- disabled={disabled}
56
- placement="top"
57
- duration={300}
58
- offset={TopOffset}
59
- >
60
- <button
61
- className={clsx(`${name}-btn`, "undo", theme)}
62
- disabled={disabled || undoSteps === 0}
63
- onClick={useCallback(() => room && room.undo(), [room])}
64
- >
65
- <Icon
66
- fallback={<Undo theme={theme} />}
67
- src={undoSteps === 0 ? undoIconDisable : undoIcon}
68
- alt="[undo]"
69
- />
70
- </button>
71
- </Tippy>
72
- <Tippy
73
- className="fastboard-tip"
74
- content={i18n?.t("redo")}
75
- theme={theme}
76
- disabled={disabled}
77
- placement="top"
78
- duration={300}
79
- offset={TopOffset}
80
- >
81
- <button
82
- className={clsx(`${name}-btn`, "redo", theme)}
83
- disabled={disabled || redoSteps === 0}
84
- onClick={useCallback(() => room && room.redo(), [room])}
85
- >
86
- <Icon
87
- fallback={<Redo theme={theme} />}
88
- src={redoSteps === 0 ? redoIconDisable : redoIcon}
89
- alt="[redo]"
90
- />
91
- </button>
92
- </Tippy>
93
- </div>
94
- );
95
- }
@@ -1,55 +0,0 @@
1
- .fastboard-root {
2
- position: relative;
3
- width: 100%;
4
- height: 100%;
5
- overflow: hidden;
6
- }
7
-
8
- .fastboard-loading {
9
- position: absolute;
10
- top: 0;
11
- left: 0;
12
- width: 100%;
13
- height: 100%;
14
- display: flex;
15
- align-items: center;
16
- justify-content: center;
17
- opacity: 0.6;
18
- }
19
-
20
- .fastboard-view {
21
- position: absolute;
22
- top: 0;
23
- left: 0;
24
- width: 100%;
25
- height: 100%;
26
- }
27
-
28
- $unit: 8px;
29
-
30
- .fastboard-left {
31
- position: absolute;
32
- top: 0;
33
- left: 0;
34
- height: calc(100% - 48px);
35
- padding: $unit * 2;
36
- z-index: 201;
37
- display: flex;
38
- align-items: center;
39
- }
40
-
41
- .fastboard-bottom-left {
42
- display: flex;
43
- gap: 10px;
44
- position: absolute;
45
- bottom: $unit;
46
- left: $unit;
47
- padding: $unit;
48
- z-index: 200;
49
- }
50
-
51
- .fastboard-bottom-right {
52
- @extend .fastboard-bottom-left;
53
- left: initial;
54
- right: $unit;
55
- }
@@ -1,61 +0,0 @@
1
- import React, { useCallback, useState } from "react";
2
-
3
- import { Lock, Instance } from "../internal";
4
- import { Toolbar } from "./Toolbar";
5
- import { RedoUndo } from "./RedoUndo";
6
- import { ZoomControl } from "./ZoomControl";
7
- import { PageControl } from "./PageControl";
8
-
9
- export interface RootProps {
10
- instance: Instance;
11
- }
12
-
13
- export function Root({ instance: app }: RootProps) {
14
- const [mux] = useState(() => new Lock());
15
-
16
- const useWhiteboard = useCallback(
17
- (container: HTMLDivElement | null) =>
18
- mux.schedule(
19
- container ? () => app.mount(container) : () => app.unmount()
20
- ),
21
- [app, mux]
22
- );
23
-
24
- const {
25
- Toolbar: toolbar = true,
26
- RedoUndo: redo_undo = true,
27
- ZoomControl: zoom_control = true,
28
- PageControl: page_control = true,
29
- } = app.config.layout || {};
30
-
31
- const props = {
32
- room: app.room,
33
- manager: app.manager,
34
- i18n: app.i18n,
35
- };
36
-
37
- return (
38
- <Instance.Context.Provider value={app}>
39
- <div className="fastboard-root">
40
- {!app.room && <div className="fastboard-loading">Loading&hellip;</div>}
41
- <div className="fastboard-view" ref={useWhiteboard} />
42
- {toolbar && (
43
- <div className="fastboard-left">
44
- <Toolbar {...props} />
45
- </div>
46
- )}
47
- {(redo_undo || zoom_control) && (
48
- <div className="fastboard-bottom-left">
49
- {redo_undo && <RedoUndo {...props} />}
50
- {zoom_control && <ZoomControl {...props} />}
51
- </div>
52
- )}
53
- {page_control && (
54
- <div className="fastboard-bottom-right">
55
- <PageControl {...props} />
56
- </div>
57
- )}
58
- </div>
59
- </Instance.Context.Provider>
60
- );
61
- }
@@ -1,93 +0,0 @@
1
- import React, { useCallback, useEffect, useRef, useState } from "react";
2
-
3
- import { useInstance, clamp } from "../../internal";
4
- import { name } from "./Toolbar";
5
- import { ItemHeight, ItemsCount, MaxHeight, MinHeight } from "./const";
6
- import { DownButton, UpButton } from "./components/UpDownButtons";
7
- import {
8
- CleanButton,
9
- ClickerButton,
10
- EraserButton,
11
- SelectorButton,
12
- } from "./components/ApplianceButtons";
13
- import { AppsButton } from "./components/AppsButton";
14
- import { PencilButton } from "./components/PencilButton";
15
- import { TextButton } from "./components/TextButton";
16
- import { ShapesButton } from "./components/ShapesButton";
17
-
18
- export function Content() {
19
- const app = useInstance();
20
- const ref = useRef<HTMLDivElement>(null);
21
- const [scrollTop, setScrollTop] = useState(0);
22
- const [parentHeight, setParentHeight] = useState(0);
23
-
24
- const hasAppButton = app?.config.toolbar?.apps?.enable ?? true;
25
- const needScroll = parentHeight < ItemHeight * ItemsCount + 48;
26
- const sectionHeight = clamp(
27
- parentHeight - 48 * (needScroll ? 3 : 1),
28
- MinHeight,
29
- MaxHeight
30
- );
31
- const scrollBuffer = Math.max(parentHeight - sectionHeight - 1, 0);
32
- const disableScrollUp = scrollTop === 0;
33
- const disableScrollDown = scrollTop === scrollBuffer;
34
-
35
- const scrollTo = useCallback(
36
- (height: number) => {
37
- setScrollTop(clamp(scrollTop + height, 0, scrollBuffer));
38
- },
39
- [scrollBuffer, scrollTop]
40
- );
41
-
42
- useEffect(() => {
43
- if (ref.current) {
44
- ref.current.scrollTop = scrollTop;
45
- }
46
- }, [scrollTop]);
47
-
48
- useEffect(() => {
49
- const container = ref.current?.parentElement?.parentElement;
50
- if (container) {
51
- const { paddingTop, paddingBottom } = getComputedStyle(container);
52
- const padding = parseInt(paddingTop) + parseInt(paddingBottom) || 0;
53
- const resizeObserver = new ResizeObserver(() => {
54
- setParentHeight(container.getBoundingClientRect().height - padding);
55
- });
56
- resizeObserver.observe(container);
57
- return () => resizeObserver.disconnect();
58
- }
59
- }, []);
60
-
61
- return (
62
- <>
63
- {needScroll && (
64
- <UpButton scrollTo={scrollTo} disabled={disableScrollUp} />
65
- )}
66
- <div
67
- ref={ref}
68
- className={`${name}-section`}
69
- style={{
70
- height: `${sectionHeight}px`,
71
- overflow: needScroll ? "hidden" : "visible",
72
- }}
73
- >
74
- <ClickerButton />
75
- <SelectorButton />
76
- <PencilButton />
77
- <TextButton />
78
- <ShapesButton />
79
- <EraserButton />
80
- <CleanButton />
81
- {hasAppButton && (
82
- <AppsButton
83
- content={app?.config.toolbar?.apps?.content}
84
- onClick={app?.config.toolbar?.apps?.onClick}
85
- />
86
- )}
87
- </div>
88
- {needScroll && (
89
- <DownButton scrollTo={scrollTo} disabled={disableScrollDown} />
90
- )}
91
- </>
92
- );
93
- }
@@ -1,247 +0,0 @@
1
- $name: "fastboard-toolbar";
2
-
3
- .#{$name} {
4
- display: flex;
5
- align-items: center;
6
- padding: 4px;
7
- border-radius: 4px;
8
- flex-direction: column;
9
- gap: 4px;
10
- position: absolute;
11
- z-index: 100;
12
- backdrop-filter: blur(2px);
13
- -webkit-backdrop-filter: blur(2px);
14
-
15
- .rc-slider {
16
- padding: 6px 0;
17
- }
18
- .rc-slider-rail,
19
- .rc-slider-track {
20
- height: 2px;
21
- }
22
-
23
- .tippy-content {
24
- padding: 8px;
25
- }
26
- .tippy-box {
27
- border: 1px solid rgba(0, 0, 0, 0.15);
28
- background-color: rgba($color: #333, $alpha: 0.95);
29
- backdrop-filter: blur(2px);
30
- -webkit-backdrop-filter: blur(2px);
31
- }
32
- .tippy-box[data-theme~="light"] {
33
- background-color: rgba($color: #fff, $alpha: 0.95);
34
- box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.25);
35
- }
36
-
37
- &.light {
38
- color: #333;
39
- background-color: rgba($color: #fff, $alpha: 0.85);
40
- border: 1px solid rgba(0, 0, 0, 0.15);
41
- }
42
-
43
- &.dark {
44
- color: #ddd;
45
- background-color: rgba($color: #333, $alpha: 0.85);
46
- border: 1px solid rgba(0, 0, 0, 0.45);
47
- }
48
-
49
- &-tooltip {
50
- display: inline-flex;
51
- align-items: center;
52
- gap: 4px;
53
- }
54
-
55
- &-hotkey {
56
- margin-right: -4px;
57
- width: 24px;
58
- height: 24px;
59
- border-radius: 4px;
60
- background-color: rgba($color: #fff, $alpha: 0.1);
61
- display: inline-flex;
62
- align-items: center;
63
- justify-content: center;
64
- }
65
-
66
- &-btn {
67
- appearance: none;
68
- cursor: pointer;
69
- margin: 0;
70
- border: 0;
71
- padding: 4px;
72
- width: 32px;
73
- height: 32px;
74
- background-color: transparent;
75
- border-radius: 4px;
76
- font-size: 24px;
77
- line-height: 1;
78
- position: relative;
79
-
80
- &-interactive {
81
- display: inline-block;
82
- width: 32px;
83
- height: 32px;
84
- }
85
-
86
- svg,
87
- img {
88
- width: 1em;
89
- height: 1em;
90
- }
91
-
92
- &:disabled {
93
- opacity: 0.5;
94
- cursor: not-allowed;
95
- }
96
-
97
- &.light:not(:disabled):hover {
98
- background-color: rgba(51, 129, 255, 0.1);
99
- }
100
-
101
- &.dark:not(:disabled):hover {
102
- background-color: rgba(51, 129, 255, 0.25);
103
- }
104
- }
105
-
106
- &-triangle {
107
- width: 0px;
108
- height: 0px;
109
- border-bottom: 4px solid;
110
- border-left: 4px solid transparent;
111
- position: absolute;
112
- bottom: 0;
113
- right: 0;
114
- }
115
-
116
- &-cut-line {
117
- display: inline-block;
118
- height: 0.5px;
119
- width: 100%;
120
-
121
- &.light {
122
- background-color: #e7e7e7;
123
- }
124
-
125
- &.dark {
126
- background-color: rgba(255, 255, 255, 0.15);
127
- }
128
- }
129
-
130
- &-section {
131
- display: inline-flex;
132
- flex-flow: column nowrap;
133
- gap: 4px;
134
- scroll-behavior: smooth;
135
- }
136
-
137
- &-panel {
138
- width: 136px - 8px * 2;
139
- padding: 0;
140
- display: flex;
141
- flex-flow: column nowrap;
142
- align-items: center;
143
- gap: 8px;
144
-
145
- &.apps {
146
- width: 240px - 8px * 2;
147
- }
148
- }
149
-
150
- &-color-box,
151
- &-shapes {
152
- display: grid;
153
- grid-template-columns: repeat(4, 1fr);
154
- gap: 8px;
155
- align-items: center;
156
- justify-items: center;
157
-
158
- .#{$name}-btn {
159
- padding: 0;
160
- width: 24px;
161
- height: 24px;
162
- }
163
- }
164
-
165
- &-apps {
166
- width: 100%;
167
- display: grid;
168
- grid-template-columns: repeat(3, 1fr);
169
- gap: 8px;
170
- align-items: center;
171
- justify-items: center;
172
-
173
- .#{$name}-btn {
174
- width: 40px;
175
- height: 40px;
176
- font-size: 40px;
177
- }
178
- }
179
-
180
- &-app-icon {
181
- padding-top: 4px;
182
- display: inline-flex;
183
- flex-flow: column nowrap;
184
- align-items: center;
185
- gap: 4px;
186
-
187
- .fastboard-toolbar-btn {
188
- padding: 0;
189
- }
190
-
191
- &-text {
192
- font-size: 12px;
193
- color: #5d5d5d;
194
- overflow: hidden;
195
- white-space: nowrap;
196
- text-overflow: ellipsis;
197
- }
198
- }
199
-
200
- &-color-item {
201
- width: 24px;
202
- height: 24px;
203
- border-radius: 4px;
204
- cursor: pointer;
205
-
206
- *.light:hover {
207
- background-color: #f5f5f5;
208
- }
209
-
210
- *.dark:hover {
211
- background-color: #333;
212
- }
213
- }
214
-
215
- &-color-border {
216
- width: 24px;
217
- height: 24px;
218
- border: 1px solid transparent;
219
- border-radius: 4px;
220
- display: inline-flex;
221
- align-items: center;
222
- justify-content: center;
223
-
224
- &.active.light {
225
- border: 1px solid rgba(51, 129, 255, 0.8);
226
- }
227
-
228
- &.active.dark {
229
- border: 1px solid rgba(51, 129, 255, 0.8);
230
- }
231
- }
232
-
233
- &-color-btn {
234
- margin: 0;
235
- border: 1px solid rgba(0, 0, 0, 0.24);
236
- padding: 0;
237
- appearance: none;
238
- width: 16px;
239
- height: 16px;
240
- border-radius: 4px;
241
- cursor: pointer;
242
-
243
- &:focus-visible {
244
- outline-offset: 2px;
245
- }
246
- }
247
- }