Skip to content

Commit

Permalink
feat(settings): setup caret style and animation store
Browse files Browse the repository at this point in the history
TODO: functionalities of caret style and animation
  • Loading branch information
Muhammed-Rahif committed Dec 27, 2024
1 parent e134eab commit 802e953
Show file tree
Hide file tree
Showing 17 changed files with 276 additions and 78 deletions.
Binary file added public/click.wav
Binary file not shown.
2 changes: 1 addition & 1 deletion src/lib/components/EditorTitle.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import * as Tooltip from '@/components/ui/tooltip';
import CloseIcon from '@/components/icons/Close.svelte';
import Button from '@/components/ui/button/button.svelte';
import type { EditorType } from '@/types/EditorTypes';
import type { EditorType } from '@/src/lib/types/EditorType';
import { cn } from '@/utils';
import { resizeInputOnDynamicContent } from '@/actions/input-auto-width';
Expand Down
93 changes: 35 additions & 58 deletions src/lib/components/editor/Editor.svelte
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
<script lang="ts">
import { onMount, tick } from 'svelte';
import throttle from 'lodash.throttle';
import debounce from 'lodash.debounce';
import StatusBar from '@/components/StatusBar.svelte';
import Quill from 'quill';
import { position as getCaretPosition } from 'caret-pos';
import { Notpad } from '@/helpers/notpad';
import { settings } from '@/store/store';
import FakeCaret from './FakeCaret.svelte';
import type { EditorType } from '@/src/lib/types/EditorType';
import 'quill/dist/quill.core.css';
import type { EditorType } from '@/types/EditorTypes';
import { RichTextEditor } from '@/helpers/rich-text-editor';
import './Editor.css';
interface Props {
Expand All @@ -18,23 +16,43 @@
let { editor }: Props = $props();
let editorContainer: HTMLDivElement = $state(null!);
let quill: Quill | undefined = $state();
let fakeCaret: HTMLSpanElement | null = $state(null);
let caretPosition = $state({ top: 10, left: 8, height: 24 });
let lineNo = $state(0);
let caretLineNo = $state(1);
let caretColumnNo = $state(1);
let characterCount = $state(0);
let wordCount = $state(0);
let quill = $derived(editor.quill!);
let fakeCaret: FakeCaret = $state(null!);
async function setupQuill() {
const richTextEditor = new RichTextEditor({
editorContainer
});
quill = richTextEditor.quill;
await Notpad.editors.setQuill(editor.id, quill);
quill.setContents(Notpad.editors.getContent(editor.id)!);
const options = {
formats: [
'bold',
'code',
'italic',
'link',
'size',
'strike',
'script',
'underline',
'blockquote',
'header',
'indent',
'list',
'align',
'direction',
'code-block',
'formula'
// 'background',
// 'font',
// 'image',
// 'video'
]
};
editor.quill = new Quill(editorContainer, options);
await Notpad.editors.setQuill(editor.id, editor.quill);
editor.quill.setContents(Notpad.editors.getContent(editor.id)!);
}
/**
Expand All @@ -58,39 +76,7 @@
.trim()
.split(/\s+/)
.filter((word) => word).length; // \s+ is RegEx for whitespace characters.
}, 300);
const resumeFakeCaretBlink = debounce(function () {
if (fakeCaret) fakeCaret.classList.add('animate-caret-blink');
}, 1000);
// Using requestAnimationFrame for smooth updates
const updateCaretPosition = throttle(() => {
if (!quill) return;
// console.count('Update caret position');
requestAnimationFrame(async () => {
if (fakeCaret) fakeCaret.classList.remove('animate-caret-blink');
if (!quill) return;
if (editorContainer) {
try {
const caret = getCaretPosition(quill.root);
// Adjust for the scroll position
caretPosition = {
top: caret.top - editorContainer.scrollTop,
left: caret.left - editorContainer.scrollLeft,
height: caret.height
};
} catch (error) {
console.error(error);
}
}
resumeFakeCaretBlink();
});
});
}, 150);
$effect(() => {
if ($settings) {
Expand All @@ -108,7 +94,7 @@
const updateCaretPosAndEditorData = async () => {
updateEditorData();
await tick();
updateCaretPosition();
fakeCaret.updateCaretPosition();
};
onMount(async () => {
Expand Down Expand Up @@ -141,16 +127,7 @@
bind:this={editorContainer}
></div>

<span
class="fake-caret absolute z-0 w-0.5 animate-caret-blink
rounded-[.06em] bg-primary"
bind:this={fakeCaret}
style="top: {caretPosition.top}px;
left: {caretPosition.left}px;
height: {caretPosition.height}px;
width: {$settings.zoom * 2}px"
spellcheck="false"
></span>
<FakeCaret {quill} {editorContainer} bind:this={fakeCaret} />
</div>

<StatusBar {caretLineNo} {caretColumnNo} {characterCount} {wordCount} />
58 changes: 58 additions & 0 deletions src/lib/components/editor/FakeCaret.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<script lang="ts">
import throttle from 'lodash.throttle';
import debounce from 'lodash.debounce';
import { position as getCaretPosition } from 'caret-pos';
import { settings } from '@/store/store';
import type Quill from 'quill';
interface Props {
quill: Quill;
editorContainer: HTMLElement;
}
let { quill, editorContainer }: Props = $props();
let caret = $state({ top: 10, left: 8, height: 24 });
let fakeCaretElement: HTMLSpanElement = $state(null!);
const resumeFakeCaretBlink = debounce(function () {
if (fakeCaretElement) fakeCaretElement.classList.add('animate-caret-blink');
}, 1000);
export const updateCaretPosition = throttle(() => {
if (!quill) return;
// console.count('Update caret position');
requestAnimationFrame(async () => {
if (fakeCaretElement) fakeCaretElement.classList.remove('animate-caret-blink');
if (!quill) return;
if (editorContainer) {
try {
const { top, left, height } = getCaretPosition(quill.root);
// Adjust for the scroll position
caret = {
top: top - editorContainer.scrollTop,
left: left - editorContainer.scrollLeft,
height: height
};
} catch (error) {
console.error(error);
}
}
resumeFakeCaretBlink();
});
});
</script>

<span
class="fake-caret absolute z-0 w-0.5 animate-caret-blink
rounded-[.06em] bg-primary"
bind:this={fakeCaretElement}
style="top: {caret.top}px;
left: {caret.left}px;
height: {caret.height}px;
width: {$settings.zoom * 2}px"
spellcheck="false"
></span>
2 changes: 1 addition & 1 deletion src/lib/components/font-dialog/FontDialog.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import FontSizeCombobox from './FontSizeCombobox.svelte';
import { Label } from '@/components/ui/label';
import * as Card from '@/components/ui/card';
import { FontFamily, FontSize } from '@/types/SettingsTypes';
import { FontFamily, FontSize } from '@/src/lib/types/SettingsType';
import { get } from 'svelte/store';
const currentSettings = get(settings);
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/font-dialog/FontFamilyCombobox.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { get } from 'svelte/store';
import CheckIcon from '@/components/icons/Check.svelte';
import ChevronsUpDownIcon from '@/components/icons/ChevronsUpDown.svelte';
import { FontFamily } from '@/types/SettingsTypes';
import { FontFamily } from '@/src/lib/types/SettingsType';
interface Props {
value: FontFamily;
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/font-dialog/FontSizeCombobox.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { cn } from '@/utils';
import { settings } from '@/store/store';
import { get } from 'svelte/store';
import { FontSize } from '@/types/SettingsTypes';
import { FontSize } from '@/src/lib/types/SettingsType';
interface Props {
value: FontSize;
Expand Down
70 changes: 60 additions & 10 deletions src/lib/components/menubar/SettingsMenu.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import GithubOultineIcon from '@/components/icons/GithubOultine.svelte';
import ALargeSmallIcon from '@/components/icons/ALargeSmall.svelte';
import { Notpad } from '@/helpers/notpad';
import { CaretAnimation, CaretStyle } from '@/types/SettingsType';
import { settings } from '@/store/store';
</script>

<Menubar.Menu>
Expand All @@ -30,21 +32,69 @@
<Menubar.Sub>
<Menubar.SubTrigger>Sound</Menubar.SubTrigger>
<Menubar.SubContent>
<Menubar.CheckboxItem>None</Menubar.CheckboxItem>
<Menubar.CheckboxItem>Click</Menubar.CheckboxItem>
<Menubar.CheckboxItem>Pop</Menubar.CheckboxItem>
<Menubar.CheckboxItem>Typewriter</Menubar.CheckboxItem>
<Menubar.Sub>
<Menubar.SubTrigger class="gap-2">Effect</Menubar.SubTrigger>
<Menubar.SubContent>
<Menubar.CheckboxItem>None</Menubar.CheckboxItem>
<Menubar.CheckboxItem>Click</Menubar.CheckboxItem>
<Menubar.CheckboxItem>Pop</Menubar.CheckboxItem>
<Menubar.CheckboxItem>Typewriter</Menubar.CheckboxItem>
</Menubar.SubContent>
</Menubar.Sub>

<Menubar.Sub>
<Menubar.SubTrigger class="gap-2">Volume</Menubar.SubTrigger>
<Menubar.SubContent>
<Menubar.CheckboxItem>100%</Menubar.CheckboxItem>
<Menubar.CheckboxItem>75%</Menubar.CheckboxItem>
<Menubar.CheckboxItem>50%</Menubar.CheckboxItem>
<Menubar.CheckboxItem>25%</Menubar.CheckboxItem>
</Menubar.SubContent>
</Menubar.Sub>

<Menubar.Sub>
<Menubar.SubTrigger class="gap-2">Vibration</Menubar.SubTrigger>
<Menubar.SubContent>
<Menubar.CheckboxItem>None</Menubar.CheckboxItem>
<Menubar.CheckboxItem>Low</Menubar.CheckboxItem>
<Menubar.CheckboxItem>Medium</Menubar.CheckboxItem>
<Menubar.CheckboxItem>High</Menubar.CheckboxItem>
<Menubar.CheckboxItem>Strong</Menubar.CheckboxItem>
</Menubar.SubContent>
</Menubar.Sub>
</Menubar.SubContent>
</Menubar.Sub>

<Menubar.Sub>
<Menubar.SubTrigger>Vibration</Menubar.SubTrigger>
<Menubar.SubTrigger>Caret</Menubar.SubTrigger>
<Menubar.SubContent>
<Menubar.CheckboxItem>None</Menubar.CheckboxItem>
<Menubar.CheckboxItem>Low</Menubar.CheckboxItem>
<Menubar.CheckboxItem>Medium</Menubar.CheckboxItem>
<Menubar.CheckboxItem>High</Menubar.CheckboxItem>
<Menubar.CheckboxItem>Strong</Menubar.CheckboxItem>
<Menubar.Sub>
<Menubar.SubTrigger class="gap-2">Style</Menubar.SubTrigger>
<Menubar.SubContent>
{#each Object.values(CaretStyle) as caretStyle}
<Menubar.CheckboxItem
checked={$settings.caret.style == caretStyle}
onclick={() => Notpad.settings.updateCaret({ style: caretStyle })}
>
{caretStyle}
</Menubar.CheckboxItem>
{/each}
</Menubar.SubContent>
</Menubar.Sub>

<Menubar.Sub>
<Menubar.SubTrigger class="gap-2">Animation</Menubar.SubTrigger>
<Menubar.SubContent>
{#each Object.values(CaretAnimation) as caretAnimation}
<Menubar.CheckboxItem
checked={$settings.caret.animation == caretAnimation}
onclick={() => Notpad.settings.updateCaret({ animation: caretAnimation })}
>
{caretAnimation}
</Menubar.CheckboxItem>
{/each}
</Menubar.SubContent>
</Menubar.Sub>
</Menubar.SubContent>
</Menubar.Sub>

Expand Down
2 changes: 1 addition & 1 deletion src/lib/helpers/github-api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ContributorType, ReleasesType } from '@/types/GithubApiTypes';
import type { ContributorType, ReleasesType } from '@/src/lib/types/GithubApiType';

export class GithubApi {
public async getAppLicense(): Promise<string | undefined> {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/helpers/menubar/view-options.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { get } from 'svelte/store';
import { settings } from '@/store/store';
import { Notpad } from '@/helpers/notpad';
import type { SettingsType } from '@/types/SettingsTypes';
import type { SettingsType } from '@/src/lib/types/SettingsType';

export class ViewOptions {
public toggleStatusBar() {
Expand Down
21 changes: 19 additions & 2 deletions src/lib/helpers/settings.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { settings } from '@/store/store';
import { FontFamily, FontSize, type SettingsType } from '@/types/SettingsTypes';
import {
CaretAnimation,
CaretStyle,
FontFamily,
FontSize,
type SettingsType
} from '@/types/SettingsType';

export class Settings {
static defaultSettings: SettingsType = {
Expand All @@ -8,7 +14,11 @@ export class Settings {
fontFamily: FontFamily.SUSE,
fontSize: FontSize.Size16,
lineNumbers: false,
wrapLines: true
wrapLines: true,
caret: {
animation: CaretAnimation.Medium,
style: CaretStyle.VerticalBar
}
};

setFontFamily(fontFamily: FontFamily) {
Expand All @@ -18,4 +28,11 @@ export class Settings {
setFontSize(fontSize: FontSize) {
settings.update((value) => ({ ...value, fontSize }));
}

updateCaret(caret: Partial<SettingsType['caret']>) {
settings.update((value) => ({
...value,
caret: { ...value.caret, ...caret }
}));
}
}
Loading

0 comments on commit 802e953

Please sign in to comment.