Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions resources/css/content.css
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,14 @@
@apply rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm;
}
> :where(pre) {
@apply mb-[1.25em] !whitespace-pre overflow-x-auto;
@apply relative grid grid-cols-1 grid-rows-1 mb-[1.25em] !whitespace-pre overflow-x-auto;
&[data-language]::after {
@apply row-start-1 col-start-1 sticky left-0 p-2 content-[attr(data-language)] pointer-events-none text-muted-foreground text-xs;
}
> :where(code) {
@apply block py-[.625em] px-[1.125em] bg-muted rounded-md [font-size:inherit];
@apply block row-start-1 col-start-1 min-w-max in-data-language:pt-[calc(1em+1.25rem)] py-[1em] px-[1.25em] bg-muted rounded-md [font-size:inherit]
transition-colors in-data-has-dropdown-open:in-data-textselected:outline-primary/50 outline outline-transparent -outline-offset-3
;
}
}
> :where(hr) {
Expand Down
4 changes: 2 additions & 2 deletions resources/js/components/ui/dropdown-menu/DropdownMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const forwarded = useForwardPropsEmits(props, emits)
</script>

<template>
<DropdownMenuRoot v-bind="forwarded">
<slot />
<DropdownMenuRoot v-bind="forwarded" v-slot="{ open }">
<slot :open="open" />
</DropdownMenuRoot>
</template>
15 changes: 15 additions & 0 deletions resources/js/components/ui/popover/PopoverAnchor.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script setup lang="ts">
import type { PopoverAnchorProps } from "reka-ui"
import { PopoverAnchor } from "reka-ui"

const props = defineProps<PopoverAnchorProps>()
</script>

<template>
<PopoverAnchor
data-slot="popover-anchor"
v-bind="props"
>
<slot />
</PopoverAnchor>
</template>
1 change: 1 addition & 0 deletions resources/js/components/ui/popover/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { default as Popover } from './Popover.vue'
export { default as PopoverAnchor } from './PopoverAnchor.vue'
export { default as PopoverTrigger } from './PopoverTrigger.vue'
export { default as PopoverContent } from './PopoverContent.vue'
2 changes: 1 addition & 1 deletion resources/js/components/ui/toggle/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { type VariantProps, cva } from 'class-variance-authority'
export { default as Toggle } from './Toggle.vue'

export const toggleVariants = cva(
'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground',
'inline-flex gap-2 items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground aria-pressed:bg-accent aria-pressed:text-accent-foreground',
{
variants: {
variant: {
Expand Down
6 changes: 6 additions & 0 deletions resources/js/form/components/fields/editor/Editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
import {
getTextInputReplacementsExtension
} from "@/form/components/fields/editor/extensions/TextInputReplacements";
import CodeBlockDropdown from "@/form/components/fields/editor/toolbar/CodeBlockDropdown.vue";

const emit = defineEmits<FormFieldEmits<FormEditorFieldData>>();
const props = defineProps<FormFieldProps<FormEditorFieldData>>();
Expand All @@ -77,6 +78,7 @@
const el = useTemplateRef<HTMLDialogElement | HTMLDivElement>('el');
const embedModal = ref<InstanceType<typeof EditorEmbedModal>>();
const linkDropdown = ref<InstanceType<typeof LinkDropdown>>();
const codeBlockDropdown = ref<InstanceType<typeof CodeBlockDropdown>>();

provide<ParentEditor>('editor', {
props,
Expand Down Expand Up @@ -254,6 +256,7 @@
? 'border-none rounded-none bg-transparent'
: 'focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 focus-within:ring-offset-background'
)"
:data-has-dropdown-open="codeBlockDropdown?.open ? true : null"
:data-fullscreen="isFullscreen ? true : null"
ref="el"
>
Expand Down Expand Up @@ -281,6 +284,9 @@
<template v-else-if="button === 'table'">
<TableDropdown v-bind="props" :editor="editor" />
</template>
<template v-else-if="button === 'code-block'">
<CodeBlockDropdown v-bind="props" :editor="editor" :ref="(c) => codeBlockDropdown = c as InstanceType<typeof CodeBlockDropdown>" />
</template>
<template v-else-if="button.startsWith('embed:')">
<Toggle
size="sm"
Expand Down
32 changes: 32 additions & 0 deletions resources/js/form/components/fields/editor/extensions/CodeBlock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { CodeBlock as BaseCodeBlock } from '@tiptap/extension-code-block';
import { Plugin } from "@tiptap/pm/state";
import { PluginKey } from "prosemirror-state";
import { Decoration, DecorationSet } from "prosemirror-view";

export const CodeBlock = BaseCodeBlock.extend({
addProseMirrorPlugins() {
return [
...this.parent(),
new Plugin({
key: new PluginKey('codeBlockLanguageDecoration'),
props: {
decorations(state) {
const decos: Decoration[] = [];

state.doc.descendants((node, pos) => {
if (node.type.name === CodeBlock.name && node.attrs.language) {
decos.push(
Decoration.node(pos, pos + node.nodeSize, {
'data-language': node.attrs.language
})
);
}
});

return DecorationSet.create(state.doc, decos);
}
},
})
]
}
})
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { TableRow } from '@tiptap/extension-table-row';
import { TableHeader } from '@tiptap/extension-table-header';
import { TableCell } from '@tiptap/extension-table-cell';
import { Highlight } from '@tiptap/extension-highlight';
import { CodeBlock } from '@tiptap/extension-code-block';
import { Superscript } from '@tiptap/extension-superscript';
import { OrderedList } from '@tiptap/extension-ordered-list';
import { Link } from '@tiptap/extension-link';
Expand All @@ -28,7 +27,7 @@ import { Iframe } from './iframe/Iframe';
import { Clipboard } from './Clipboard';
import { Small } from './Small';
import { FormEditorFieldData, FormEditorToolbarButton } from "@/types";

import { CodeBlock } from "@/form/components/fields/editor/extensions/CodeBlock";

export function getExtensions(field: FormEditorFieldData) {
const toolbarHas = (buttonName: FormEditorToolbarButton | FormEditorToolbarButton[]) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<script setup lang="ts">
import { __ } from "@/utils/i18n";
import { Button } from '@/components/ui/button';
import { Editor, getMarkRange, getMarkType } from "@tiptap/vue-3";
import { ref } from "vue";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Toggle } from "@/components/ui/toggle";
import { FileCode, ChevronDown } from "lucide-vue-next";
import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { useId } from "@/composables/useId";
import { FormFieldProps } from "@/form/types";
import { FormEditorFieldData } from "@/types";

const props = defineProps<FormFieldProps<FormEditorFieldData> & {
editor: Editor,
}>();

const open = ref(false);
const language = ref<string | null>(null);

props.editor.on('transaction', () => {
language.value = props.editor.getAttributes('codeBlock')?.language;
});

function onOpen() {
}

function onHide() {
props.editor.chain()
.focus()
.run();
}

function onSubmit() {
props.editor.chain().focus().updateAttributes('codeBlock', { language: language.value?.toLowerCase() }).run();
}

function onToggleClick() {
if(props.editor.isActive('codeBlock')) {
open.value = true;
} else {
props.editor.chain().focus().toggleCodeBlock().run();
}
}

function onUntoggleCodeBlockClick() {
if(props.editor.isActive('codeBlock')) {
props.editor.chain().focus().toggleCodeBlock().run();
} else {
props.editor.chain().focus().run();
}
}

defineExpose({
open,
});
</script>

<template>
<Popover
v-model:open="open"
@update:open="$event ? onOpen() : onHide()"
:modal="false"
>
<PopoverAnchor as-child>
<Toggle
:model-value="open || props.editor.isActive('codeBlock')"
size="sm"
:disabled="props.field.readOnly"
:title="__('sharp::form.editor.toolbar.code_block.title')"
@click="onToggleClick"
>
<FileCode class="size-4" />
</Toggle>
</PopoverAnchor>

<PopoverContent class="w-max">
<form @submit.prevent="onSubmit()">
<div class="flex gap-3 ">
<Input class="flex-1 min-w-0 w-24" v-model="language" :placeholder="__('sharp::form.editor.dialogs.code_block.language_label')" />
<Button type="submit">{{ __('sharp::modals.ok_button') }}</Button>
</div>
</form>
<Button class="mt-3 w-full" variant="outline" size="sm" @click="onUntoggleCodeBlockClick">
{{ __('sharp::form.editor.dialogs.code_block.toggle_off') }}
</Button>
</PopoverContent>
</Popover>
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
}

defineExpose({
open: () => { open.value = true; onOpen() },
open,
});
</script>

Expand All @@ -91,8 +91,7 @@
>
<PopoverTrigger as-child>
<Toggle
class="data-[state=open]:bg-accent"
:class="props.editor.isActive('link') ? 'bg-accent' : ''"
:model-value="open || props.editor.isActive('link')"
size="sm"
:disabled="props.field.readOnly"
:title="__('sharp::form.editor.toolbar.link.title')"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@
</script>

<template>
<DropdownMenu :modal="false">
<DropdownMenu :modal="false" v-slot="{ open }">
<DropdownMenuTrigger as-child>
<Toggle
class="data-[state=open]:bg-accent"
:class="props.editor.isActive('table') ? 'bg-accent' : ''"
:model-value="open || props.editor.isActive('table')"
size="sm"
:disabled="props.field.readOnly"
:title="__('sharp::form.editor.toolbar.table.title')"
Expand Down
2 changes: 2 additions & 0 deletions resources/lang/en/form.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@
'editor.dialogs.iframe.insert_title' => 'Insert Iframe (embed)',
'editor.dialogs.iframe.update_title' => 'Update Iframe (embed)',
'editor.dialogs.iframe.invalid_message' => 'Iframe code is invalid',
'editor.dialogs.code_block.language_label' => 'Language',
'editor.dialogs.code_block.toggle_off' => 'Convert to paragraph',

'editor.dialogs.embed.submit_button_insert' => 'Insert',
'editor.dialogs.embed.submit_button_update' => 'Update',
Expand Down
3 changes: 3 additions & 0 deletions resources/lang/fr/form.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@
'editor.dialogs.iframe.update_title' => 'Modifier l’Iframe (video, audio...)',
'editor.dialogs.iframe.invalid_message' => 'Le code de l’Iframe est invalide',

'editor.dialogs.code_block.language_label' => 'Langage',
'editor.dialogs.code_block.toggle_off' => 'Convertir en paragraphe',

'editor.dialogs.embed.submit_button_insert' => 'Insérer',
'editor.dialogs.embed.submit_button_update' => 'Mettre à jour',

Expand Down
Loading