first commit

This commit is contained in:
Beyhan Oğur
2026-04-26 21:33:39 +03:00
commit 4362c3b83f
1991 changed files with 285411 additions and 0 deletions

35
public/assets/quill/themes/base.d.ts vendored Normal file
View File

@@ -0,0 +1,35 @@
import type Quill from '../core/quill.js';
import Theme from '../core/theme.js';
import type { ThemeOptions } from '../core/theme.js';
import Picker from '../ui/picker.js';
import Tooltip from '../ui/tooltip.js';
import type { Range } from '../core/selection.js';
import type Clipboard from '../modules/clipboard.js';
import type History from '../modules/history.js';
import type Keyboard from '../modules/keyboard.js';
import type Uploader from '../modules/uploader.js';
import type Selection from '../core/selection.js';
declare class BaseTheme extends Theme {
pickers: Picker[];
tooltip?: Tooltip;
constructor(quill: Quill, options: ThemeOptions);
addModule(name: 'clipboard'): Clipboard;
addModule(name: 'keyboard'): Keyboard;
addModule(name: 'uploader'): Uploader;
addModule(name: 'history'): History;
addModule(name: 'selection'): Selection;
addModule(name: string): unknown;
buildButtons(buttons: NodeListOf<HTMLElement>, icons: Record<string, Record<string, string> | string>): void;
buildPickers(selects: NodeListOf<HTMLSelectElement>, icons: Record<string, string | Record<string, string>>): void;
}
declare class BaseTooltip extends Tooltip {
textbox: HTMLInputElement | null;
linkRange?: Range;
constructor(quill: Quill, boundsContainer?: HTMLElement);
listen(): void;
cancel(): void;
edit(mode?: string, preview?: string | null): void;
restoreFocus(): void;
save(): void;
}
export { BaseTooltip, BaseTheme as default };

View File

