Skip to content

Commit

Permalink
feat: 🎸 implment StrBinding
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed Dec 1, 2023
1 parent 8614192 commit ca34dba
Show file tree
Hide file tree
Showing 8 changed files with 414 additions and 18 deletions.
19 changes: 1 addition & 18 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import * as React from 'react';
import {Preview} from '@storybook/react';
import {Provider, GlobalCss} from 'nano-theme';
import {useStoryContext} from '@storybook/addons';

import 'nano-theme/lib/global-reset';

const preview: Preview = {
parameters: {
Expand All @@ -14,19 +9,7 @@ const preview: Preview = {
date: /Date$/i,
},
},
},
decorators: [
(Story) => {
const color = useStoryContext()?.globals?.backgrounds?.value;
const isDark = color ? String(color)[1].toLowerCase() !== 'f' : false;
return (
<Provider theme={isDark ? 'dark' : 'light'}>
<GlobalCss />
<Story />
</Provider>
);
},
],
}
};

export default preview;
17 changes: 17 additions & 0 deletions src/Selection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {ITimestampStruct} from 'json-joy/es2020/json-crdt-patch/clock';

export class Selection {
/** Local selection start. */
public start: number | null = null;
/** Local selection end. */
public end: number | null = null;
/** Local selection direction. */
public dir: 'forward' | 'backward' | 'none' | null = null;
/** Timestamp when selection last updated. */
public ts: number = 0;
/** Model tick. */
public tick: number = 0;
/** Remote selection start. */ public startId: ITimestampStruct | null = null;
/** Remote selection end. */
public endId: ITimestampStruct | null = null;
}
105 changes: 105 additions & 0 deletions src/StrBinding.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import * as React from 'react';
import {Model} from 'json-joy/es2020/json-crdt';
import {StrBinding} from './StrBinding';
import type {Meta, StoryObj} from '@storybook/react';

const Demo: React.FC<{textarea: boolean}> = ({textarea}) => {
const inputRef = React.useRef<HTMLInputElement | HTMLTextAreaElement>(null);
const [model, clone] = React.useMemo(() => {
const model = Model.withLogicalClock();
model.api.root({text: 'Hell'});
return [model, model.clone()];
}, [1]);
React.useSyncExternalStore(model.api.subscribe, () => model.tick);
React.useEffect(() => {
if (!inputRef.current) return;
const input = inputRef.current;
const unbind = StrBinding.bind(model.api.str(['text']), input, true);
return () => {
unbind();
};
}, [model]);

return (
<div>
{textarea ? <textarea ref={inputRef as any} /> : <input ref={inputRef as any} type="text" />}
<div>
<button
onClick={() => {
const input = inputRef.current;
if (!input) return;
input.value += '!';
}}
>
Append "!" to input
</button>
</div>
<div>
<button
onClick={() => {
const str = model.api.str(['text']);
str.ins(str.view().length, '?');
}}
>
Append "?" to model
</button>
</div>
<div>
<button
onClick={() => {
setTimeout(() => {
const str = model.api.str(['text']);
str.ins(str.view().length, '?');
}, 2000);
}}
>
Append "?" to model after 2s
</button>
</div>
<div>
<button
onClick={() => {
setTimeout(() => {
const str = model.api.str(['text']);
str.ins(0, '1. ');
}, 2000);
}}
>
Prepend "1. " to model after 2s
</button>
</div>
<div>
<button
onClick={() => {
setTimeout(() => {
model.reset(clone);
}, 2000);
}}
>
RESET after 2s
</button>
</div>
<pre style={{fontSize: '10px'}}>
<code>{model.root + ''}</code>
</pre>
</div>
);
};

const meta: Meta<typeof Text> = {
title: 'StrBinding',
component: Demo as any,
argTypes: {},
};

export default meta;

export const Input: StoryObj<typeof meta> = {
args: {},
};

export const Textarea: StoryObj<typeof meta> = {
args: {
textarea: true,
} as any,
};
Loading

0 comments on commit ca34dba

Please sign in to comment.