diff --git a/packages/react/storybook/.storybook/preview.ts b/packages/react/storybook/.storybook/preview.ts index b6dc191f..1dbcd053 100644 --- a/packages/react/storybook/.storybook/preview.ts +++ b/packages/react/storybook/.storybook/preview.ts @@ -1,6 +1,20 @@ import type {Preview} from '@storybook/react-vite' const preview: Preview = { + globalTypes: { + showPlainValue: { + name: 'Plain Value', + description: 'Toggle plain value panel', + defaultValue: 'show', + toolbar: { + icon: 'eye', + items: [ + {value: 'show', icon: 'eye'}, + {value: 'hide', icon: 'eyeclose'}, + ], + }, + }, + }, parameters: { controls: { hideNoControlsWarning: true, diff --git a/packages/react/storybook/src/pages/Drag/Drag.stories.tsx b/packages/react/storybook/src/pages/Drag/Drag.stories.tsx index c3ab62b5..96b43270 100644 --- a/packages/react/storybook/src/pages/Drag/Drag.stories.tsx +++ b/packages/react/storybook/src/pages/Drag/Drag.stories.tsx @@ -1,11 +1,12 @@ -import {MarkedInput} from '@markput/react' -import type {MarkProps, Option} from '@markput/react' +import {MarkedInput, useMark} from '@markput/react' +import type {MarkProps, MarkedInputProps, Option} from '@markput/react' import type {Meta, StoryObj} from '@storybook/react-vite' import type {CSSProperties, ReactNode} from 'react' import {useState} from 'react' import {Text} from '../../shared/components/Text' import {DRAG_MARKDOWN} from '../../shared/lib/sampleTexts' +import {withPlainValue} from '../../shared/lib/withPlainValue' import {markdownOptions} from '../Nested/MarkdownOptions' export default { @@ -72,114 +73,120 @@ const todoOptions: Option[] = [ }), }, { - markup: '- [ ] __nested__\n\n', - mark: (props: MarkProps) => ({...props, todo: 'pending' as const, style: {display: 'block'} as CSSProperties}), - }, - { - markup: '- [x] __nested__\n\n', - mark: (props: MarkProps) => ({ - ...props, - todo: 'done' as const, - style: {display: 'block', textDecoration: 'line-through', opacity: 0.5} as CSSProperties, - }), - }, - { - markup: '\t- [ ] __nested__\n\n', - mark: (props: MarkProps) => ({ - ...props, - todo: 'pending' as const, - style: {display: 'block', paddingLeft: '1.5em'} as CSSProperties, - }), + markup: '- [__value__] __nested__\n', + mark: (props: MarkProps) => { + const isDone = props.value === 'x' + return { + ...props, + todo: isDone ? 'done' : 'pending', + style: { + display: 'block', + opacity: isDone ? 0.55 : undefined, + } as CSSProperties, + } + }, }, { - markup: '\t- [x] __nested__\n\n', - mark: (props: MarkProps) => ({ - ...props, - todo: 'done' as const, - style: { - display: 'block', - paddingLeft: '1.5em', - textDecoration: 'line-through', - opacity: 0.5, - } as CSSProperties, - }), + markup: '\t- [__value__] __nested__\n', + mark: (props: MarkProps) => { + const isDone = props.value === 'x' + return { + ...props, + todo: isDone ? 'done' : 'pending', + style: { + display: 'block', + paddingLeft: '22px', + borderLeft: '2px solid #d0d7de', + opacity: isDone ? 0.55 : undefined, + } as CSSProperties, + } + }, }, { - markup: '\t\t- [ ] __nested__\n\n', - mark: (props: MarkProps) => ({ - ...props, - todo: 'pending' as const, - style: {display: 'block', paddingLeft: '3em'} as CSSProperties, - }), + markup: '\t\t- [__value__] __nested__\n', + mark: (props: MarkProps) => { + const isDone = props.value === 'x' + return { + ...props, + todo: isDone ? 'done' : 'pending', + style: { + display: 'block', + paddingLeft: '22px', + marginLeft: '24px', + borderLeft: '2px solid #d0d7de', + opacity: isDone ? 0.55 : undefined, + } as CSSProperties, + } + }, }, { - markup: '\t\t- [x] __nested__\n\n', + markup: '> __nested__\n\n', mark: (props: MarkProps) => ({ ...props, - todo: 'done' as const, style: { display: 'block', - paddingLeft: '3em', - textDecoration: 'line-through', - opacity: 0.5, + fontSize: '0.85em', + color: '#888', + fontStyle: 'italic', + marginTop: 16, } as CSSProperties, }), }, - { - markup: '> __nested__\n\n', - mark: (props: MarkProps) => ({ - ...props, - style: {display: 'block', fontSize: '0.85em', color: '#888', fontStyle: 'italic'} as CSSProperties, - }), - }, ] -const TodoMark = ({children, value, style, todo}: TodoMarkProps) => ( - - {todo && {todo === 'done' ? '\u2611' : '\u2610'}} - {children || value} - -) +const TodoMark = ({nested, style, todo}: TodoMarkProps) => { + const mark = useMark() + const [isDone, setIsDone] = useState(mark.value === 'x') + + return ( + + {todo !== undefined && ( + { + const next = !isDone + setIsDone(next) + mark.value = next ? 'x' : ' ' + }} + disabled={mark.readOnly} + style={{marginRight: 6}} + /> + )} + {nested} + + ) +} const TODO_VALUE = `# \u{1F4CB} Project Launch Checklist - [ ] Design Phase - \t- [ ] Create wireframes - \t- [x] Define color palette - \t- [ ] Design component library - - [x] Research - \t- [x] Analyze competitors - \t- [x] User interviews - \t\t- [x] Draft interview questions - \t\t- [x] Schedule 5 sessions - - [ ] Development - \t- [ ] Set up CI/CD pipeline - \t- [x] Write unit tests - \t- [ ] API integration - \t\t- [ ] Auth endpoints - \t\t- [ ] Data sync - - [ ] Launch - \t- [ ] Final QA pass - \t- [ ] Deploy to production +> \u2610 = pending \u2611 = done -> \u2610 = pending \u2611 = done` +` // ─── Test helper stories (used by Drag.spec.tsx) ───────────────────────────── @@ -229,22 +236,12 @@ export const ReadOnlyDrag: Story = { // ─── Todo list (all marks include \n\n) ─────────────────────────────────────── -export const TodoList: Story = { - render: () => { - const [value, setValue] = useState(TODO_VALUE) - - return ( -
- - -
- ) +export const TodoList: StoryObj> = { + decorators: [withPlainValue], + args: { + Mark: TodoMark, + options: todoOptions, + value: TODO_VALUE, + drag: true, }, } \ No newline at end of file diff --git a/packages/react/storybook/src/shared/components/Text/Text.css b/packages/react/storybook/src/shared/components/Text/Text.css index 20635e98..b110eae8 100644 --- a/packages/react/storybook/src/shared/components/Text/Text.css +++ b/packages/react/storybook/src/shared/components/Text/Text.css @@ -1,12 +1,79 @@ -pre { - font-family: - Apple-System, - Arial, - Helvetica, - PingFang SC, - Hiragino Sans GB, - Microsoft YaHei, - STXihei, - sans-serif; - font-size: 14px; +.text-container { + border: 1px solid #d0d7de; + border-radius: 6px; + overflow: hidden; +} + +.text-header { + background: #f0f3f6; + padding: 6px 16px; + border-bottom: 1px solid #d0d7de; +} + +.text-label { + font-size: 12px; + font-weight: 600; + color: #6b7280; + text-transform: uppercase; + letter-spacing: 0.06em; + font-family: Arial, Helvetica, sans-serif; +} + +.text-label::before { + content: ' '; + color: #0550ae; + font-style: normal; + letter-spacing: 0; +} + +.text-container.text-inset { + border: none; + border-left: 1px solid #d0d7de; + border-radius: 0; + height: 100%; + background: #f6f8fa; +} + +.text-container.text-inset .text-header { + background: transparent; + border-bottom-color: #d0d7de; +} + +.text-container.text-inset .text-pre { + background: transparent; +} + +.tok-heading-prefix { + color: #8b949e; +} +.tok-heading-text { + color: #0550ae; + font-weight: 600; +} +.tok-bullet { + color: #8b949e; +} +.tok-checked { + color: #1a7f37; + font-weight: 600; +} +.tok-unchecked { + color: #8b949e; +} +.tok-blockquote { + color: #8b949e; + font-style: italic; +} + +.text-pre { + font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; + font-size: 13px; + background: #f6f8fa; + padding: 12px 12px; + overflow-x: auto; + line-height: 1.5; + color: #24292f; + margin: 0; + white-space: pre-wrap; + word-break: break-word; } diff --git a/packages/react/storybook/src/shared/components/Text/Text.tsx b/packages/react/storybook/src/shared/components/Text/Text.tsx index c5ae58a3..8ee03e58 100644 --- a/packages/react/storybook/src/shared/components/Text/Text.tsx +++ b/packages/react/storybook/src/shared/components/Text/Text.tsx @@ -3,12 +3,16 @@ import './Text.css' export interface TextProps { value: string label?: string + className?: string } -export const Text = ({value, label}: TextProps) => ( - <> -
- {label && {label}} -
{value}
- +export const Text = ({value, label, className}: TextProps) => ( +
+ {label && ( +
+ {label} +
+ )} +
{value}
+
) \ No newline at end of file diff --git a/packages/react/storybook/src/shared/lib/withPlainValue.tsx b/packages/react/storybook/src/shared/lib/withPlainValue.tsx new file mode 100644 index 00000000..3b72165a --- /dev/null +++ b/packages/react/storybook/src/shared/lib/withPlainValue.tsx @@ -0,0 +1,42 @@ +import {useCallback} from 'react' +import {useArgs, useGlobals} from 'storybook/preview-api' + +import {Text} from '../components/Text' + +export const withPlainValue = (Story: any) => { + const [args, updateArgs] = useArgs() + const [globals] = useGlobals() + const showPlainValue = globals.showPlainValue !== 'hide' + + const handleChange = useCallback( + (newValue: string) => { + updateArgs({value: newValue}) + }, + [updateArgs] + ) + + if (!showPlainValue) { + return + } + + return ( +
+
+ +
+ {args.value !== undefined && ( +
+ +
+ )} +
+ ) +} \ No newline at end of file diff --git a/packages/vue/storybook/src/pages/Drag/Drag.stories.ts b/packages/vue/storybook/src/pages/Drag/Drag.stories.ts index e711e0d5..5477601b 100644 --- a/packages/vue/storybook/src/pages/Drag/Drag.stories.ts +++ b/packages/vue/storybook/src/pages/Drag/Drag.stories.ts @@ -177,11 +177,11 @@ const todoOptions: Option[] = [ }), }, { - markup: '- [ ] __nested__\n\n' as Markup, + markup: '- [ ] __nested__\n' as Markup, mark: (props: MarkProps) => ({...props, todo: 'pending', style: {display: 'block'}}), }, { - markup: '- [x] __nested__\n\n' as Markup, + markup: '- [x] __nested__\n' as Markup, mark: (props: MarkProps) => ({ ...props, todo: 'done', @@ -189,11 +189,11 @@ const todoOptions: Option[] = [ }), }, { - markup: '\t- [ ] __nested__\n\n' as Markup, + markup: '\t- [ ] __nested__\n' as Markup, mark: (props: MarkProps) => ({...props, todo: 'pending', style: {display: 'block', paddingLeft: '1.5em'}}), }, { - markup: '\t- [x] __nested__\n\n' as Markup, + markup: '\t- [x] __nested__\n' as Markup, mark: (props: MarkProps) => ({ ...props, todo: 'done', @@ -201,11 +201,11 @@ const todoOptions: Option[] = [ }), }, { - markup: '\t\t- [ ] __nested__\n\n' as Markup, + markup: '\t\t- [ ] __nested__\n' as Markup, mark: (props: MarkProps) => ({...props, todo: 'pending', style: {display: 'block', paddingLeft: '3em'}}), }, { - markup: '\t\t- [x] __nested__\n\n' as Markup, + markup: '\t\t- [x] __nested__\n' as Markup, mark: (props: MarkProps) => ({ ...props, todo: 'done', @@ -224,42 +224,26 @@ const todoOptions: Option[] = [ const TODO_VALUE = `# \u{1F4CB} Project Launch Checklist - [ ] Design Phase - \t- [ ] Create wireframes - \t- [x] Define color palette - \t- [ ] Design component library - - [x] Research - \t- [x] Analyze competitors - \t- [x] User interviews - \t\t- [x] Draft interview questions - \t\t- [x] Schedule 5 sessions - - [ ] Development - \t- [ ] Set up CI/CD pipeline - \t- [x] Write unit tests - \t- [ ] API integration - \t\t- [ ] Auth endpoints - \t\t- [ ] Data sync - - [ ] Launch - \t- [ ] Final QA pass - \t- [ ] Deploy to production +> \u2610 = pending \u2611 = done -> \u2610 = pending \u2611 = done` +` // ─── Test helper stories (used by Drag.spec.ts) ──────────────────────────────