@@ -0,0 +1,257 @@
import { merge } from 'lodash-es';
import Emitter from '../core/emitter.js';
import Theme from '../core/theme.js';
import ColorPicker from '../ui/color-picker.js';
import IconPicker from '../ui/icon-picker.js';
import Picker from '../ui/picker.js';
import Tooltip from '../ui/tooltip.js';
const ALIGNS = [false, 'center', 'right', 'justify'];
const COLORS = ['#000000', '#e60000', '#ff9900', '#ffff00', '#008a00', '#0066cc', '#9933ff', '#ffffff', '#facccc', '#ffebcc', '#ffffcc', '#cce8cc', '#cce0f5', '#ebd6ff', '#bbbbbb', '#f06666', '#ffc266', '#ffff66', '#66b966', '#66a3e0', '#c285ff', '#888888', '#a10000', '#b26b00', '#b2b200', '#006100', '#0047b2', '#6b24b2', '#444444', '#5c0000', '#663d00', '#666600', '#003700', '#002966', '#3d1466'];
const FONTS = [false, 'serif', 'monospace'];
const HEADERS = ['1', '2', '3', false];
const SIZES = ['small', false, 'large', 'huge'];
class BaseTheme extends Theme {
constructor(quill, options) {
super(quill, options);
const listener = e => {
if (!document.body.contains(quill.root)) {
document.body.removeEventListener('click', listener);
return;
}
if (this.tooltip != null &&
// @ts-expect-error
!this.tooltip.root.contains(e.target) &&
// @ts-expect-error
document.activeElement !== this.tooltip.textbox && !this.quill.hasFocus()) {
this.tooltip.hide();
}
if (this.pickers != null) {
this.pickers.forEach(picker => {
// @ts-expect-error
if (!picker.container.contains(e.target)) {
picker.close();
}
});
}
};
quill.emitter.listenDOM('click', document.body, listener);
}
addModule(name) {
const module = super.addModule(name);
if (name === 'toolbar') {
// @ts-expect-error
this.extendToolbar(module);
}
return module;
}
buildButtons(buttons, icons) {
Array.from(buttons).forEach(button => {
const className = button.getAttribute('class') || '';
className.split(/\s+/).forEach(name => {
if (!name.startsWith('ql-')) return;
name = name.slice('ql-'.length);
if (icons[name] == null) return;
if (name === 'direction') {
// @ts-expect-error
button.innerHTML = icons[name][''] + icons[name].rtl;
} else if (typeof icons[name] === 'string') {
// @ts-expect-error
button.innerHTML = icons[name];
} else {
// @ts-expect-error
const value = button.value || '';
// @ts-expect-error
if (value != null && icons[name][value]) {
// @ts-expect-error
button.innerHTML = icons[name][value];
}
}
});
});
}
buildPickers(selects, icons) {
this.pickers = Array.from(selects).map(select => {
if (select.classList.contains('ql-align')) {
if (select.querySelector('option') == null) {
fillSelect(select, ALIGNS);
}
if (typeof icons.align === 'object') {
return new IconPicker(select, icons.align);
}
}
if (select.classList.contains('ql-background') || select.classList.contains('ql-color')) {
const format = select.classList.contains('ql-background') ? 'background' : 'color';
if (select.querySelector('option') == null) {
fillSelect(select, COLORS, format === 'background' ? '#ffffff' : '#000000');
}
return new ColorPicker(select, icons[format]);
}
if (select.querySelector('option') == null) {
if (select.classList.contains('ql-font')) {
fillSelect(select, FONTS);
} else if (select.classList.contains('ql-header')) {
fillSelect(select, HEADERS);
} else if (select.classList.contains('ql-size')) {
fillSelect(select, SIZES);
}
}
return new Picker(select);
});
const update = () => {
this.pickers.forEach(picker => {
picker.update();
});
};
this.quill.on(Emitter.events.EDITOR_CHANGE, update);
}
}
BaseTheme.DEFAULTS = merge({}, Theme.DEFAULTS, {
modules: {
toolbar: {
handlers: {
formula() {
this.quill.theme.tooltip.edit('formula');
},
image() {
let fileInput = this.container.querySelector('input.ql-image[type=file]');
if (fileInput == null) {
fileInput = document.createElement('input');
fileInput.setAttribute('type', 'file');
fileInput.setAttribute('accept', this.quill.uploader.options.mimetypes.join(', '));
fileInput.classList.add('ql-image');
fileInput.addEventListener('change', () => {
const range = this.quill.getSelection(true);
this.quill.uploader.upload(range, fileInput.files);
fileInput.value = '';
});
this.container.appendChild(fileInput);
}
fileInput.click();
},
video() {
this.quill.theme.tooltip.edit('video');
}
}
}
}
});
class BaseTooltip extends Tooltip {
constructor(quill, boundsContainer) {
super(quill, boundsContainer);
this.textbox = this.root.querySelector('input[type="text"]');
this.listen();
}
listen() {
// @ts-expect-error Fix me later
this.textbox.addEventListener('keydown', event => {
if (event.key === 'Enter') {
this.save();
event.preventDefault();
} else if (event.key === 'Escape') {
this.cancel();
event.preventDefault();
}
});
}
cancel() {
this.hide();
this.restoreFocus();
}
edit() {
let mode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'link';
let preview = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
this.root.classList.remove('ql-hidden');
this.root.classList.add('ql-editing');
if (this.textbox == null) return;
if (preview != null) {
this.textbox.value = preview;
} else if (mode !== this.root.getAttribute('data-mode')) {
this.textbox.value = '';
}
const bounds = this.quill.getBounds(this.quill.selection.savedRange);
if (bounds != null) {
this.position(bounds);
}
this.textbox.select();
this.textbox.setAttribute('placeholder', this.textbox.getAttribute(`data-${mode}`) || '');
this.root.setAttribute('data-mode', mode);
}
restoreFocus() {
this.quill.focus({
preventScroll: true
});
}
save() {
// @ts-expect-error Fix me later
let {
value
} = this.textbox;
switch (this.root.getAttribute('data-mode')) {
case 'link':
{
const {
scrollTop
} = this.quill.root;
if (this.linkRange) {
this.quill.formatText(this.linkRange, 'link', value, Emitter.sources.USER);
delete this.linkRange;
} else {
this.restoreFocus();
this.quill.format('link', value, Emitter.sources.USER);
}
this.quill.root.scrollTop = scrollTop;
break;
}
case 'video':
{
value = extractVideoUrl(value);
}
// eslint-disable-next-line no-fallthrough
case 'formula':
{
if (!value) break;
const range = this.quill.getSelection(true);
if (range != null) {
const index = range.index + range.length;
this.quill.insertEmbed(index,
// @ts-expect-error Fix me later
this.root.getAttribute('data-mode'), value, Emitter.sources.USER);
if (this.root.getAttribute('data-mode') === 'formula') {
this.quill.insertText(index + 1, ' ', Emitter.sources.USER);
}
this.quill.setSelection(index + 2, Emitter.sources.USER);
}
break;
}
default:
}
// @ts-expect-error Fix me later
this.textbox.value = '';
this.hide();
}
}
function extractVideoUrl(url) {
let match = url.match(/^(?:(https?):\/\/)?(?:(?:www|m)\.)?youtube\.com\/watch.*v=([a-zA-Z0-9_-]+)/) || url.match(/^(?:(https?):\/\/)?(?:(?:www|m)\.)?youtu\.be\/([a-zA-Z0-9_-]+)/);
if (match) {
return `${match[1] || 'https'}://www.youtube.com/embed/${match[2]}?showinfo=0`;
}
// eslint-disable-next-line no-cond-assign
if (match = url.match(/^(?:(https?):\/\/)?(?:www\.)?vimeo\.com\/(\d+)/)) {
return `${match[1] || 'https'}://player.vimeo.com/video/${match[2]}/`;
}
return url;
}
function fillSelect(select, values) {
let defaultValue = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
values.forEach(value => {
const option = document.createElement('option');
if (value === defaultValue) {
option.setAttribute('selected', 'selected');
} else {
option.setAttribute('value', String(value));
}
select.appendChild(option);
});
}
export { BaseTooltip, BaseTheme as default };
//# sourceMappingURL=base.js.map

