Skip to content

Commit 6fed7ce

Browse files
authored
feat: Settings modal and Raw Mode (#32)
* boilerplate code for settings * chore: pkg cleanup * fix: polish ui and syncc * chore: compat and minor transitions * fix: initialization disparity * chore: better checkbox * fix: stupidity
1 parent 0c1d4f0 commit 6fed7ce

File tree

8 files changed

+408
-88
lines changed

8 files changed

+408
-88
lines changed

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@
3636
},
3737
"resolutions": {
3838
"trim": "1.0.1",
39-
"micromatch":">=4.0.8",
40-
"dompurify":">=2.5.0",
41-
"braces":">=3.0.3"
39+
"micromatch": ">=4.0.8",
40+
"dompurify": ">=2.5.0",
41+
"braces": ">=3.0.3"
4242
}
4343
}

pnpm-lock.yaml

Lines changed: 43 additions & 43 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/menu.vue

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
<template>
22
<div class="container">
3-
<Button @click="state.dropdownOpen = !state.dropdownOpen" class="ghost trigger">
3+
<Button
4+
v-click-outside="onClickOutside"
5+
@click="state.dropdownOpen = !state.dropdownOpen"
6+
class="ghost trigger"
7+
>
48
{{ triggerLabel }}
59
</Button>
610

7-
<div v-if="state.dropdownOpen" class="dropdown" @click="state.dropdownOpen = false"></div>
11+
<div
12+
v-if="state.dropdownOpen"
13+
class="dropdown"
14+
@click="state.dropdownOpen = false"
15+
></div>
816

917
<div v-if="state.dropdownOpen" class="dropdown-items">
1018
<slot></slot>
@@ -21,6 +29,25 @@ defineProps({
2129
triggerLabel: String,
2230
});
2331
32+
const onClickOutside = () => {
33+
state.dropdownOpen = false
34+
};
35+
36+
const vClickOutside = {
37+
mounted: (el, binding, vnode) => {
38+
el.clickOutsideEvent = function (event) {
39+
40+
if (!(el == event.target || el.contains(event.target))) {
41+
binding.value(event)
42+
}
43+
};
44+
document.addEventListener("click", el.clickOutsideEvent);
45+
},
46+
unmounted: (el) => {
47+
document.removeEventListener("click", el.clickOutsideEvent);
48+
},
49+
};
50+
2451
const state = reactive({ dropdownOpen: false });
2552
</script>
2653

