Requested Changes for Editor Knossos Implementation (#129)
* placeholder * max length & placeholder * Accept editor comment conflict * integrate requested features * null check for ref * Change prompt for image upload * change filter for proper input blocking * Add spoiler button * change url of helper link * shallow resource link style * resource link inherit site style * detach preview styling from markdown-body style * remove sizing dependance on global styles * Bump 0.6.5
This commit is contained in:
parent
544111846c
commit
39a4297168
@ -21,3 +21,25 @@
|
||||
<UploadIcon />
|
||||
</FileInput>
|
||||
```
|
||||
|
||||
## Long Style
|
||||
|
||||
<DemoContainer>
|
||||
<FileInput
|
||||
:max-size="262144"
|
||||
accept="image/png,image/jpeg,image/gif,image/webp"
|
||||
long-style
|
||||
class="btn"
|
||||
prompt="Upload icon"
|
||||
/>
|
||||
</DemoContainer>
|
||||
|
||||
```vue
|
||||
<FileInput
|
||||
:max-size="262144"
|
||||
accept="image/png,image/jpeg,image/gif,image/webp"
|
||||
long-style
|
||||
class="btn"
|
||||
prompt="Upload icon"
|
||||
/>
|
||||
```
|
||||
@ -30,7 +30,7 @@ const description = ref(null)
|
||||
|
||||
## With options
|
||||
<DemoContainer>
|
||||
<MarkdownEditor v-model="description1" placeholder="Enter a description" max-length="30" />
|
||||
<MarkdownEditor v-model="description1" placeholder="Enter a description" max-length="800" max-height="400" />
|
||||
</DemoContainer>
|
||||
|
||||
```vue
|
||||
@ -39,7 +39,7 @@ import { ref } from "vue";
|
||||
const description = ref(null)
|
||||
</script>
|
||||
|
||||
<MarkdownEditor v-model="description" placeholder="Enter a description" max-length="30" />
|
||||
<MarkdownEditor v-model="description" placeholder="Enter a description" max-length="800" max-height="400" />
|
||||
```
|
||||
|
||||
## With image upload
|
||||
|
||||
1
lib/assets/icons/scan-eye.svg
Normal file
1
lib/assets/icons/scan-eye.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-scan-eye"><path d="M3 7V5a2 2 0 0 1 2-2h2"/><path d="M17 3h2a2 2 0 0 1 2 2v2"/><path d="M21 17v2a2 2 0 0 1-2 2h-2"/><path d="M7 21H5a2 2 0 0 1-2-2v-2"/><circle cx="12" cy="12" r="1"/><path d="M5 12s2.5-5 7-5 7 5 7 5-2.5 5-7 5-7-5-7-5"/></svg>
|
||||
|
After Width: | Height: | Size: 444 B |
@ -883,7 +883,7 @@ a,
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
color: var(--color-blue);
|
||||
color: var(--color-link);
|
||||
|
||||
&:focus-visible,
|
||||
&:hover {
|
||||
|
||||
@ -191,6 +191,7 @@ const isChildOfDropdown = (element) => {
|
||||
<style lang="scss" scoped>
|
||||
.animated-dropdown {
|
||||
width: 20rem;
|
||||
min-height: 40px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
@ -199,6 +200,9 @@ const isChildOfDropdown = (element) => {
|
||||
}
|
||||
|
||||
.selected {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
@ -100,7 +100,7 @@ label {
|
||||
border-radius: var(--radius-sm);
|
||||
border: dashed 0.3rem var(--color-contrast);
|
||||
cursor: pointer;
|
||||
color: var(--color-accent-contrast);
|
||||
color: var(--color-contrast);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -37,11 +37,13 @@
|
||||
<span class="label__title">Preview</span>
|
||||
<span class="label__description"></span>
|
||||
</span>
|
||||
<div
|
||||
style="width: 100%"
|
||||
class="markdown-body"
|
||||
v-html="renderHighlightedString(linkMarkdown)"
|
||||
/>
|
||||
<div class="markdown-body-wrapper">
|
||||
<div
|
||||
style="width: 100%"
|
||||
class="markdown-body"
|
||||
v-html="renderHighlightedString(linkMarkdown)"
|
||||
/>
|
||||
</div>
|
||||
<div class="input-group push-right">
|
||||
<Button :action="() => linkModal?.hide()"><XIcon /> Cancel</Button>
|
||||
<Button
|
||||
@ -86,13 +88,14 @@
|
||||
</div>
|
||||
<div
|
||||
v-if="props.onImageUpload && imageUploadOption === 'upload'"
|
||||
class="iconified-input btn-input-alternative"
|
||||
class="btn-input-alternative"
|
||||
>
|
||||
<FileInput
|
||||
accept="image/png,image/jpeg,image/gif,image/webp"
|
||||
prompt="Upload an image"
|
||||
class="btn"
|
||||
prompt="Drag and drop to upload or click to select file"
|
||||
long-style
|
||||
should-always-reset
|
||||
class="file-input"
|
||||
@change="handleImageUpload"
|
||||
>
|
||||
<UploadIcon />
|
||||
@ -121,11 +124,13 @@
|
||||
<span class="label__title">Preview</span>
|
||||
<span class="label__description"></span>
|
||||
</span>
|
||||
<div
|
||||
style="width: 100%"
|
||||
class="markdown-body"
|
||||
v-html="renderHighlightedString(imageMarkdown)"
|
||||
/>
|
||||
<div class="markdown-body-wrapper">
|
||||
<div
|
||||
style="width: 100%"
|
||||
class="markdown-body"
|
||||
v-html="renderHighlightedString(imageMarkdown)"
|
||||
/>
|
||||
</div>
|
||||
<div class="input-group push-right">
|
||||
<Button :action="() => imageModal?.hide()"><XIcon /> Cancel</Button>
|
||||
<Button
|
||||
@ -172,11 +177,14 @@
|
||||
<span class="label__title">Preview</span>
|
||||
<span class="label__description"></span>
|
||||
</span>
|
||||
<div
|
||||
style="width: 100%"
|
||||
class="markdown-body"
|
||||
v-html="renderHighlightedString(videoMarkdown)"
|
||||
/>
|
||||
|
||||
<div class="markdown-body-wrapper">
|
||||
<div
|
||||
style="width: 100%"
|
||||
class="markdown-body"
|
||||
v-html="renderHighlightedString(videoMarkdown)"
|
||||
/>
|
||||
</div>
|
||||
<div class="input-group push-right">
|
||||
<Button :action="() => videoModal?.hide()"><XIcon /> Cancel</Button>
|
||||
<Button
|
||||
@ -227,22 +235,30 @@
|
||||
<InfoIcon />
|
||||
<span
|
||||
>This editor supports
|
||||
<a class="link" href="https://docs.modrinth.com/docs/markdown" target="_blank"
|
||||
<a
|
||||
class="markdown-resource-link"
|
||||
href="https://docs.modrinth.com/markdown"
|
||||
target="_blank"
|
||||
>Markdown formatting</a
|
||||
>.</span
|
||||
>
|
||||
</div>
|
||||
<div :class="{ hide: !props.maxLength }" class="max-length-label">
|
||||
<span>Max length: </span>
|
||||
<span>{{ props.maxLength ?? 'Unlimited' }}</span>
|
||||
<span>
|
||||
{{ props.maxLength ? `${currentValue?.length || 0}/${props.maxLength}` : 'Unlimited' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="previewMode">
|
||||
<div class="markdown-body-wrapper">
|
||||
<div
|
||||
style="width: 100%"
|
||||
class="markdown-body"
|
||||
v-html="renderHighlightedString(currentValue ?? '')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="previewMode"
|
||||
style="width: 100%"
|
||||
class="markdown-body"
|
||||
v-html="renderHighlightedString(currentValue ?? '')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -260,6 +276,7 @@ import {
|
||||
Heading3Icon,
|
||||
BoldIcon,
|
||||
ItalicIcon,
|
||||
ScanEyeIcon,
|
||||
StrikethroughIcon,
|
||||
CodeIcon,
|
||||
ListBulletedIcon,
|
||||
@ -294,14 +311,16 @@ const props = withDefaults(
|
||||
onImageUpload?: (file: File) => Promise<string>
|
||||
placeholder?: string
|
||||
maxLength?: number
|
||||
maxHeight?: number
|
||||
}>(),
|
||||
{
|
||||
modelValue: '',
|
||||
disabled: false,
|
||||
headingButtons: true,
|
||||
onImageUpload: undefined,
|
||||
placeholder: undefined,
|
||||
placeholder: 'Write something...',
|
||||
maxLength: undefined,
|
||||
maxHeight: undefined,
|
||||
}
|
||||
)
|
||||
|
||||
@ -320,13 +339,15 @@ onMounted(() => {
|
||||
|
||||
const theme = EditorView.theme({
|
||||
// in defualts.scss there's references to .cm-content and such to inherit global styles
|
||||
'.cm-content, .cm-gutter': {
|
||||
'.cm-content': {
|
||||
marginBlockEnd: '0.5rem',
|
||||
padding: '0.5rem',
|
||||
minHeight: '200px',
|
||||
caretColor: 'var(--color-contrast)',
|
||||
width: '100%',
|
||||
overflowX: 'scroll',
|
||||
maxHeight: props.maxHeight ? `${props.maxHeight}px` : 'unset',
|
||||
overflowY: 'scroll',
|
||||
},
|
||||
'.cm-scroller': {
|
||||
height: '100%',
|
||||
@ -359,19 +380,6 @@ onMounted(() => {
|
||||
return false
|
||||
}
|
||||
},
|
||||
beforeinput: (ev, view) => {
|
||||
if (props.maxLength && view.state.doc.length > props.maxLength) {
|
||||
ev.preventDefault()
|
||||
// Calculate how many characters to remove from the end
|
||||
const excessLength = view.state.doc.length - props.maxLength
|
||||
// Dispatch transaction to remove excess characters
|
||||
view.dispatch({
|
||||
changes: { from: view.state.doc.length - excessLength, to: view.state.doc.length },
|
||||
selection: { anchor: props.maxLength, head: props.maxLength }, // Place cursor at the end
|
||||
})
|
||||
return true
|
||||
}
|
||||
},
|
||||
blur: (_, view) => {
|
||||
if (props.maxLength && view.state.doc.length > props.maxLength) {
|
||||
// Calculate how many characters to remove from the end
|
||||
@ -385,6 +393,13 @@ onMounted(() => {
|
||||
},
|
||||
})
|
||||
|
||||
const inputFilter = EditorState.changeFilter.of((transaction) => {
|
||||
if (props.maxLength && transaction.newDoc.length > props.maxLength) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
const editorState = EditorState.create({
|
||||
doc: props.modelValue,
|
||||
extensions: [
|
||||
@ -399,6 +414,7 @@ onMounted(() => {
|
||||
}),
|
||||
keymap.of(historyKeymap),
|
||||
cm_placeholder(props.placeholder || ''),
|
||||
inputFilter,
|
||||
],
|
||||
})
|
||||
|
||||
@ -468,6 +484,7 @@ const BUTTONS: ButtonGroupMap = {
|
||||
markdownCommands.toggleStrikethrough
|
||||
),
|
||||
composeCommandButton('Code', CodeIcon, markdownCommands.toggleCodeBlock),
|
||||
composeCommandButton('Spoiler', ScanEyeIcon, markdownCommands.toggleSpoiler),
|
||||
],
|
||||
},
|
||||
lists: {
|
||||
@ -627,6 +644,38 @@ function openVideoModal() {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.file-input {
|
||||
width: 100%;
|
||||
padding: 1.5rem;
|
||||
padding-left: 2.5rem;
|
||||
background: var(--color-button-bg);
|
||||
border: 2px dashed var(--color-gray);
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
transition: opacity 0.5s ease-in-out, filter 0.2s ease-in-out, scale 0.05s ease-in-out,
|
||||
outline 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
filter: brightness(0.85);
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-resource-link {
|
||||
cursor: pointer;
|
||||
color: var(--color-link);
|
||||
|
||||
&:focus-visible,
|
||||
&:hover {
|
||||
filter: brightness(1.2);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
filter: brightness(1.1);
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.display-options {
|
||||
margin-bottom: var(--gap-sm);
|
||||
}
|
||||
@ -698,9 +747,10 @@ function openVideoModal() {
|
||||
gap: var(--gap-xs);
|
||||
}
|
||||
|
||||
.markdown-body {
|
||||
.markdown-body-wrapper {
|
||||
border: 1px solid var(--color-button-bg);
|
||||
border-radius: var(--radius-md);
|
||||
width: 100%;
|
||||
padding: var(--radius-md);
|
||||
min-height: 6rem;
|
||||
}
|
||||
|
||||
@ -152,6 +152,7 @@ export { default as ReportIcon } from '@/assets/icons/report.svg?component'
|
||||
export { default as RightArrowIcon } from '@/assets/icons/right-arrow.svg?component'
|
||||
export { default as SaveIcon } from '@/assets/icons/save.svg?component'
|
||||
export { default as ScaleIcon } from '@/assets/icons/scale.svg?component'
|
||||
export { default as ScanEyeIcon } from '@/assets/icons/scan-eye.svg?component'
|
||||
export { default as SearchIcon } from '@/assets/icons/search.svg?component'
|
||||
export { default as SendIcon } from '@/assets/icons/send.svg?component'
|
||||
export { default as ServerIcon } from '@/assets/icons/server.svg?component'
|
||||
|
||||
@ -22,6 +22,10 @@ const toggleCodeBlock: Command = ({ state, dispatch }) => {
|
||||
return toggleAround(state, dispatch, codeBlockMark, codeBlockMark)
|
||||
}
|
||||
|
||||
const toggleSpoiler: Command = ({ state, dispatch }) => {
|
||||
return toggleAround(state, dispatch, '||', '||')
|
||||
}
|
||||
|
||||
const toggleHeader: Command = ({ state, dispatch }) => {
|
||||
return toggleLineStart(state, dispatch, '# ')
|
||||
}
|
||||
@ -342,6 +346,7 @@ const commands = {
|
||||
toggleItalic,
|
||||
toggleStrikethrough,
|
||||
toggleCodeBlock,
|
||||
toggleSpoiler,
|
||||
toggleHeader,
|
||||
toggleHeader2,
|
||||
toggleHeader3,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "omorphia",
|
||||
"type": "module",
|
||||
"version": "0.6.4",
|
||||
"version": "0.6.5",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user