File diff suppressed because one or more lines are too long

18
public/assets/quill/themes/bubble.d.ts vendored Normal file
View File

@@ -0,0 +1,18 @@
import BaseTheme, { BaseTooltip } from './base.js';
import type { Bounds } from '../core/selection.js';
import Quill from '../core/quill.js';
import type { ThemeOptions } from '../core/theme.js';
import type Toolbar from '../modules/toolbar.js';
declare class BubbleTooltip extends BaseTooltip {
static TEMPLATE: string;
constructor(quill: Quill, bounds?: HTMLElement);
listen(): void;
cancel(): void;
position(reference: Bounds): number;
}
declare class BubbleTheme extends BaseTheme {
tooltip: BubbleTooltip;
constructor(quill: Quill, options: ThemeOptions);
extendToolbar(toolbar: Toolbar): void;
}
export { BubbleTooltip, BubbleTheme as default };

View File

@@ -0,0 +1,114 @@
import { merge } from 'lodash-es';
import Emitter from '../core/emitter.js';
import BaseTheme, { BaseTooltip } from './base.js';
import { Range } from '../core/selection.js';
import icons from '../ui/icons.js';
import Quill from '../core/quill.js';
const TOOLBAR_CONFIG = [['bold', 'italic', 'link'], [{
header: 1
}, {
header: 2
}, 'blockquote']];
class BubbleTooltip extends BaseTooltip {
static TEMPLATE = ['<span class="ql-tooltip-arrow"></span>', '<div class="ql-tooltip-editor">', '<input type="text" data-formula="e=mc^2" data-link="https://quilljs.com" data-video="Embed URL">', '<a class="ql-close"></a>', '</div>'].join('');
constructor(quill, bounds) {
super(quill, bounds);
this.quill.on(Emitter.events.EDITOR_CHANGE, (type, range, oldRange, source) => {
if (type !== Emitter.events.SELECTION_CHANGE) return;
if (range != null && range.length > 0 && source === Emitter.sources.USER) {
this.show();
// Lock our width so we will expand beyond our offsetParent boundaries
this.root.style.left = '0px';
this.root.style.width = '';
this.root.style.width = `${this.root.offsetWidth}px`;
const lines = this.quill.getLines(range.index, range.length);
if (lines.length === 1) {
const bounds = this.quill.getBounds(range);
if (bounds != null) {
this.position(bounds);
}
} else {
const lastLine = lines[lines.length - 1];
const index = this.quill.getIndex(lastLine);
const length = Math.min(lastLine.length() - 1, range.index + range.length - index);
const indexBounds = this.quill.getBounds(new Range(index, length));
if (indexBounds != null) {
this.position(indexBounds);
}
}
} else if (document.activeElement !== this.textbox && this.quill.hasFocus()) {
this.hide();
}
});
}
listen() {
super.listen();
// @ts-expect-error Fix me later
this.root.querySelector('.ql-close').addEventListener('click', () => {
this.root.classList.remove('ql-editing');
});
this.quill.on(Emitter.events.SCROLL_OPTIMIZE, () => {
// Let selection be restored by toolbar handlers before repositioning
setTimeout(() => {
if (this.root.classList.contains('ql-hidden')) return;
const range = this.quill.getSelection();
if (range != null) {
const bounds = this.quill.getBounds(range);
if (bounds != null) {
this.position(bounds);
}
}
}, 1);
});
}
cancel() {
this.show();
}
position(reference) {
const shift = super.position(reference);
const arrow = this.root.querySelector('.ql-tooltip-arrow');
// @ts-expect-error
arrow.style.marginLeft = '';
if (shift !== 0) {
// @ts-expect-error
arrow.style.marginLeft = `${-1 * shift - arrow.offsetWidth / 2}px`;
}
return shift;
}
}
class BubbleTheme extends BaseTheme {
constructor(quill, options) {
if (options.modules.toolbar != null && options.modules.toolbar.container == null) {
options.modules.toolbar.container = TOOLBAR_CONFIG;
}
super(quill, options);
this.quill.container.classList.add('ql-bubble');
}
extendToolbar(toolbar) {
// @ts-expect-error
this.tooltip = new BubbleTooltip(this.quill, this.options.bounds);
if (toolbar.container != null) {
this.tooltip.root.appendChild(toolbar.container);
this.buildButtons(toolbar.container.querySelectorAll('button'), icons);
this.buildPickers(toolbar.container.querySelectorAll('select'), icons);
}
}
}
BubbleTheme.DEFAULTS = merge({}, BaseTheme.DEFAULTS, {
modules: {
toolbar: {
handlers: {
link(value) {
if (!value) {
this.quill.format('link', false, Quill.sources.USER);
} else {
// @ts-expect-error
this.quill.theme.tooltip.edit();
}
}
}
}
}
});
export { BubbleTooltip, BubbleTheme as default };
//# sourceMappingURL=bubble.js.map

