瀏覽代碼

feat: add bold/italic controls to text shape

Peng Xiao 3 年之前
父節點
當前提交
134d7bf7dd

+ 68 - 35
tldraw/apps/tldraw-logseq/src/components/ContextBar/contextBarActionFactory.tsx

@@ -35,7 +35,7 @@ export const contextBarActionTypes = [
   'NoFill',
   'StrokeType',
   'ScaleLevel',
-  // 'TextStyle',
+  'TextStyle',
   'YoutubeLink',
   'LogseqPortalViewMode',
   'ArrowMode',
@@ -58,7 +58,7 @@ const shapeMapping: Partial<Record<ShapeType, ContextBarActionType[]>> = {
   line: ['Edit', 'Swatch', 'ArrowMode'],
   pencil: ['Swatch'],
   highlighter: ['Swatch'],
-  text: ['Edit', 'Swatch', 'ScaleLevel', 'AutoResizing'],
+  text: ['Edit', 'Swatch', 'ScaleLevel', 'AutoResizing', 'TextStyle'],
   html: ['ScaleLevel', 'AutoResizing'],
 }
 
@@ -80,6 +80,7 @@ const EditAction = observer(() => {
     <button
       className="tl-contextbar-button"
       type="button"
+      title="Edit"
       onClick={() => {
         app.api.editShape(shape)
         if (shape.props.type === 'logseq-portal') {
@@ -110,6 +111,8 @@ const AutoResizingAction = observer(() => {
 
   return (
     <ToggleInput
+      title="Auto Resize"
+      toggle={shapes.every(s => s.props.type === 'logseq-portal')}
       className="tl-contextbar-button"
       pressed={pressed}
       onPressedChange={v => {
@@ -150,6 +153,7 @@ const LogseqPortalViewModeAction = observer(() => {
   ]
   return (
     <ToggleGroupInput
+      title="View Mode"
       options={ViewModeOptions}
       value={collapsed ? '1' : '0'}
       onValueChange={v => {
@@ -194,6 +198,7 @@ const ScaleLevelAction = observer(() => {
   ]
   return (
     <SelectInput
+      title="Scale Level"
       options={sizeOptions}
       value={scaleLevel}
       onValueChange={v => {
@@ -216,6 +221,7 @@ const OpenPageAction = observer(() => {
   return (
     <span className="flex gap-1">
       <button
+        title="Open Page in Right Sidebar"
         className="tl-contextbar-button"
         type="button"
         onClick={() => handlers?.sidebarAddBlock(pageId, blockType === 'B' ? 'block' : 'page')}
@@ -223,6 +229,7 @@ const OpenPageAction = observer(() => {
         <TablerIcon name="layout-sidebar-right" />
       </button>
       <button
+        title="Open Page"
         className="tl-contextbar-button"
         type="button"
         onClick={() => handlers?.redirectToPage(pageId)}
@@ -243,8 +250,14 @@ const YoutubeLinkAction = observer(() => {
 
   return (
     <span className="flex gap-3">
-      <TextInput className="tl-youtube-link" value={`${shape.props.url}`} onChange={handleChange} />
+      <TextInput
+        title="YouTube Link"
+        className="tl-youtube-link"
+        value={`${shape.props.url}`}
+        onChange={handleChange}
+      />
       <button
+        title="Open YouTube Link"
         className="tl-contextbar-button"
         type="button"
         onClick={() => window.logseq?.api?.open_external_link?.(shape.props.url)}
@@ -269,7 +282,12 @@ const NoFillAction = observer(() => {
   const noFill = shapes.every(s => s.props.noFill)
 
   return (
-    <ToggleInput className="tl-contextbar-button" pressed={noFill} onPressedChange={handleChange}>
+    <ToggleInput
+      title="Fill Toggle"
+      className="tl-contextbar-button"
+      pressed={noFill}
+      onPressedChange={handleChange}
+    >
       {noFill ? <TablerIcon name="eye-off" /> : <TablerIcon name="eye" />}
     </ToggleInput>
   )
@@ -301,7 +319,7 @@ const SwatchAction = observer(() => {
   }, [])
 
   const value = shapes[0].props.noFill ? shapes[0].props.stroke : shapes[0].props.fill
-  return <ColorInput value={value} onChange={handleChange} />
+  return <ColorInput title="Color Picker" value={value} onChange={handleChange} />
 })
 
 const StrokeTypeAction = observer(() => {
@@ -329,6 +347,7 @@ const StrokeTypeAction = observer(() => {
 
   return (
     <ToggleGroupInput
+      title="Stroke Type"
       options={StrokeTypeOptions}
       value={value}
       onValueChange={v => {
@@ -372,6 +391,7 @@ const ArrowModeAction = observer(() => {
 
   return (
     <ToggleGroupMultipleInput
+      title="Arrow Head"
       options={StrokeTypeOptions}
       value={value}
       onValueChange={v => {
@@ -386,35 +406,48 @@ const ArrowModeAction = observer(() => {
   )
 })
 
-// const TextStyleAction = observer(() => {
-//   const app = useApp<Shape>()
-//   const shapes = filterShapeByAction<TextShape>(
-//     app.selectedShapesArray,
-//     'TextStyle'
-//   )
-
-//   const StrokeTypeOptions: ToggleGroupInputOption[] = [
-//     {
-//       value: 'bold',
-//       icon: 'bold',
-//     },
-//     {
-//       value: 'italic',
-//       icon: 'italic',
-//     },
-//   ]
-
-//   const bold = shapes.every(s => s.props.fontWeight > 500 ?)
-//   const italic = shapes.every(s => s.props.fontStyle === 'italic')
-
-//   const value = [startValue ? 'start' : null, endValue ? 'end' : null].filter(isNonNullable)
-
-//   return (
-//     <ToggleInput className="tl-contextbar-button" pressed={noFill} onPressedChange={handleChange}>
-//       {noFill ? <TablerIcon name="eye-off" /> : <TablerIcon name="eye" />}
-//     </ToggleInput>
-//   )
-// })
+const TextStyleAction = observer(() => {
+  const app = useApp<Shape>()
+  const shapes = filterShapeByAction<TextShape>(app.selectedShapesArray, 'TextStyle')
+
+  const bold = shapes.every(s => s.props.fontWeight > 500)
+  const italic = shapes.every(s => s.props.italic)
+
+  return (
+    <span className="flex gap-1">
+      <ToggleInput
+        title='Bold'
+        className="tl-contextbar-button"
+        pressed={bold}
+        onPressedChange={v => {
+          shapes.forEach(shape => {
+            shape.update({
+              fontWeight: v ? 700 : 400,
+            })
+          })
+          app.persist()
+        }}
+      >
+        <TablerIcon name="bold" />
+      </ToggleInput>
+      <ToggleInput
+        title='Italic'
+        className="tl-contextbar-button"
+        pressed={italic}
+        onPressedChange={v => {
+          shapes.forEach(shape => {
+            shape.update({
+              italic: v,
+            })
+          })
+          app.persist()
+        }}
+      >
+        <TablerIcon name="italic" />
+      </ToggleInput>
+    </span>
+  )
+})
 
 contextBarActionMapping.set('Edit', EditAction)
 contextBarActionMapping.set('AutoResizing', AutoResizingAction)
@@ -426,7 +459,7 @@ contextBarActionMapping.set('NoFill', NoFillAction)
 contextBarActionMapping.set('Swatch', SwatchAction)
 contextBarActionMapping.set('StrokeType', StrokeTypeAction)
 contextBarActionMapping.set('ArrowMode', ArrowModeAction)
-// contextBarActionMapping.set('TextStyle', TextStyleAction)
+contextBarActionMapping.set('TextStyle', TextStyleAction)
 
 const getContextBarActionTypes = (type: ShapeType) => {
   return (shapeMapping[type] ?? []).filter(isNonNullable)

+ 3 - 1
tldraw/apps/tldraw-logseq/src/components/inputs/ToggleInput.tsx

@@ -1,14 +1,16 @@
 import * as Toggle from '@radix-ui/react-toggle'
 
 interface ToggleInputProps extends React.HTMLAttributes<HTMLElement> {
+  toggle?: boolean
   pressed: boolean
   onPressedChange: (value: boolean) => void
 }
 
-export function ToggleInput({ pressed, onPressedChange, className, ...rest }: ToggleInputProps) {
+export function ToggleInput({ toggle = true, pressed, onPressedChange, className, ...rest }: ToggleInputProps) {
   return (
     <Toggle.Root
       {...rest}
+      data-toggle={toggle}
       className={'tl-toggle-input' + (className ? ' ' + className : '')}
       pressed={pressed}
       onPressedChange={onPressedChange}

+ 16 - 2
tldraw/apps/tldraw-logseq/src/lib/shapes/TextShape.tsx

@@ -12,6 +12,7 @@ export interface TextShapeProps extends TLTextShapeProps, CustomStyleProps {
   fontFamily: string
   fontSize: number
   fontWeight: number
+  italic: boolean
   lineHeight: number
   padding: number
   type: 'text'
@@ -41,6 +42,7 @@ export class TextShape extends TLTextShape<TextShapeProps> {
     lineHeight: 1.2,
     fontSize: 20,
     fontWeight: 400,
+    italic: false,
     padding: 4,
     fontFamily: "var(--ls-font-family), 'Helvetica Neue', Helvetica, Arial, sans-serif",
     borderRadius: 0,
@@ -54,7 +56,17 @@ export class TextShape extends TLTextShape<TextShapeProps> {
 
   ReactComponent = observer(({ events, isErasing, isEditing, onEditingEnd }: TLComponentProps) => {
     const {
-      props: { opacity, fontFamily, fontSize, fontWeight, lineHeight, text, stroke, padding },
+      props: {
+        opacity,
+        fontFamily,
+        fontSize,
+        fontWeight,
+        italic,
+        lineHeight,
+        text,
+        stroke,
+        padding,
+      },
     } = this
     const rInput = React.useRef<HTMLTextAreaElement>(null)
 
@@ -171,6 +183,7 @@ export class TextShape extends TLTextShape<TextShapeProps> {
           data-isediting={isEditing}
           style={{
             fontFamily,
+            fontStyle: italic ? 'italic' : 'normal',
             fontSize,
             fontWeight,
             padding,
@@ -217,7 +230,7 @@ export class TextShape extends TLTextShape<TextShapeProps> {
   @action setScaleLevel = async (v?: SizeLevel) => {
     this.update({
       scaleLevel: v,
-      fontSize: levelToScale[v ?? 'md']
+      fontSize: levelToScale[v ?? 'md'],
     })
     this.onResetBounds()
   }
@@ -234,6 +247,7 @@ export class TextShape extends TLTextShape<TextShapeProps> {
         rx={borderRadius}
         ry={borderRadius}
         fill="transparent"
+        stroke="none"
       />
     )
   })

+ 3 - 0
tldraw/apps/tldraw-logseq/src/styles.css

@@ -747,6 +747,9 @@ html[data-theme='dark'] {
   &:hover {
     background-color: var(--ls-tertiary-background-color);
   }
+  &[data-toggle='false'] {
+    opacity: 1;
+  }
   &[data-state='on'] {
     background-color: var(--ls-tertiary-background-color);
     color: var(--ls-primary-text-color);

+ 1 - 1
tldraw/demo/postcss.config.js

@@ -7,7 +7,7 @@ module.exports = {
     tailwindcss: {
       content: [
         './**/*.jsx',
-        '../../apps/**/*.{js,jsx,ts,tsx}',
+        '../apps/**/*.{js,jsx,ts,tsx}',
       ]
     },
     autoprefixer: {},

+ 0 - 8
tldraw/demo/tailwind.config.js

@@ -1,8 +0,0 @@
-module.exports = {
-  // just import everything for ease of dev
-  safelist: [{ pattern: /.*/ }],
-  theme: {
-    extend: {},
-  },
-  plugins: [],
-}