src/components/settings-modal.vue

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
<template>
2+
<div class="modal">
3+
<div class="modal-container">
4+
<div class="modal-content">
5+
<div class="modal-header">
6+
<h3>Settings</h3>
7+
<Button @click="onClose">
8+
<svg
9+
xmlns="http://www.w3.org/2000/svg"
10+
width="16"
11+
height="16"
12+
viewBox="0 0 24 24"
13+
fill="none"
14+
stroke="currentColor"
15+
stroke-width="2"
16+
stroke-linecap="round"
17+
stroke-linejoin="round"
18+
class="icon icon-tabler icons-tabler-outline icon-tabler-x"
19+
>
20+
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
21+
<path d="M18 6l-12 12" />
22+
<path d="M6 6l12 12" />
23+
</svg>
24+
</Button>
25+
</div>
26+
<div class="modal-body">
27+
<label class="checkbox-container">
28+
<input
29+
v-model="settings.value.rawMode"
30+
type="checkbox"
31+
name="rawMode"
32+
/>
33+
<span class="checkbox">
34+
<span class="checkbox-overlay"></span>
35+
</span>
36+
<span class="checkbox-label">Raw Edit Mode </span>
37+
</label>
38+
</div>
39+
</div>
40+
</div>
41+
</div>
42+
</template>
43+
44+
<script setup>
45+
import { settings } from "../stores/settings";
46+
import Button from "./button.vue";
47+
48+
const props = defineProps(["onClose"]);
49+
</script>
50+
51+
<style>
52+
.modal {
53+
position: fixed;
54+
height: 100vh;
55+
width: 100vw;
56+
top: 0;
57+
left: 0;
58+
}
59+
60+
.modal-container {
61+
height: 100%;
62+
width: 100%;
63+
display: flex;
64+
justify-content: center;
65+
align-items: center;
66+
background: rgba(0, 0, 0, 0.5);
67+
backdrop-filter: blur(4px);
68+
}
69+
70+
.modal-content {
71+
background-color: var(--overlay);
72+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
73+
padding: 20px;
74+
border-radius: 12px;
75+
min-height: 50%;
76+
min-width: 50%;
77+
display: flex;
78+
flex-direction: column;
79+
transition: transform 0.3s ease, opacity 0.3s ease;
80+
}
81+
82+
.modal-content .modal-header {
83+
height: auto;
84+
padding: 0 10px;
85+
padding-bottom: 10px;
86+
border-bottom: 1px solid var(--surface);
87+
display: flex;
88+
flex-direction: row;
89+
justify-content: space-between;
90+
align-items: center;
91+
}
92+
93+
.modal-content .modal-header h3 {
94+
margin: 0;
95+
font-weight: 600;
96+
color: var(--text-primary);
97+
}
98+
99+
.modal-content .modal-body {
100+
padding: 15px 10px 0 10px;
101+
font-size: 14px;
102+
color: var(--text-secondary);
103+
}
104+
105+
.toggle-label {
106+
display: flex;
107+
align-items: center;
108+
gap: 8px;
109+
}
110+
111+
input[type="checkbox"] {
112+
width: 18px;
113+
height: 18px;
114+
cursor: pointer;
115+
}
116+
117+
.checkbox-container {
118+
display: inline-flex;
119+
position: relative;
120+
gap: 0.5rem;
121+
}
122+
123+
.checkbox-container input[type="checkbox"] {
124+
display: none;
125+
cursor: pointer;
126+
}
127+
128+
.checkbox-container input[type="checkbox"]:after {
129+
opacity: 1;
130+
}
131+
132+
.checkbox-container .checkbox {
133+
display: inline-flex;
134+
position: absolute;
135+
top: 0px;
136+
left: 0px;
137+
justify-content: center;
138+
align-items: center;
139+
width: 1.25rem;
140+
height: 1.25rem;
141+
border-radius: 0.375rem;
142+
border-width: 2px;
143+
background: overlay;
144+
border-color: var(--text);
145+
border-style: solid;
146+
}
147+
148+
.checkbox-container .checkbox-overlay {
149+
transition-property: all;
150+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
151+
transition-duration: 150ms;
152+
transition-duration: 300ms;
153+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
154+
display: inline-block;
155+
width: 0.625rem;
156+
height: 0.625rem;
157+
border-radius: 2px;
158+
opacity: 0;
159+
background: var(--text);
160+
}
161+
162+
.checkbox-container .checkbox-label {
163+
margin-left: 2rem;
164+
}
165+
166+
input[type="checkbox"]:checked ~ .checkbox > .checkbox-overlay{
167+
opacity:1
168+
}
169+
</style>

src/lib/quill/delta-md.js

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { marked } from "marked";
12
import { QuillDeltaToHtmlConverter } from "quill-delta-to-html";
3+
import Quill from "quill";
24
import TurndownService from "turndown";
35
const turndownService = new TurndownService({
46
codeBlockStyle: "fenced",
@@ -19,14 +21,26 @@ export const deltaToMarkdown = (delta) => {
1921
const html = new QuillDeltaToHtmlConverter(delta, {});
2022
html.beforeRender((groupType, data) => {
2123
if (groupType !== "block") return;
22-
if (!(data.op && "code-block" in data.op.attributes)) return;
23-
return `<pre data-language="${
24-
data.op.attributes["code-block"]
25-
}"><code>${data.ops
26-
.map((d) => {
27-
return d.insert.value;
28-
})
29-
.join("")}</code></pre>`;
24+
if (!(data.op && data.op.attributes && data.op.attributes["code-block"]))
25+
return;
26+
const codeContent = data.ops
27+
.map((d) =>
28+
typeof d.insert === "string"
29+
? d.insert
30+
: d.insert && d.insert.value
31+
? d.insert.value
32+
: ""
33+
)
34+
.join("");
35+
return `<pre data-language="${data.op.attributes["code-block"]}"><code>${codeContent}</code></pre>`;
3036
});
3137
return htmlToMarkdown(html.convert());
3238
};
39+
40+
export const markdownToDelta = (markdown) => {
41+
const html = marked(markdown);
42+
const container = document.createElement("div");
43+
container.innerHTML = html;
44+
const quillInstance = new Quill(container);
45+
return quillInstance.getContents().ops;
46+
};

src/lib/settings.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
const KEY = "mark:settings";
2+
3+
const settings = {};
4+
5+
export const loadSettings = () => {
6+
const _settings = localStorage.getItem(KEY) || "{}";
7+
try {
8+
Object.assign(settings, JSON.parse(_settings));
9+
} catch (err) {
10+
Object.assign(settings, {});
11+
}
12+
return settings;
13+
};
14+
15+
export const saveSettings = () => {
16+
localStorage.setItem(KEY, JSON.stringify(settings));
17+
};
18+
19+
export const updateSettings = (patch) => {
20+
Object.assign(settings, patch);
21+
saveSettings();
22+
};

0 commit comments

Comments
 (0)