File diff suppressed because one or more lines are too long

9
public/assets/quill/themes/snow.d.ts vendored Normal file
View File

@@ -0,0 +1,9 @@
import BaseTheme from './base.js';
import Quill from '../core/quill.js';
import type Toolbar from '../modules/toolbar.js';
import type { ThemeOptions } from '../core/theme.js';
declare class SnowTheme extends BaseTheme {
constructor(quill: Quill, options: ThemeOptions);
extendToolbar(toolbar: Toolbar): void;
}
export default SnowTheme;

View File

@@ -0,0 +1,122 @@
import { merge } from 'lodash-es';
import Emitter from '../core/emitter.js';
import BaseTheme, { BaseTooltip } from './base.js';
import LinkBlot from '../formats/link.js';
import { Range } from '../core/selection.js';
import icons from '../ui/icons.js';
import Quill from '../core/quill.js';
const TOOLBAR_CONFIG = [[{
header: ['1', '2', '3', false]
}], ['bold', 'italic', 'underline', 'link'], [{
list: 'ordered'
}, {
list: 'bullet'
}], ['clean']];
class SnowTooltip extends BaseTooltip {
static TEMPLATE = ['<a class="ql-preview" rel="noopener noreferrer" target="_blank" href="about:blank"></a>', '<input type="text" data-formula="e=mc^2" data-link="https://quilljs.com" data-video="Embed URL">', '<a class="ql-action"></a>', '<a class="ql-remove"></a>'].join('');
preview = this.root.querySelector('a.ql-preview');
listen() {
super.listen();
// @ts-expect-error Fix me later
this.root.querySelector('a.ql-action').addEventListener('click', event => {
if (this.root.classList.contains('ql-editing')) {
this.save();
} else {
// @ts-expect-error Fix me later
this.edit('link', this.preview.textContent);
}
event.preventDefault();
});
// @ts-expect-error Fix me later
this.root.querySelector('a.ql-remove').addEventListener('click', event => {
if (this.linkRange != null) {
const range = this.linkRange;
this.restoreFocus();
this.quill.formatText(range, 'link', false, Emitter.sources.USER);
delete this.linkRange;
}
event.preventDefault();
this.hide();
});
this.quill.on(Emitter.events.SELECTION_CHANGE, (range, oldRange, source) => {
if (range == null) return;
if (range.length === 0 && source === Emitter.sources.USER) {
const [link, offset] = this.quill.scroll.descendant(LinkBlot, range.index);
if (link != null) {
this.linkRange = new Range(range.index - offset, link.length());
const preview = LinkBlot.formats(link.domNode);
// @ts-expect-error Fix me later
this.preview.textContent = preview;
// @ts-expect-error Fix me later
this.preview.setAttribute('href', preview);
this.show();
const bounds = this.quill.getBounds(this.linkRange);
if (bounds != null) {
this.position(bounds);
}
return;
}
} else {
delete this.linkRange;
}
this.hide();
});
}
show() {
super.show();
this.root.removeAttribute('data-mode');
}
}
class SnowTheme extends BaseTheme {
constructor(quill, options) {
if (options.modules.toolbar != null && options.modules.toolbar.container == null) {
options.modules.toolbar.container = TOOLBAR_CONFIG;
}
super(quill, options);
this.quill.container.classList.add('ql-snow');
}
extendToolbar(toolbar) {
if (toolbar.container != null) {
toolbar.container.classList.add('ql-snow');
this.buildButtons(toolbar.container.querySelectorAll('button'), icons);
this.buildPickers(toolbar.container.querySelectorAll('select'), icons);
// @ts-expect-error
this.tooltip = new SnowTooltip(this.quill, this.options.bounds);
if (toolbar.container.querySelector('.ql-link')) {
this.quill.keyboard.addBinding({
key: 'k',
shortKey: true
}, (_range, context) => {
toolbar.handlers.link.call(toolbar, !context.format.link);
});
}
}
}
}
SnowTheme.DEFAULTS = merge({}, BaseTheme.DEFAULTS, {
modules: {
toolbar: {
handlers: {
link(value) {
if (value) {
const range = this.quill.getSelection();
if (range == null || range.length === 0) return;
let preview = this.quill.getText(range);
if (/^\S+@\S+\.\S+$/.test(preview) && preview.indexOf('mailto:') !== 0) {
preview = `mailto:${preview}`;
}
// @ts-expect-error
const {
tooltip
} = this.quill.theme;
tooltip.edit('link', preview);
} else {
this.quill.format('link', false, Quill.sources.USER);
}
}
}
}
}
});
export default SnowTheme;
//# sourceMappingURL=snow.js.map

File diff suppressed because one or more lines are too long