mirror of
https://github.com/logseq/logseq.git
synced 2026-04-24 14:14:55 +00:00
feat: links panel
This commit is contained in:
@@ -31,7 +31,11 @@
|
||||
|
||||
(rum/defc breadcrumb
|
||||
[props]
|
||||
(block/breadcrumb {:preview? true} (state/get-current-repo) (uuid (gobj/get props "blockId")) {:end-separator? true}))
|
||||
(block/breadcrumb {:preview? true}
|
||||
(state/get-current-repo)
|
||||
(uuid (gobj/get props "blockId"))
|
||||
{:end-separator? true
|
||||
:level-limit (gobj/get props "levelLimit" 3)}))
|
||||
|
||||
(rum/defc page-name-link
|
||||
[props]
|
||||
|
||||
@@ -20,6 +20,7 @@ import { Button } from '../Button'
|
||||
import { TablerIcon } from '../icons'
|
||||
import { ColorInput } from '../inputs/ColorInput'
|
||||
import { SelectInput, type SelectOption } from '../inputs/SelectInput'
|
||||
import { ShapeLinksInput } from '../inputs/ShapeLinksInput'
|
||||
import { TextInput } from '../inputs/TextInput'
|
||||
import {
|
||||
ToggleGroupInput,
|
||||
@@ -364,7 +365,6 @@ const SwatchAction = observer(() => {
|
||||
popoverSide="top"
|
||||
color={color}
|
||||
opacity={shapes[0].props.opacity}
|
||||
collisionRef={document.getElementById('main-content-container')}
|
||||
setOpacity={handleSetOpacity}
|
||||
setColor={handleSetColor}
|
||||
/>
|
||||
@@ -504,80 +504,20 @@ const LinksAction = observer(() => {
|
||||
const app = useApp<Shape>()
|
||||
const shape = app.selectedShapesArray[0]
|
||||
|
||||
const [value, setValue] = React.useState('')
|
||||
|
||||
const [show, setShow] = React.useState(false)
|
||||
|
||||
const { handlers } = React.useContext(LogseqContext)
|
||||
|
||||
const handleChange = () => {
|
||||
const refs = shape.props.refs ?? []
|
||||
if (refs.includes(value)) return
|
||||
shape.update({ refs: [...refs, value] })
|
||||
const handleChange = (refs: string[]) => {
|
||||
shape.update({ refs: refs })
|
||||
app.persist()
|
||||
}
|
||||
|
||||
const hasLinks = shape.props.refs && shape.props.refs.length > 0
|
||||
|
||||
return (
|
||||
<span className="flex gap-3 relative">
|
||||
<ToggleInput
|
||||
className="px-2 tl-button"
|
||||
pressed={show}
|
||||
onPressedChange={s => setShow(s)}
|
||||
title="Open References & Links"
|
||||
>
|
||||
<TablerIcon name="link" />
|
||||
{hasLinks && <div className="tl-shape-links-count">{shape.props.refs?.length}</div>}
|
||||
</ToggleInput>
|
||||
|
||||
{show && (
|
||||
<div className="tl-shape-links-panel">
|
||||
<TextInput
|
||||
title="Website Url"
|
||||
className="tl-iframe-src"
|
||||
value={value}
|
||||
onChange={e => {
|
||||
setValue(e.target.value)
|
||||
}}
|
||||
onKeyDown={e => {
|
||||
if (e.key === 'Enter') {
|
||||
handleChange()
|
||||
}
|
||||
e.stopPropagation()
|
||||
}}
|
||||
/>
|
||||
<div className="text-xs font-bold inline-flex gap-1 items-center">
|
||||
<TablerIcon name="link" />
|
||||
Your Links
|
||||
</div>
|
||||
{shape.props.refs?.map((ref, i) => {
|
||||
return (
|
||||
<div className="tl-shape-links-panel-item">
|
||||
<div>{ref}</div>
|
||||
<div className="flex-1" />
|
||||
<Button
|
||||
title="Open Page in Right Sidebar"
|
||||
type="button"
|
||||
onClick={() => handlers?.sidebarAddBlock(ref, validUUID(ref) ? 'block' : 'page')}
|
||||
>
|
||||
<TablerIcon name="layout-sidebar-right" />
|
||||
</Button>
|
||||
<button
|
||||
className="hover:opacity-60"
|
||||
onClick={() => {
|
||||
shape.update({ refs: shape.props.refs?.filter((_, j) => j !== i) })
|
||||
app.persist()
|
||||
}}
|
||||
>
|
||||
<TablerIcon name="x" />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</span>
|
||||
<ShapeLinksInput
|
||||
onRefsChange={handleChange}
|
||||
refs={shape.props.refs ?? []}
|
||||
shapeType={shape.props.type}
|
||||
side="right"
|
||||
pageId={shape.props.type === 'logseq-portal' ? shape.props.pageId : undefined}
|
||||
portalType={shape.props.type === 'logseq-portal' ? shape.props.blockType : undefined}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import * as Popover from '@radix-ui/react-popover'
|
||||
import type { Side } from '@radix-ui/react-popper'
|
||||
import { BoundsUtils } from '@tldraw/core'
|
||||
import { useApp } from '@tldraw/react'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
|
||||
interface PopoverButton extends React.HTMLAttributes<HTMLButtonElement> {
|
||||
side: Side // default side
|
||||
label: React.ReactNode
|
||||
children: React.ReactNode
|
||||
border?: boolean
|
||||
arrow?: boolean
|
||||
}
|
||||
|
||||
const sideAndOpposite = {
|
||||
top: 'bottom',
|
||||
bottom: 'top',
|
||||
left: 'right',
|
||||
right: 'left',
|
||||
} as const
|
||||
|
||||
export const PopoverButton = observer(
|
||||
({ side, label, arrow, children, border, ...rest }: PopoverButton) => {
|
||||
const contentRef = React.useRef<HTMLDivElement>(null)
|
||||
|
||||
const [isOpen, setIsOpen] = React.useState(false)
|
||||
|
||||
const {
|
||||
viewport: {
|
||||
bounds,
|
||||
camera: { point, zoom },
|
||||
},
|
||||
} = useApp()
|
||||
|
||||
const [tick, setTick] = React.useState<number>(0)
|
||||
|
||||
// Change side if popover is out of bounds
|
||||
React.useEffect(() => {
|
||||
if (!contentRef.current || !isOpen) return
|
||||
|
||||
const boundingRect = contentRef.current.getBoundingClientRect()
|
||||
const outOfView = !BoundsUtils.boundsContain(bounds, {
|
||||
minX: boundingRect.x,
|
||||
minY: boundingRect.y,
|
||||
maxX: boundingRect.right,
|
||||
maxY: boundingRect.bottom,
|
||||
width: boundingRect.width,
|
||||
height: boundingRect.height,
|
||||
})
|
||||
|
||||
if (outOfView) {
|
||||
setTick(tick => tick + 1)
|
||||
}
|
||||
}, [point[0], point[1], zoom, isOpen])
|
||||
|
||||
return (
|
||||
<Popover.Root onOpenChange={o => setIsOpen(o)}>
|
||||
<Popover.Trigger {...rest} data-border={border} className="tl-popover-trigger-button">
|
||||
{label}
|
||||
</Popover.Trigger>
|
||||
|
||||
<Popover.Content
|
||||
// it seems like the Popover.Content component doesn't update collission when camera changes
|
||||
key={'popover-content-' + tick}
|
||||
ref={contentRef}
|
||||
className="tl-popover-content"
|
||||
side={side}
|
||||
sideOffset={15}
|
||||
collisionBoundary={document.querySelector('.logseq-tldraw')}
|
||||
>
|
||||
{children}
|
||||
{arrow && <Popover.Arrow className="tl-popover-arrow" />}
|
||||
</Popover.Content>
|
||||
</Popover.Root>
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
export * from './PopoverButton'
|
||||
@@ -33,7 +33,6 @@ export const PrimaryTools = observer(function PrimaryTools() {
|
||||
title="Color Picker"
|
||||
popoverSide="left"
|
||||
color={app.settings.color}
|
||||
collisionRef={document.getElementById('main-content-container')}
|
||||
setColor={handleSetColor}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import * as React from 'react'
|
||||
import * as Popover from '@radix-ui/react-popover'
|
||||
import type { Side } from '@radix-ui/react-popper'
|
||||
import * as Slider from '@radix-ui/react-slider'
|
||||
import { TablerIcon } from '../icons'
|
||||
import { Color } from '@tldraw/core'
|
||||
import { TablerIcon } from '../icons'
|
||||
import { PopoverButton } from '../PopoverButton'
|
||||
|
||||
interface ColorInputProps extends React.InputHTMLAttributes<HTMLButtonElement> {
|
||||
interface ColorInputProps extends React.HTMLAttributes<HTMLButtonElement> {
|
||||
color?: string
|
||||
opacity?: number
|
||||
collisionRef: HTMLElement | null
|
||||
popoverSide: Side
|
||||
setColor: (value: string) => void
|
||||
setOpacity?: (value: number) => void
|
||||
@@ -17,15 +15,12 @@ interface ColorInputProps extends React.InputHTMLAttributes<HTMLButtonElement> {
|
||||
export function ColorInput({
|
||||
color,
|
||||
opacity,
|
||||
collisionRef,
|
||||
popoverSide,
|
||||
setColor,
|
||||
setOpacity,
|
||||
...rest
|
||||
}: ColorInputProps) {
|
||||
const ref = React.useRef<HTMLDivElement>(null)
|
||||
|
||||
function renderColor(color: string) {
|
||||
function renderColor(color?: string) {
|
||||
return color ? (
|
||||
<div className="tl-color-bg" style={{ backgroundColor: color }}>
|
||||
<div className={`w-full h-full bg-${color}-500`}></div>
|
||||
@@ -38,15 +33,8 @@ export function ColorInput({
|
||||
}
|
||||
|
||||
return (
|
||||
<Popover.Root>
|
||||
<Popover.Trigger className="tl-color-drip">{renderColor(color)}</Popover.Trigger>
|
||||
|
||||
<Popover.Content
|
||||
className="tl-popover-content p-1"
|
||||
side={popoverSide}
|
||||
sideOffset={15}
|
||||
collisionBoundary={collisionRef}
|
||||
>
|
||||
<PopoverButton {...rest} border arrow side={popoverSide} label={renderColor(color)}>
|
||||
<div className="p-1">
|
||||
<div className={'tl-color-palette'}>
|
||||
{Object.values(Color).map(value => (
|
||||
<button
|
||||
@@ -62,7 +50,7 @@ export function ColorInput({
|
||||
{setOpacity && (
|
||||
<div className="mx-1 my-2">
|
||||
<Slider.Root
|
||||
defaultValue={[opacity]}
|
||||
defaultValue={[opacity ?? 0]}
|
||||
onValueCommit={value => setOpacity(value[0])}
|
||||
max={1}
|
||||
step={0.1}
|
||||
@@ -76,9 +64,7 @@ export function ColorInput({
|
||||
</Slider.Root>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Popover.Arrow className="tl-popover-arrow" />
|
||||
</Popover.Content>
|
||||
</Popover.Root>
|
||||
</div>
|
||||
</PopoverButton>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
import type { Side } from '@radix-ui/react-popper'
|
||||
import { validUUID } from '@tldraw/core'
|
||||
import React from 'react'
|
||||
import { LogseqContext } from '../../lib/logseq-context'
|
||||
import { Button } from '../Button'
|
||||
import { TablerIcon } from '../icons'
|
||||
import { PopoverButton } from '../PopoverButton'
|
||||
import { TextInput } from './TextInput'
|
||||
|
||||
interface ShapeLinksInputProps extends React.HTMLAttributes<HTMLButtonElement> {
|
||||
shapeType: string
|
||||
side: Side
|
||||
refs: string[]
|
||||
pageId?: string // the portal referenced block id or page name
|
||||
portalType?: 'B' | 'P'
|
||||
onRefsChange: (value: string[]) => void
|
||||
}
|
||||
|
||||
function ShapeLinkItem({
|
||||
id,
|
||||
type,
|
||||
onRemove,
|
||||
}: {
|
||||
id: string
|
||||
type: 'B' | 'P'
|
||||
onRemove?: () => void
|
||||
}) {
|
||||
const {
|
||||
handlers,
|
||||
renderers: { Breadcrumb, PageNameLink },
|
||||
} = React.useContext(LogseqContext)
|
||||
|
||||
return (
|
||||
<div className="tl-shape-links-panel-item color-level">
|
||||
<TablerIcon name={type === 'P' ? 'page' : 'block'} />
|
||||
{type === 'P' ? <PageNameLink pageName={id} /> : <Breadcrumb levelLimit={2} blockId={id} />}
|
||||
<div className="flex-1" />
|
||||
<Button title="Open Page" type="button" onClick={() => handlers?.redirectToPage(id)}>
|
||||
<TablerIcon name="external-link" />
|
||||
</Button>
|
||||
<Button
|
||||
title="Open Page in Right Sidebar"
|
||||
type="button"
|
||||
onClick={() => handlers?.sidebarAddBlock(id, type === 'B' ? 'block' : 'page')}
|
||||
>
|
||||
<TablerIcon name="layout-sidebar-right" />
|
||||
</Button>
|
||||
{onRemove && (
|
||||
<Button title="Remove link" type="button" onClick={onRemove}>
|
||||
<TablerIcon name="x" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function ShapeLinksInput({
|
||||
pageId,
|
||||
portalType,
|
||||
shapeType,
|
||||
refs,
|
||||
side,
|
||||
onRefsChange,
|
||||
...rest
|
||||
}: ShapeLinksInputProps) {
|
||||
const noOfLinks = refs.length + (pageId ? 1 : 0)
|
||||
const [value, setValue] = React.useState('')
|
||||
|
||||
return (
|
||||
<PopoverButton
|
||||
{...rest}
|
||||
side={side}
|
||||
label={
|
||||
<div className="flex gap-1 relative items-center justify-center px-1">
|
||||
<TablerIcon name="link" />
|
||||
{noOfLinks > 0 && <div className="tl-shape-links-count">{noOfLinks}</div>}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="color-level">
|
||||
{pageId && portalType && (
|
||||
<div className="tl-shape-links-reference-panel">
|
||||
<div className="text-base font-bold inline-flex gap-1 items-center">
|
||||
<TablerIcon name="external-link" />
|
||||
Your Reference
|
||||
</div>
|
||||
<div className="h-2" />
|
||||
<ShapeLinkItem type={portalType} id={pageId} />
|
||||
</div>
|
||||
)}
|
||||
<div className="tl-shape-links-panel color-level">
|
||||
<div className="text-base font-bold inline-flex gap-1 items-center">
|
||||
<TablerIcon name="link" />
|
||||
Your Links
|
||||
</div>
|
||||
<div className="h-2" />
|
||||
<div className="whitespace-pre-wrap">
|
||||
This <strong>{shapeType}</strong> can be linked to any other block, page or whiteboard
|
||||
element you have stored in Logseq.
|
||||
</div>
|
||||
<TextInput
|
||||
value={value}
|
||||
onChange={e => {
|
||||
setValue(e.target.value)
|
||||
}}
|
||||
onKeyDown={e => {
|
||||
if (e.key === 'Enter') {
|
||||
if (value && !refs.includes(value)) {
|
||||
onRefsChange([...refs, value])
|
||||
}
|
||||
}
|
||||
e.stopPropagation()
|
||||
}}
|
||||
/>
|
||||
<div className="flex flex-col items-stretch gap-2">
|
||||
{refs.map((ref, i) => {
|
||||
return (
|
||||
<ShapeLinkItem
|
||||
key={ref}
|
||||
id={ref}
|
||||
type={validUUID(ref) ? 'B' : 'P'}
|
||||
onRemove={() => {
|
||||
onRefsChange(refs.filter((_, j) => i !== j))
|
||||
}}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PopoverButton>
|
||||
)
|
||||
}
|
||||
@@ -16,6 +16,7 @@ export interface LogseqContextValue {
|
||||
}>
|
||||
Breadcrumb: React.FC<{
|
||||
blockId: string
|
||||
levelLimit?: number
|
||||
}>
|
||||
PageNameLink: React.FC<{
|
||||
pageName: string
|
||||
|
||||
@@ -125,7 +125,7 @@ html[data-theme='light'] {
|
||||
margin-left: auto;
|
||||
padding-left: 20px;
|
||||
|
||||
.keyboard-shortcut>code {
|
||||
.keyboard-shortcut > code {
|
||||
padding: 4px !important;
|
||||
text-rendering: initial;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
|
||||
@@ -349,7 +349,7 @@ button.tl-select-input-trigger {
|
||||
bottom: -3px;
|
||||
}
|
||||
|
||||
.floating-panel[data-tool-locked='true']>.tl-button[data-selected='true']::after {
|
||||
.floating-panel[data-tool-locked='true'] > .tl-button[data-selected='true']::after {
|
||||
@apply block absolute;
|
||||
|
||||
content: '';
|
||||
@@ -602,7 +602,7 @@ button.tl-select-input-trigger {
|
||||
background-color: rgba(0, 0, 0, 0.5) !important;
|
||||
}
|
||||
|
||||
>i.ti {
|
||||
> i.ti {
|
||||
transform: translateY(-0.5px);
|
||||
}
|
||||
}
|
||||
@@ -721,7 +721,7 @@ button.tl-select-input-trigger {
|
||||
|
||||
.page-ref {
|
||||
color: var(--ls-title-text-color);
|
||||
background: var(--ls-tertiary-background-color));
|
||||
background: var(--ls-tertiary-background-color);
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
@@ -750,7 +750,7 @@ button.tl-select-input-trigger {
|
||||
.tl-image-shape-container {
|
||||
@apply h-full w-full overflow-hidden flex items-center justify-center pointer-events-auto;
|
||||
|
||||
&[data-asset-loaded="false"] {
|
||||
&[data-asset-loaded='false'] {
|
||||
background-color: var(--ls-secondary-background-color);
|
||||
}
|
||||
}
|
||||
@@ -770,7 +770,7 @@ button.tl-select-input-trigger {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.tl-html-anchor>iframe {
|
||||
.tl-html-anchor > iframe {
|
||||
@apply h-full w-full !important;
|
||||
margin: 0;
|
||||
}
|
||||
@@ -778,7 +778,7 @@ button.tl-select-input-trigger {
|
||||
.tl-video-container {
|
||||
@apply h-full w-full m-0 relative;
|
||||
|
||||
>video {
|
||||
> video {
|
||||
@apply h-full w-full m-0 relative;
|
||||
}
|
||||
}
|
||||
@@ -905,7 +905,7 @@ html[data-theme='dark'] {
|
||||
}
|
||||
|
||||
.tl-popover-content {
|
||||
@apply rounded-sm drop-shadow-md;
|
||||
@apply rounded-lg drop-shadow-md;
|
||||
|
||||
background-color: var(--ls-secondary-background-color);
|
||||
z-index: 100000;
|
||||
@@ -951,8 +951,7 @@ html[data-theme='dark'] {
|
||||
|
||||
user-select: none;
|
||||
touch-action: none;
|
||||
background: url("../img/checker.png");
|
||||
|
||||
background: url('../img/checker.png');
|
||||
}
|
||||
|
||||
.tl-slider-track {
|
||||
@@ -961,11 +960,11 @@ html[data-theme='dark'] {
|
||||
background: linear-gradient(90deg, transparent, var(--ls-tertiary-background-color));
|
||||
border: 1px solid var(--ls-secondary-border-color);
|
||||
|
||||
&[data-orientation="horizontal"] {
|
||||
&[data-orientation='horizontal'] {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
&[data-orientation="vertical"] {
|
||||
&[data-orientation='vertical'] {
|
||||
width: 10px;
|
||||
}
|
||||
}
|
||||
@@ -999,8 +998,8 @@ html[data-theme='dark'] {
|
||||
border-top-right-radius: 6px;
|
||||
border-bottom-right-radius: 6px;
|
||||
|
||||
|
||||
&:hover, &.open {
|
||||
&:hover,
|
||||
&.open {
|
||||
@apply p-1.5;
|
||||
}
|
||||
}
|
||||
@@ -1011,28 +1010,53 @@ html[data-theme='dark'] {
|
||||
height: 32px;
|
||||
transform: translate(4px, -6px);
|
||||
|
||||
&:hover, &.open {
|
||||
&:hover,
|
||||
&.open {
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
.tl-shape-links-count {
|
||||
@apply px-1;
|
||||
@apply px-1 rounded-sm;
|
||||
background-color: var(--ls-page-properties-background-color);
|
||||
}
|
||||
|
||||
.tl-shape-links-panel {
|
||||
@apply absolute shadow-lg rounded-lg p-3;
|
||||
.tl-shape-links-panel,
|
||||
.tl-shape-links-reference-panel {
|
||||
@apply p-3;
|
||||
width: 320px;
|
||||
transform: translate(20px, -8px);
|
||||
left: 100%;
|
||||
top: 0;
|
||||
background-color: var(--ls-secondary-background-color);
|
||||
color: var(--ls-primary-text-color);
|
||||
}
|
||||
|
||||
.tl-shape-links-reference-panel {
|
||||
@apply rounded-t-lg;
|
||||
}
|
||||
|
||||
.tl-shape-links-panel-item {
|
||||
@apply rounded py-1 px-4 flex items-center justify-center gap-1;
|
||||
@apply rounded py-1 px-4 pr-2 flex items-center justify-center gap-1;
|
||||
color: var(--color-text);
|
||||
background-color: var(--ls-tertiary-background-color);
|
||||
|
||||
.page-ref {
|
||||
color: var(--ls-title-text-color);
|
||||
}
|
||||
}
|
||||
|
||||
.tl-popover-trigger-button {
|
||||
@apply rounded text-sm;
|
||||
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
padding: 3px;
|
||||
color: var(--ls-secondary-text-color);
|
||||
|
||||
&[data-border='true'] {
|
||||
border: 1px solid var(--ls-secondary-border-color);
|
||||
}
|
||||
|
||||
&[data-state='open'] {
|
||||
background-color: var(--ls-tertiary-background-color);
|
||||
color: var(--ls-primary-text-color);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ export default function App() {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={`h-screen w-screen`}>
|
||||
<div className={`h-screen w-screen`} id="main-content-container">
|
||||
<ThemeSwitcher />
|
||||
<PreviewButton model={model} />
|
||||
<TldrawApp
|
||||
|
||||
@@ -1,3 +1,81 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
.color-level {
|
||||
background-color: var(--color-level-1);
|
||||
}
|
||||
|
||||
.color-level .color-level {
|
||||
background-color: var(--color-level-2);
|
||||
}
|
||||
|
||||
.color-level .color-level .color-level {
|
||||
background-color: var(--color-level-3);
|
||||
}
|
||||
|
||||
.color-level .color-level .color-level .color-level {
|
||||
background-color: var(--color-level-4);
|
||||
}
|
||||
|
||||
.color-level .color-level .color-level .color-level .color-level {
|
||||
background-color: var(--color-level-5);
|
||||
}
|
||||
|
||||
.color-level .color-level .color-level .color-level .color-level .color-level {
|
||||
background-color: var(--color-level-3);
|
||||
}
|
||||
|
||||
.color-level .color-level .color-level .color-level .color-level .color-level .color-level {
|
||||
background-color: var(--color-level-4);
|
||||
}
|
||||
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level {
|
||||
background-color: var(--color-level-5);
|
||||
}
|
||||
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level {
|
||||
background-color: var(--color-level-3);
|
||||
}
|
||||
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level {
|
||||
background-color: var(--color-level-4);
|
||||
}
|
||||
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level
|
||||
.color-level {
|
||||
background-color: var(--color-level-5);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"postinstall": "yarn build",
|
||||
"dev": "cd demo && yarn dev",
|
||||
"fix:style": "yarn run pretty-quick",
|
||||
"pretty-quick": "pretty-quick --pattern 'tldraw/**/*.{js,jsx,ts,tsx,html}'"
|
||||
"pretty-quick": "pretty-quick --pattern 'tldraw/**/*.{js,jsx,ts,tsx,css,html}'"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^17.0.42",
|
||||
|
||||
Reference in New Issue
Block a user