@airoom/nextmin-react 2.0.1 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +57 -0
  2. package/dist/components/AddressAutocomplete.d.ts +4 -1
  3. package/dist/components/AddressAutocomplete.js +2 -2
  4. package/dist/components/AdminApp.js +86 -1
  5. package/dist/components/FileUploader.d.ts +3 -0
  6. package/dist/components/FileUploader.js +84 -28
  7. package/dist/components/PasswordInput.d.ts +2 -0
  8. package/dist/components/PasswordInput.js +2 -2
  9. package/dist/components/PhoneInput.d.ts +2 -0
  10. package/dist/components/PhoneInput.js +2 -2
  11. package/dist/components/QRCodeModal.d.ts +10 -0
  12. package/dist/components/QRCodeModal.js +153 -0
  13. package/dist/components/RefSelect.d.ts +4 -1
  14. package/dist/components/RefSelect.js +163 -65
  15. package/dist/components/SchemaForm.d.ts +2 -1
  16. package/dist/components/SchemaForm.js +132 -55
  17. package/dist/components/Sidebar.js +96 -7
  18. package/dist/components/TableFilters.js +1 -1
  19. package/dist/components/editor/TiptapEditor.js +46 -13
  20. package/dist/components/editor/Toolbar.js +195 -62
  21. package/dist/components/editor/components/CustomFontSizeModal.d.ts +13 -0
  22. package/dist/components/editor/components/CustomFontSizeModal.js +81 -0
  23. package/dist/components/editor/components/DistrictGridModal.js +1 -1
  24. package/dist/components/editor/components/SchemaInsertionModal.js +1 -1
  25. package/dist/components/editor/editor.css +2 -0
  26. package/dist/components/editor/extensions/FontSize.d.ts +19 -0
  27. package/dist/components/editor/extensions/FontSize.js +41 -0
  28. package/dist/components/viewer/DynamicViewer.js +50 -15
  29. package/dist/components/viewer/useViewDrawer.js +42 -2
  30. package/dist/hooks/useAppNavigation.d.ts +3 -0
  31. package/dist/hooks/useAppNavigation.js +37 -0
  32. package/dist/index.d.ts +2 -0
  33. package/dist/index.js +1 -0
  34. package/dist/lib/QueryBuilder.d.ts +3 -1
  35. package/dist/lib/QueryBuilder.js +10 -0
  36. package/dist/lib/api.d.ts +6 -0
  37. package/dist/lib/api.js +45 -24
  38. package/dist/lib/schemaUtils.js +1 -1
  39. package/dist/lib/types.d.ts +6 -1
  40. package/dist/nextmin.css +1 -1
  41. package/dist/providers/NextMinProvider.d.ts +5 -1
  42. package/dist/providers/NextMinProvider.js +12 -2
  43. package/dist/providers/themeResolver.d.ts +25 -0
  44. package/dist/providers/themeResolver.js +174 -0
  45. package/dist/router/NextMinRouter.d.ts +0 -1
  46. package/dist/router/NextMinRouter.js +0 -1
  47. package/dist/views/CreateEditPage.js +20 -11
  48. package/dist/views/DashboardPage.js +1 -1
  49. package/dist/views/ListPage.js +37 -6
  50. package/dist/views/SettingsEdit.js +1 -1
  51. package/dist/views/list/DataTableHero.d.ts +5 -5
  52. package/dist/views/list/DataTableHero.js +154 -41
  53. package/dist/views/list/ListHeader.js +9 -1
  54. package/package.json +5 -2
@@ -4,6 +4,7 @@ import { Color } from '@tiptap/extension-color';
4
4
  import { TextStyle } from '@tiptap/extension-text-style';
5
5
  import Highlight from '@tiptap/extension-highlight';
6
6
  import { useEditor, EditorContent } from '@tiptap/react';
7
+ import { FontFamily } from '@tiptap/extension-font-family';
7
8
  import StarterKit from '@tiptap/starter-kit';
8
9
  import Link from '@tiptap/extension-link';
9
10
  import { api } from '../../lib/api';
@@ -17,19 +18,37 @@ import TableHeader from '@tiptap/extension-table-header';
17
18
  import { ResizableImage } from './extensions/ResizableImage';
18
19
  import { GridContainer, GridItem } from './extensions/Grid';
19
20
  import { LayoutRow, LayoutColumn } from './extensions/Layout';
21
+ import { FontSize } from './extensions/FontSize';
20
22
  import { Toolbar } from './Toolbar';
21
23
  import { cn } from '../../lib/utils';
22
24
  // Styles for Tiptap
23
25
  // Styles for Tiptap
24
26
  // import '../../styles.css'; // REMOVED: CSS should be imported by consumer (see package.json exports)
25
27
  import { SlashCommand, getSuggestionOptions } from './extensions/SlashCommand';
26
- // ... imports
28
+ import { Extension } from '@tiptap/core';
27
29
  import { SchemaInsertionModal } from './components/SchemaInsertionModal';
28
30
  import { DistrictGridModal } from './components/DistrictGridModal';
29
31
  import { ImageBubbleMenu } from './components/ImageBubbleMenu';
30
32
  import { TableBubbleMenu } from './components/TableBubbleMenu';
31
33
  import { Container } from './extensions/Container';
32
- // ... interface
34
+ // Extension to clear inherited marks when pressing Enter at the end of a heading
35
+ const ClearMarksOnHeaderEnter = Extension.create({
36
+ name: 'clearMarksOnHeaderEnter',
37
+ addKeyboardShortcuts() {
38
+ return {
39
+ Enter: ({ editor }) => {
40
+ if (editor.isActive('heading')) {
41
+ const handled = editor.commands.splitBlock();
42
+ if (handled) {
43
+ editor.commands.unsetAllMarks();
44
+ return true;
45
+ }
46
+ }
47
+ return false;
48
+ },
49
+ };
50
+ },
51
+ });
33
52
  export const TiptapEditor = ({ value, onChange, className, placeholder = 'Start writing...', availableSchemas = [], currentSpeciality }) => {
34
53
  // State for schema insertion modal
35
54
  const [selectedSchema, setSelectedSchema] = useState(null);
@@ -44,13 +63,18 @@ export const TiptapEditor = ({ value, onChange, className, placeholder = 'Start
44
63
  const extensions = React.useMemo(() => [
45
64
  StarterKit.configure({
46
65
  heading: {
47
- levels: [1, 2, 3]
48
- }
66
+ levels: [1, 2, 3, 4, 5, 6]
67
+ },
68
+ link: false,
69
+ underline: false,
49
70
  }),
71
+ ClearMarksOnHeaderEnter,
50
72
  Highlight.configure({
51
73
  multicolor: true,
52
74
  }),
53
75
  TextStyle,
76
+ FontSize,
77
+ FontFamily,
54
78
  Color,
55
79
  Link.configure({
56
80
  openOnClick: false,
@@ -90,22 +114,31 @@ export const TiptapEditor = ({ value, onChange, className, placeholder = 'Start
90
114
  editorProps: {
91
115
  attributes: {
92
116
  class: cn("prose prose-sm sm:prose-base dark:prose-invert focus:outline-none h-[500px] overflow-y-auto p-4 max-w-none [&_li_p]:m-0", className)
93
- }
117
+ },
118
+ transformPastedHTML(html) {
119
+ let cleaned = html
120
+ .replace(/<!--[\s\S]*?-->/g, '')
121
+ .replace(/<o:p>[\s\S]*?<\/o:p>/gi, '')
122
+ .replace(/<\/?(xml|meta|link)[^>]*>/gi, '');
123
+ return cleaned;
124
+ },
94
125
  },
95
126
  onUpdate: ({ editor }) => {
96
127
  onChange(editor.getHTML());
97
128
  },
98
129
  immediatelyRender: false,
99
130
  });
100
- // ... useEffect for sync ...
131
+ // Sync editor content when value prop changes from outside
101
132
  useEffect(() => {
102
- if (editor && value !== editor.getHTML()) {
103
- if (editor.getText() === '' && value === '')
104
- return;
105
- if (!editor.isFocused) {
106
- setTimeout(() => {
107
- editor.commands.setContent(value);
108
- }, 0);
133
+ if (editor && value !== undefined) {
134
+ const currentHTML = editor.getHTML();
135
+ if (value !== currentHTML) {
136
+ // Do not reset if both are effectively empty
137
+ if ((editor.getText() === '' || currentHTML === '<p></p>') && (value === '' || value === '<p></p>'))
138
+ return;
139
+ if (!editor.isFocused) {
140
+ editor.commands.setContent(value || '', { emitUpdate: false });
141
+ }
109
142
  }
110
143
  }
111
144
  }, [value, editor]);
@@ -1,95 +1,228 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useRef } from 'react';
3
- import { Bold, Italic, Underline as UnderlineIcon, List, ListOrdered, Quote, Code, Undo, Redo, Image as ImageIcon, LayoutTemplate, LayoutPanelLeft, AlignLeft, AlignCenter, AlignRight, Square, Table as TableIcon, Palette, PaintBucket, Type, MapPin, AlignJustify, Link as LinkIcon } from 'lucide-react';
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import React, { useRef, useState } from 'react';
4
+ import { RemoveFormatting, Bold, Italic, Underline as UnderlineIcon, Strikethrough, List, ListOrdered, Quote, Code, Code2, Undo, Redo, Image as ImageIcon, LayoutTemplate, LayoutPanelLeft, AlignLeft, AlignCenter, AlignRight, AlignJustify, Square, Table as TableIcon, Palette, PaintBucket, Type, MapPin, Link as LinkIcon, Minus, } from 'lucide-react';
4
5
  import { cn } from '../../lib/utils';
5
6
  import { uploadFile } from '../../lib/upload';
7
+ import { CustomFontSizeModal } from './components/CustomFontSizeModal';
8
+ // ─── Block Type Dropdown ─────────────────────────────────────────────────────
9
+ const BLOCK_TYPES = [
10
+ { label: 'Paragraph', value: 'paragraph' },
11
+ { label: 'Heading 1', value: 'h1' },
12
+ { label: 'Heading 2', value: 'h2' },
13
+ { label: 'Heading 3', value: 'h3' },
14
+ { label: 'Heading 4', value: 'h4' },
15
+ { label: 'Heading 5', value: 'h5' },
16
+ { label: 'Heading 6', value: 'h6' },
17
+ ];
18
+ function getActiveBlock(editor) {
19
+ for (let i = 1; i <= 6; i++) {
20
+ if (editor.isActive('heading', { level: i }))
21
+ return `h${i}`;
22
+ }
23
+ return 'paragraph';
24
+ }
25
+ function setBlock(editor, value) {
26
+ if (value === 'paragraph') {
27
+ editor.chain().focus().setParagraph().run();
28
+ }
29
+ else {
30
+ const level = parseInt(value.replace('h', ''), 10);
31
+ editor.chain().focus().toggleHeading({ level }).unsetFontSize().run();
32
+ }
33
+ }
34
+ // ─── Font Family List ─────────────────────────────────────────────────────────
35
+ const FONT_GROUPS = [
36
+ {
37
+ label: 'Default',
38
+ fonts: [
39
+ { label: 'Default', value: 'default' },
40
+ ],
41
+ },
42
+ {
43
+ label: 'Sans-Serif',
44
+ fonts: [
45
+ { label: 'Inter', value: "'Inter', sans-serif" },
46
+ { label: 'Roboto', value: "'Roboto', sans-serif" },
47
+ { label: 'Open Sans', value: "'Open Sans', sans-serif" },
48
+ { label: 'Lato', value: "'Lato', sans-serif" },
49
+ { label: 'Poppins', value: "'Poppins', sans-serif" },
50
+ { label: 'Nunito', value: "'Nunito', sans-serif" },
51
+ { label: 'Outfit', value: "'Outfit', sans-serif" },
52
+ { label: 'DM Sans', value: "'DM Sans', sans-serif" },
53
+ { label: 'Figtree', value: "'Figtree', sans-serif" },
54
+ ],
55
+ },
56
+ {
57
+ label: 'Serif',
58
+ fonts: [
59
+ { label: 'Georgia', value: "Georgia, serif" },
60
+ { label: 'Playfair Display', value: "'Playfair Display', serif" },
61
+ { label: 'Merriweather', value: "'Merriweather', serif" },
62
+ { label: 'Lora', value: "'Lora', serif" },
63
+ { label: 'EB Garamond', value: "'EB Garamond', serif" },
64
+ ],
65
+ },
66
+ {
67
+ label: 'Monospace',
68
+ fonts: [
69
+ { label: 'Fira Code', value: "'Fira Code', monospace" },
70
+ { label: 'JetBrains Mono', value: "'JetBrains Mono', monospace" },
71
+ { label: 'Source Code Pro', value: "'Source Code Pro', monospace" },
72
+ ],
73
+ },
74
+ {
75
+ label: 'Bangla',
76
+ fonts: [
77
+ { label: 'Hind Siliguri', value: "'Hind Siliguri', sans-serif" },
78
+ { label: 'Noto Sans Bengali', value: "'Noto Sans Bengali', sans-serif" },
79
+ { label: 'Kalpurush', value: "'Kalpurush', sans-serif" },
80
+ { label: 'Siyam Rupali', value: "'Siyam Rupali', sans-serif" },
81
+ { label: 'SolaimanLipi', value: "'SolaimanLipi', sans-serif" },
82
+ { label: 'Akaash', value: "'Akaash', sans-serif" },
83
+ ],
84
+ },
85
+ ];
86
+ // ─── Font Size List ──────────────────────────────────────────────────────────
87
+ const FONT_SIZES = [
88
+ { label: 'Default size', value: 'default' },
89
+ { label: '10px', value: '10px' },
90
+ { label: '12px', value: '12px' },
91
+ { label: '13px', value: '13px' },
92
+ { label: '14px', value: '14px' },
93
+ { label: '15px', value: '15px' },
94
+ { label: '16px', value: '16px' },
95
+ { label: '18px', value: '18px' },
96
+ { label: '20px', value: '20px' },
97
+ { label: '24px', value: '24px' },
98
+ { label: '28px', value: '28px' },
99
+ { label: '32px', value: '32px' },
100
+ { label: '36px', value: '36px' },
101
+ { label: '48px', value: '48px' },
102
+ { label: '72px', value: '72px' },
103
+ { label: 'Custom...', value: 'custom' },
104
+ ];
105
+ // ─── Divider ──────────────────────────────────────────────────────────────────
106
+ const Divider = () => (_jsx("div", { className: "w-px h-5 bg-gray-200 dark:bg-gray-700 mx-0.5 flex-shrink-0" }));
107
+ // ─── Toolbar Button ───────────────────────────────────────────────────────────
108
+ const Btn = ({ onClick, isActive = false, disabled = false, title, children, }) => (_jsx("button", { onClick: onClick, disabled: disabled, title: title, type: "button", className: cn('inline-flex items-center justify-center w-7 h-7 rounded-md text-gray-600 dark:text-gray-300', 'transition-all duration-100', 'hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-900 dark:hover:text-white', isActive && 'bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400 ring-1 ring-blue-200 dark:ring-blue-800', disabled && 'opacity-30 cursor-not-allowed pointer-events-none'), children: children }));
109
+ // ─── Styled native select ─────────────────────────────────────────────────────
110
+ const NativeSelect = ({ value, onChange, className, children, title, }) => (_jsx("select", { title: title, value: value, onChange: e => onChange(e.target.value), className: cn('h-7 px-1.5 text-xs rounded-md border border-gray-200 dark:border-gray-700', 'bg-white dark:bg-zinc-900 text-gray-700 dark:text-gray-200', 'outline-none focus:ring-1 focus:ring-blue-400 cursor-pointer', 'appearance-none pr-5', className), style: { backgroundImage: "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%236b7280'/%3E%3C/svg%3E\")", backgroundRepeat: 'no-repeat', backgroundPosition: 'right 4px center' }, children: children }));
111
+ // ─── Main Toolbar ─────────────────────────────────────────────────────────────
6
112
  export const Toolbar = ({ editor, onDistrictGridClick }) => {
7
113
  const fileInputRef = useRef(null);
8
- if (!editor) {
114
+ const [fontSizeInput, setFontSizeInput] = useState(null);
115
+ const [isCustomFontSizeModalOpen, setIsCustomFontSizeModalOpen] = useState(false);
116
+ const [, setTick] = useState(0);
117
+ React.useEffect(() => {
118
+ if (!editor)
119
+ return;
120
+ const update = () => setTick(t => t + 1);
121
+ editor.on('selectionUpdate', update);
122
+ editor.on('transaction', update);
123
+ editor.on('focus', update);
124
+ editor.on('blur', update);
125
+ return () => {
126
+ editor.off('selectionUpdate', update);
127
+ editor.off('transaction', update);
128
+ editor.off('focus', update);
129
+ editor.off('blur', update);
130
+ };
131
+ }, [editor]);
132
+ if (!editor)
9
133
  return null;
10
- }
11
134
  const addImage = async (e) => {
12
135
  const file = e.target.files?.[0];
13
136
  if (file) {
14
137
  try {
15
- // Upload logic here - reusing the one we discussed or a simple URL creator for now
16
- // For production, use the real uploadFile utility
17
- // const url = URL.createObjectURL(file);
18
- const url = await uploadFile(file); // Needs verify this exists/works
19
- if (url) {
138
+ const url = await uploadFile(file);
139
+ if (url)
20
140
  editor.chain().focus().setImage({ src: url }).run();
21
- }
22
141
  }
23
142
  catch (error) {
24
- console.error("Upload failed", error);
143
+ console.error('Upload failed', error);
25
144
  }
26
145
  }
27
- // Reset inputs
28
146
  if (fileInputRef.current)
29
147
  fileInputRef.current.value = '';
30
148
  };
31
- const handleImageClick = () => {
32
- fileInputRef.current?.click();
149
+ const activeFont = editor.getAttributes('textStyle').fontFamily || 'default';
150
+ const activeFontSize = editor.getAttributes('textStyle').fontSize || '';
151
+ const applyFontSize = (val) => {
152
+ const trimmed = val.trim();
153
+ if (!trimmed || trimmed.toLowerCase() === 'default' || trimmed === '0') {
154
+ editor.chain().focus().unsetFontSize().run();
155
+ }
156
+ else {
157
+ editor.chain().focus().setFontSize(trimmed).run();
158
+ }
33
159
  };
34
- const ToolbarButton = ({ onClick, isActive = false, children, disabled = false }) => (_jsx("button", { onClick: onClick, disabled: disabled, className: cn("p-2 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors", isActive && "bg-gray-200 dark:bg-gray-700 text-blue-500", disabled && "opacity-50 cursor-not-allowed"), type: "button", children: children }));
35
- return (_jsxs("div", { className: "border-b border-gray-200 dark:border-gray-800 p-2 flex flex-wrap gap-1 sticky top-0 bg-white dark:bg-zinc-950 z-10 items-center", children: [_jsx(ToolbarButton, { onClick: () => editor.chain().focus().toggleBold().run(), isActive: editor.isActive('bold'), children: _jsx(Bold, { size: 18 }) }), _jsx(ToolbarButton, { onClick: () => editor.chain().focus().toggleItalic().run(), isActive: editor.isActive('italic'), children: _jsx(Italic, { size: 18 }) }), _jsx(ToolbarButton, { onClick: () => editor.chain().focus().toggleUnderline().run(), isActive: editor.isActive('underline'), children: _jsx(UnderlineIcon, { size: 18 }) }), _jsx(ToolbarButton, { onClick: () => {
36
- const previousUrl = editor.getAttributes('link').href;
37
- const url = window.prompt('URL', previousUrl);
38
- if (url === null) {
39
- return;
160
+ return (_jsxs("div", { className: "\n flex flex-wrap items-center gap-0.5 px-2 py-1.5\n border-b border-gray-100 dark:border-gray-800\n bg-gray-50/80 dark:bg-zinc-900/80\n backdrop-blur-sm sticky top-0 z-10\n ", children: [_jsx(Btn, { onClick: () => editor.chain().focus().undo().run(), disabled: !editor.can().undo(), title: "Undo (Ctrl+Z)", children: _jsx(Undo, { size: 15 }) }), _jsx(Btn, { onClick: () => editor.chain().focus().redo().run(), disabled: !editor.can().redo(), title: "Redo (Ctrl+Shift+Z)", children: _jsx(Redo, { size: 15 }) }), _jsx(Divider, {}), _jsx(NativeSelect, { title: "Block type", value: getActiveBlock(editor), onChange: v => setBlock(editor, v), className: "w-32", children: BLOCK_TYPES.map(bt => (_jsx("option", { value: bt.value, children: bt.label }, bt.value))) }), _jsx(Divider, {}), _jsx(NativeSelect, { title: "Font family", value: activeFont, onChange: v => {
161
+ if (v === 'default') {
162
+ editor.chain().focus().unsetFontFamily().run();
40
163
  }
164
+ else {
165
+ editor.chain().focus().setFontFamily(v).run();
166
+ }
167
+ }, className: "w-36", children: FONT_GROUPS.map(group => (_jsx("optgroup", { label: group.label, children: group.fonts.map(f => (_jsx("option", { value: f.value, children: f.label }, f.value))) }, group.label))) }), _jsxs(NativeSelect, { title: "Font size", value: activeFontSize || 'default', onChange: v => {
168
+ if (v === 'custom') {
169
+ setIsCustomFontSizeModalOpen(true);
170
+ }
171
+ else if (v === 'default') {
172
+ editor.chain().focus().unsetFontSize().run();
173
+ }
174
+ else {
175
+ editor.chain().focus().setFontSize(v).run();
176
+ }
177
+ }, className: "w-24", children: [!FONT_SIZES.some(s => s.value === activeFontSize) && activeFontSize && (_jsx("option", { value: activeFontSize, children: activeFontSize })), FONT_SIZES.map(s => (_jsx("option", { value: s.value, children: s.label }, s.value)))] }), _jsx(Divider, {}), _jsx(Btn, { onClick: () => editor.chain().focus().toggleBold().run(), isActive: editor.isActive('bold'), title: "Bold (Ctrl+B)", children: _jsx(Bold, { size: 15 }) }), _jsx(Btn, { onClick: () => editor.chain().focus().toggleItalic().run(), isActive: editor.isActive('italic'), title: "Italic (Ctrl+I)", children: _jsx(Italic, { size: 15 }) }), _jsx(Btn, { onClick: () => editor.chain().focus().toggleUnderline().run(), isActive: editor.isActive('underline'), title: "Underline (Ctrl+U)", children: _jsx(UnderlineIcon, { size: 15 }) }), _jsx(Btn, { onClick: () => editor.chain().focus().toggleStrike().run(), isActive: editor.isActive('strike'), title: "Strikethrough", children: _jsx(Strikethrough, { size: 15 }) }), _jsx(Btn, { onClick: () => editor.chain().focus().toggleCode().run(), isActive: editor.isActive('code'), title: "Inline code", children: _jsx(Code, { size: 15 }) }), _jsx(Btn, { onClick: () => {
178
+ editor.chain()
179
+ .focus()
180
+ .unsetAllMarks()
181
+ .unsetFontSize()
182
+ .unsetFontFamily()
183
+ .run();
184
+ }, title: "Clear formatting", children: _jsx(RemoveFormatting, { size: 15 }) }), _jsx(Divider, {}), _jsx(Btn, { onClick: () => editor.chain().focus().setTextAlign('left').run(), isActive: editor.isActive({ textAlign: 'left' }), title: "Align left", children: _jsx(AlignLeft, { size: 15 }) }), _jsx(Btn, { onClick: () => editor.chain().focus().setTextAlign('center').run(), isActive: editor.isActive({ textAlign: 'center' }), title: "Align center", children: _jsx(AlignCenter, { size: 15 }) }), _jsx(Btn, { onClick: () => editor.chain().focus().setTextAlign('right').run(), isActive: editor.isActive({ textAlign: 'right' }), title: "Align right", children: _jsx(AlignRight, { size: 15 }) }), _jsx(Btn, { onClick: () => editor.chain().focus().setTextAlign('justify').run(), isActive: editor.isActive({ textAlign: 'justify' }), title: "Justify", children: _jsx(AlignJustify, { size: 15 }) }), _jsx(Divider, {}), _jsx(Btn, { onClick: () => editor.chain().focus().toggleBulletList().run(), isActive: editor.isActive('bulletList'), title: "Bullet list", children: _jsx(List, { size: 15 }) }), _jsx(Btn, { onClick: () => editor.chain().focus().toggleOrderedList().run(), isActive: editor.isActive('orderedList'), title: "Numbered list", children: _jsx(ListOrdered, { size: 15 }) }), _jsx(Divider, {}), _jsx(Btn, { onClick: () => editor.chain().focus().toggleBlockquote().run(), isActive: editor.isActive('blockquote'), title: "Blockquote", children: _jsx(Quote, { size: 15 }) }), _jsx(Btn, { onClick: () => editor.chain().focus().toggleCodeBlock().run(), isActive: editor.isActive('codeBlock'), title: "Code block", children: _jsx(Code2, { size: 15 }) }), _jsx(Btn, { onClick: () => editor.chain().focus().setHorizontalRule().run(), title: "Horizontal rule", children: _jsx(Minus, { size: 15 }) }), _jsx(Divider, {}), _jsx(Btn, { onClick: () => {
185
+ const prev = editor.getAttributes('link').href;
186
+ const url = window.prompt('URL', prev);
187
+ if (url === null)
188
+ return;
41
189
  if (url === '') {
42
190
  editor.chain().focus().extendMarkRange('link').unsetLink().run();
43
191
  return;
44
192
  }
45
193
  editor.chain().focus().extendMarkRange('link').setLink({ href: url }).run();
46
- }, isActive: editor.isActive('link'), children: _jsx(LinkIcon, { size: 18 }) }), _jsx("div", { className: "w-[1px] h-6 bg-gray-300 dark:bg-gray-700 mx-1" }), _jsx(ToolbarButton, { onClick: () => editor.chain().focus().setTextAlign('left').run(), isActive: editor.isActive({ textAlign: 'left' }), children: _jsx(AlignLeft, { size: 18 }) }), _jsx(ToolbarButton, { onClick: () => editor.chain().focus().setTextAlign('center').run(), isActive: editor.isActive({ textAlign: 'center' }), children: _jsx(AlignCenter, { size: 18 }) }), _jsx(ToolbarButton, { onClick: () => editor.chain().focus().setTextAlign('right').run(), isActive: editor.isActive({ textAlign: 'right' }), children: _jsx(AlignRight, { size: 18 }) }), _jsx(ToolbarButton, { onClick: () => editor.chain().focus().setTextAlign('justify').run(), isActive: editor.isActive({ textAlign: 'justify' }), children: _jsx(AlignJustify, { size: 18 }) }), _jsx("div", { className: "w-[1px] h-6 bg-gray-300 dark:bg-gray-700 mx-1" }), _jsx(ToolbarButton, { onClick: () => editor.chain().focus().toggleHeading({ level: 1 }).run(), isActive: editor.isActive('heading', { level: 1 }), children: _jsx("span", { className: "font-bold text-sm", children: "H1" }) }), _jsx(ToolbarButton, { onClick: () => editor.chain().focus().toggleHeading({ level: 2 }).run(), isActive: editor.isActive('heading', { level: 2 }), children: _jsx("span", { className: "font-bold text-sm", children: "H2" }) }), _jsx("div", { className: "w-[1px] h-6 bg-gray-300 dark:bg-gray-700 mx-1" }), _jsx(ToolbarButton, { onClick: () => editor.chain().focus().toggleBulletList().run(), isActive: editor.isActive('bulletList'), children: _jsx(List, { size: 18 }) }), _jsx(ToolbarButton, { onClick: () => editor.chain().focus().toggleOrderedList().run(), isActive: editor.isActive('orderedList'), children: _jsx(ListOrdered, { size: 18 }) }), _jsx("div", { className: "w-[1px] h-6 bg-gray-300 dark:bg-gray-700 mx-1" }), _jsx(ToolbarButton, { onClick: () => editor.chain().focus().toggleBlockquote().run(), isActive: editor.isActive('blockquote'), children: _jsx(Quote, { size: 18 }) }), _jsx(ToolbarButton, { onClick: () => editor.chain().focus().toggleCodeBlock().run(), isActive: editor.isActive('codeBlock'), children: _jsx(Code, { size: 18 }) }), _jsx("div", { className: "w-[1px] h-6 bg-gray-300 dark:bg-gray-700 mx-1" }), _jsx(ToolbarButton, { onClick: () => {
47
- editor
48
- .chain()
49
- .focus()
194
+ }, isActive: editor.isActive('link'), title: "Link", children: _jsx(LinkIcon, { size: 15 }) }), _jsx(Btn, { onClick: () => fileInputRef.current?.click(), title: "Insert image", children: _jsx(ImageIcon, { size: 15 }) }), _jsx("input", { type: "file", ref: fileInputRef, onChange: addImage, className: "hidden", accept: "image/*" }), _jsx(Divider, {}), _jsx(Btn, { onClick: () => editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run(), title: "Insert table", children: _jsx(TableIcon, { size: 15 }) }), _jsx(Btn, { title: "2-column layout", onClick: () => {
195
+ editor.chain().focus()
50
196
  .insertContent('<p>&nbsp;</p>')
51
197
  .insertContent('<div data-type="layout-row" data-cols="2"><div data-type="layout-column"><p></p></div><div data-type="layout-column"><p></p></div></div>')
52
198
  .run();
53
- }, children: _jsx(LayoutTemplate, { size: 18 }) }), _jsx(ToolbarButton, { onClick: () => {
54
- editor
55
- .chain()
56
- .focus()
199
+ }, children: _jsx(LayoutTemplate, { size: 15 }) }), _jsx(Btn, { title: "3-column layout", onClick: () => {
200
+ editor.chain().focus()
57
201
  .insertContent('<p>&nbsp;</p>')
58
202
  .insertContent('<div data-type="layout-row" data-cols="3"><div data-type="layout-column"><p></p></div><div data-type="layout-column"><p></p></div><div data-type="layout-column"><p></p></div></div>')
59
203
  .run();
60
- }, children: _jsx(LayoutPanelLeft, { size: 18 }) }), _jsx("div", { className: "w-[1px] h-6 bg-gray-300 dark:bg-gray-700 mx-1" }), onDistrictGridClick && (_jsx(ToolbarButton, { onClick: onDistrictGridClick, children: _jsx(MapPin, { size: 18 }) })), _jsx("div", { className: "w-[1px] h-6 bg-gray-300 dark:bg-gray-700 mx-1" }), _jsx("div", { className: "w-[1px] h-6 bg-gray-300 dark:bg-gray-700 mx-1" }), _jsx(ToolbarButton, { onClick: () => {
61
- editor
62
- .chain()
63
- .focus()
64
- .insertContent({
65
- type: 'container',
66
- content: [
67
- {
68
- type: 'paragraph',
204
+ }, children: _jsx(LayoutPanelLeft, { size: 15 }) }), _jsx(Btn, { title: "Full-width container", onClick: () => {
205
+ editor.chain().focus().insertContent({ type: 'container', content: [{ type: 'paragraph' }] }).run();
206
+ }, children: _jsx(Square, { size: 15 }) }), onDistrictGridClick && (_jsxs(_Fragment, { children: [_jsx(Divider, {}), _jsx(Btn, { onClick: onDistrictGridClick, title: "District grid", children: _jsx(MapPin, { size: 15 }) })] })), _jsx(Divider, {}), _jsxs("div", { className: "relative inline-flex", title: "Text color", children: [_jsx(Btn, { onClick: () => { }, isActive: editor.isActive('textStyle'), children: _jsx(Type, { size: 15 }) }), _jsx("input", { type: "color", className: "absolute inset-0 opacity-0 w-full h-full cursor-pointer", onChange: e => editor.chain().focus().setColor(e.target.value).run(), value: editor.getAttributes('textStyle').color || '#000000' })] }), _jsxs("div", { className: "relative inline-flex", title: "Highlight color", children: [_jsx(Btn, { onClick: () => { }, isActive: editor.isActive('highlight'), children: _jsx(Palette, { size: 15 }) }), _jsx("input", { type: "color", className: "absolute inset-0 opacity-0 w-full h-full cursor-pointer", onChange: e => editor.chain().focus().setHighlight({ color: e.target.value }).run(), value: editor.getAttributes('highlight').color || '#ffff00' })] }), _jsxs("div", { className: "relative inline-flex", title: "Background color", children: [_jsx(Btn, { onClick: () => { }, isActive: !!editor.getAttributes('gridContainer').backgroundColor ||
207
+ !!editor.getAttributes('layoutRow').backgroundColor ||
208
+ !!editor.getAttributes('layoutColumn').backgroundColor ||
209
+ !!editor.getAttributes('container').backgroundColor, children: _jsx(PaintBucket, { size: 15 }) }), _jsx("input", { type: "color", className: "absolute inset-0 opacity-0 w-full h-full cursor-pointer", onChange: e => {
210
+ const val = e.target.value;
211
+ const cmd = editor.chain().focus();
212
+ if (editor.isActive('gridItem') || editor.isActive('gridContainer')) {
213
+ cmd.updateAttributes('gridContainer', { backgroundColor: val });
69
214
  }
70
- ]
71
- })
72
- .run();
73
- }, children: _jsxs("div", { className: "flex flex-col items-center justify-center", children: [_jsx(Square, { size: 18 }), _jsx("span", { className: "text-[10px] leading-none", children: "Full" })] }) }), _jsx("div", { className: "w-[1px] h-6 bg-gray-300 dark:bg-gray-700 mx-1" }), _jsx(ToolbarButton, { onClick: () => editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run(), children: _jsx(TableIcon, { size: 18 }) }), _jsx("div", { className: "w-[1px] h-6 bg-gray-300 dark:bg-gray-700 mx-1" }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsxs("div", { className: "relative group", children: [_jsx(ToolbarButton, { onClick: () => { }, isActive: editor.isActive('textStyle'), children: _jsx("div", { className: "flex items-center", children: _jsx(Type, { size: 18 }) }) }), _jsx("input", { type: "color", className: "absolute inset-0 opacity-0 w-full h-full cursor-pointer", onChange: (e) => editor.chain().focus().setColor(e.target.value).run(), value: editor.getAttributes('textStyle').color || '#000000' })] }), _jsxs("div", { className: "relative group", children: [_jsx(ToolbarButton, { onClick: () => { }, isActive: editor.isActive('highlight'), children: _jsx("div", { className: "flex items-center", children: _jsx(Palette, { size: 18 }) }) }), _jsx("input", { type: "color", className: "absolute inset-0 opacity-0 w-full h-full cursor-pointer", onChange: (e) => {
74
- const color = e.target.value;
75
- editor.chain().focus().setHighlight({ color }).run();
76
- }, value: editor.getAttributes('highlight').color || '#ffff00' })] }), _jsxs("div", { className: "relative group", children: [_jsx(ToolbarButton, { onClick: () => { }, isActive: !!editor.getAttributes('gridContainer').backgroundColor || !!editor.getAttributes('layoutRow').backgroundColor || !!editor.getAttributes('layoutColumn').backgroundColor || !!editor.getAttributes('container').backgroundColor, children: _jsx(PaintBucket, { size: 18 }) }), _jsx("input", { type: "color", className: "absolute inset-0 opacity-0 w-full h-full cursor-pointer", onChange: (e) => {
77
- const val = e.target.value;
78
- // Priority: Grid Container (Apply to ALL) -> Container -> Column -> Row
79
- if (editor.isActive('gridItem')) {
80
- // Set on CONTAINER so it applies to all items via variable
81
- editor.chain().focus().updateAttributes('gridContainer', { backgroundColor: val }).run();
82
- }
83
- else if (editor.isActive('container')) {
84
- editor.chain().focus().updateAttributes('container', { backgroundColor: val }).run();
85
- }
86
- else if (editor.isActive('layoutColumn')) {
87
- editor.chain().focus().updateAttributes('layoutColumn', { backgroundColor: val }).run();
88
- }
89
- else if (editor.isActive('layoutRow')) {
90
- editor.chain().focus().updateAttributes('layoutRow', { backgroundColor: val }).run();
91
- }
92
- } })] })] }), _jsx("div", { className: "w-[1px] h-6 bg-gray-300 dark:bg-gray-700 mx-1" }), _jsxs("div", { className: "flex items-center gap-1 relative group", children: [_jsx("span", { className: "text-[10px] text-gray-500 absolute left-1 pointer-events-none", children: "R" }), _jsx("input", { type: "number", placeholder: "0", className: "w-16 h-8 pl-4 pr-1 text-xs bg-transparent border border-gray-200 dark:border-gray-700 rounded", onChange: (e) => {
215
+ else if (editor.isActive('container')) {
216
+ cmd.updateAttributes('container', { backgroundColor: val });
217
+ }
218
+ else if (editor.isActive('layoutColumn')) {
219
+ cmd.updateAttributes('layoutColumn', { backgroundColor: val });
220
+ }
221
+ else if (editor.isActive('layoutRow')) {
222
+ cmd.updateAttributes('layoutRow', { backgroundColor: val });
223
+ }
224
+ cmd.run();
225
+ } })] }), _jsx(Divider, {}), _jsxs("div", { className: "relative flex items-center", title: "Border radius", children: [_jsx("span", { className: "absolute left-1.5 text-[9px] text-gray-400 pointer-events-none font-medium", children: "R" }), _jsx("input", { type: "number", placeholder: "0", className: "\n w-14 h-7 pl-4 pr-1 text-xs rounded-md\n border border-gray-200 dark:border-gray-700\n bg-white dark:bg-zinc-900\n text-gray-700 dark:text-gray-200\n outline-none focus:ring-1 focus:ring-blue-400\n ", onChange: e => {
93
226
  const val = e.target.value;
94
227
  const radius = val ? `${val}px` : null;
95
228
  const cmd = editor.chain().focus();
@@ -106,5 +239,5 @@ export const Toolbar = ({ editor, onDistrictGridClick }) => {
106
239
  cmd.updateAttributes('layoutRow', { borderRadius: radius });
107
240
  }
108
241
  cmd.run();
109
- } })] }), _jsx("div", { className: "w-[1px] h-6 bg-gray-300 dark:bg-gray-700 mx-1" }), _jsx(ToolbarButton, { onClick: handleImageClick, children: _jsx(ImageIcon, { size: 18 }) }), _jsx("input", { type: "file", ref: fileInputRef, onChange: addImage, className: "hidden", accept: "image/*" }), _jsx("div", { className: "flex-1" }), _jsx(ToolbarButton, { onClick: () => editor.chain().focus().undo().run(), disabled: !editor.can().undo(), children: _jsx(Undo, { size: 18 }) }), _jsx(ToolbarButton, { onClick: () => editor.chain().focus().redo().run(), disabled: !editor.can().redo(), children: _jsx(Redo, { size: 18 }) })] }));
242
+ } })] }), _jsx(CustomFontSizeModal, { isOpen: isCustomFontSizeModalOpen, onClose: () => setIsCustomFontSizeModalOpen(false), onApply: applyFontSize, currentSize: activeFontSize })] }));
110
243
  };
@@ -0,0 +1,13 @@
1
+ interface CustomFontSizeModalProps {
2
+ isOpen: boolean;
3
+ onClose: () => void;
4
+ onApply: (fontSize: string) => void;
5
+ currentSize?: string;
6
+ }
7
+ export declare function validateAndFormatFontSize(input: string): {
8
+ valid: boolean;
9
+ formatted: string;
10
+ error?: string;
11
+ };
12
+ export declare const CustomFontSizeModal: ({ isOpen, onClose, onApply, currentSize, }: CustomFontSizeModalProps) => import("react/jsx-runtime").JSX.Element;
13
+ export {};
@@ -0,0 +1,81 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useState, useEffect } from 'react';
4
+ import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button, Input, } from '@heroui/react';
5
+ import { Type, AlertCircle } from 'lucide-react';
6
+ export function validateAndFormatFontSize(input) {
7
+ const raw = input.trim();
8
+ if (!raw)
9
+ return { valid: false, formatted: '', error: 'Please enter a font size' };
10
+ const match = raw.match(/^([0-9]+(?:\.[0-9]+)?)\s*([a-zA-Z%]*)$/);
11
+ if (!match) {
12
+ return { valid: false, formatted: raw, error: 'Invalid size format (e.g. 16px or 1.5rem)' };
13
+ }
14
+ const num = parseFloat(match[1]);
15
+ const unit = match[2].toLowerCase();
16
+ if (isNaN(num)) {
17
+ return { valid: false, formatted: raw, error: 'Invalid numeric value' };
18
+ }
19
+ // Default unit to px if none provided
20
+ if (!unit || unit === 'px') {
21
+ if (num < 6) {
22
+ return { valid: false, formatted: '6px', error: 'Minimum allowed size is 6px' };
23
+ }
24
+ if (num > 150) {
25
+ return { valid: false, formatted: '150px', error: 'Maximum allowed size is 150px (capped)' };
26
+ }
27
+ return { valid: true, formatted: `${num}px` };
28
+ }
29
+ if (unit === 'rem' || unit === 'em') {
30
+ if (num < 0.5) {
31
+ return { valid: false, formatted: `0.5${unit}`, error: `Minimum allowed size is 0.5${unit}` };
32
+ }
33
+ if (num > 8) {
34
+ return { valid: false, formatted: `8${unit}`, error: `Maximum size for ${unit} is 8${unit} (capped at ~128px)` };
35
+ }
36
+ return { valid: true, formatted: `${num}${unit}` };
37
+ }
38
+ if (unit === 'pt') {
39
+ if (num < 5)
40
+ return { valid: false, formatted: '5pt', error: 'Minimum allowed size is 5pt' };
41
+ if (num > 115)
42
+ return { valid: false, formatted: '115pt', error: 'Maximum allowed size is 115pt' };
43
+ return { valid: true, formatted: `${num}pt` };
44
+ }
45
+ if (unit === '%') {
46
+ if (num < 50)
47
+ return { valid: false, formatted: '50%', error: 'Minimum allowed size is 50%' };
48
+ if (num > 1000)
49
+ return { valid: false, formatted: '1000%', error: 'Maximum allowed size is 1000%' };
50
+ return { valid: true, formatted: `${num}%` };
51
+ }
52
+ return { valid: false, formatted: raw, error: `Unsupported unit "${unit}". Use px, rem, pt, or em` };
53
+ }
54
+ export const CustomFontSizeModal = ({ isOpen, onClose, onApply, currentSize = '', }) => {
55
+ const [sizeInput, setSizeInput] = useState(currentSize || '16px');
56
+ useEffect(() => {
57
+ if (isOpen) {
58
+ setSizeInput(currentSize || '16px');
59
+ }
60
+ }, [isOpen, currentSize]);
61
+ const validation = validateAndFormatFontSize(sizeInput);
62
+ const handleApply = () => {
63
+ if (validation.formatted) {
64
+ onApply(validation.formatted);
65
+ }
66
+ onClose();
67
+ };
68
+ return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, size: "xs", backdrop: "opaque", classNames: {
69
+ base: "dark:bg-zinc-900 border border-gray-200 dark:border-gray-800 rounded-xl shadow-xl",
70
+ header: "border-b border-gray-100 dark:border-gray-800 py-3 px-4",
71
+ body: "py-4 px-4",
72
+ footer: "border-t border-gray-100 dark:border-gray-800 py-2.5 px-4 flex justify-end gap-2"
73
+ }, children: _jsx(ModalContent, { children: () => (_jsxs(_Fragment, { children: [_jsxs(ModalHeader, { className: "flex items-center gap-2 text-sm font-semibold text-gray-800 dark:text-gray-100", children: [_jsx(Type, { size: 16, className: "text-blue-500" }), _jsx("span", { children: "Custom Font Size" })] }), _jsx(ModalBody, { children: _jsxs("div", { className: "flex flex-col gap-2.5", children: [_jsx("label", { className: "text-xs text-gray-500 dark:text-gray-400", children: "Enter size (e.g. 15px, 22px, 1.5rem)" }), _jsx(Input, { autoFocus: true, placeholder: "16px", value: sizeInput, onChange: (e) => setSizeInput(e.target.value), onKeyDown: (e) => {
74
+ if (e.key === 'Enter') {
75
+ e.preventDefault();
76
+ handleApply();
77
+ }
78
+ }, variant: "bordered", size: "sm", isInvalid: !validation.valid, classNames: {
79
+ inputWrapper: "border-gray-200 dark:border-gray-700 bg-white dark:bg-zinc-950 shadow-none focus-within:ring-2 focus-within:ring-blue-500"
80
+ } }), validation.error && (_jsxs("div", { className: "flex items-center gap-1.5 text-[11px] text-amber-600 dark:text-amber-400 font-medium", children: [_jsx(AlertCircle, { size: 13, className: "flex-shrink-0" }), _jsx("span", { children: validation.error })] })), _jsxs("div", { className: "flex items-center gap-1.5 pt-1", children: [_jsx("span", { className: "text-[11px] text-gray-400", children: "Quick units:" }), ['12px', '15px', '22px', '28px', '40px'].map((preset) => (_jsx("button", { type: "button", onClick: () => setSizeInput(preset), className: "px-1.5 py-0.5 text-[11px] rounded bg-gray-100 dark:bg-zinc-800 text-gray-600 dark:text-gray-300 hover:bg-blue-50 hover:text-blue-600 dark:hover:bg-blue-950 transition-colors", children: preset }, preset)))] })] }) }), _jsxs(ModalFooter, { children: [_jsx(Button, { size: "sm", variant: "flat", color: "default", onPress: onClose, children: "Cancel" }), _jsx(Button, { size: "sm", color: "primary", onPress: handleApply, children: "Apply Size" })] })] })) }) }));
81
+ };
@@ -67,5 +67,5 @@ export const DistrictGridModal = ({ isOpen, onClose, onInsert, currentSpeciality
67
67
  ? "bg-primary-50 border-primary-500 text-primary-700 dark:bg-primary-900/20 dark:text-primary-400"
68
68
  : "bg-transparent border-gray-200 dark:border-gray-800 hover:bg-gray-50 dark:hover:bg-gray-900"), children: "Doctors" }), _jsx("button", { onClick: () => setBaseType('hospitals'), className: cn("px-4 py-2 rounded-lg border text-sm font-medium transition-colors", baseType === 'hospitals'
69
69
  ? "bg-primary-50 border-primary-500 text-primary-700 dark:bg-primary-900/20 dark:text-primary-400"
70
- : "bg-transparent border-gray-200 dark:border-gray-800 hover:bg-gray-50 dark:hover:bg-gray-900"), children: "Hospitals" })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(RefSelect, { name: "districts", label: "Search & Select Districts", refModel: "Districts", value: selectedDistricts, onChange: (ids) => setSelectedDistricts(ids), multiple: true, pageSize: 1000 }), _jsx("p", { className: "text-xs text-gray-400", children: "Select one or more districts to create grid items." })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(RefSelect, { name: "speciality", label: "Search & Select Speciality", refModel: "Specialities", showKey: "name", value: selectedSpecialityId, onChange: (id) => setSelectedSpecialityId(id || ''), multiple: false, pageSize: 10000 }), _jsx("p", { className: "text-xs text-gray-400", children: "Leave empty to create URLs without speciality." }), specialitySlug && (_jsx("div", { className: "mt-1 px-2 py-1 bg-primary-50 dark:bg-primary-900/20 rounded border border-primary-200 dark:border-primary-800", children: _jsxs("p", { className: "text-xs text-primary-700 dark:text-primary-400", children: ["Slug: ", _jsx("code", { className: "font-mono", children: specialitySlug })] }) }))] }), selectedDistricts.length > 0 && (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("label", { className: "text-sm font-medium", children: "URL Preview" }), _jsxs("div", { className: "px-3 py-2 bg-gray-50 dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-800 max-h-32 overflow-y-auto", children: [_jsx("p", { className: "text-xs text-gray-500 mb-2", children: "Example URL pattern (actual district slugs will be used):" }), _jsx("code", { className: "text-xs text-gray-700 dark:text-gray-300", children: getPreviewUrl('[district-slug]') })] })] }))] }), _jsxs(ModalFooter, { children: [_jsx(Button, { variant: "flat", onPress: onClose, children: "Cancel" }), _jsx(Button, { color: "primary", onPress: handleInsert, isDisabled: selectedDistricts.length === 0, children: "Insert Grid" })] })] }) }) }));
70
+ : "bg-transparent border-gray-200 dark:border-gray-800 hover:bg-gray-50 dark:hover:bg-gray-900"), children: "Hospitals" })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(RefSelect, { name: "districts", label: "Search & Select Districts", refModel: "Districts", value: selectedDistricts, onChange: (ids) => setSelectedDistricts(ids), multiple: true }), _jsx("p", { className: "text-xs text-gray-400", children: "Select one or more districts to create grid items." })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(RefSelect, { name: "speciality", label: "Search & Select Speciality", refModel: "Specialities", showKey: "name", value: selectedSpecialityId, onChange: (id) => setSelectedSpecialityId(id || ''), multiple: false }), _jsx("p", { className: "text-xs text-gray-400", children: "Leave empty to create URLs without speciality." }), specialitySlug && (_jsx("div", { className: "mt-1 px-2 py-1 bg-primary-50 dark:bg-primary-900/20 rounded border border-primary-200 dark:border-primary-800", children: _jsxs("p", { className: "text-xs text-primary-700 dark:text-primary-400", children: ["Slug: ", _jsx("code", { className: "font-mono", children: specialitySlug })] }) }))] }), selectedDistricts.length > 0 && (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("label", { className: "text-sm font-medium", children: "URL Preview" }), _jsxs("div", { className: "px-3 py-2 bg-gray-50 dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-800 max-h-32 overflow-y-auto", children: [_jsx("p", { className: "text-xs text-gray-500 mb-2", children: "Example URL pattern (actual district slugs will be used):" }), _jsx("code", { className: "text-xs text-gray-700 dark:text-gray-300", children: getPreviewUrl('[district-slug]') })] })] }))] }), _jsxs(ModalFooter, { children: [_jsx(Button, { variant: "flat", onPress: onClose, children: "Cancel" }), _jsx(Button, { color: "primary", onPress: handleInsert, isDisabled: selectedDistricts.length === 0, children: "Insert Grid" })] })] }) }) }));
71
71
  };
@@ -83,7 +83,7 @@ export const SchemaInsertionModal = ({ isOpen, onClose, onInsert, schema, availa
83
83
  base: "bg-white dark:bg-zinc-950 border border-gray-200 dark:border-gray-800",
84
84
  header: "border-b border-gray-200 dark:border-gray-800",
85
85
  footer: "border-t border-gray-200 dark:border-gray-800",
86
- }, children: _jsx(ModalContent, { children: _jsxs(_Fragment, { children: [_jsxs(ModalHeader, { children: ["Insert ", schema.modelName] }), _jsxs(ModalBody, { className: "py-6 flex flex-col gap-6", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(RefSelect, { name: "items", label: "Search & Select", refModel: schema.modelName, value: selectedIds, onChange: (ids) => setSelectedIds(ids), multiple: true, pageSize: 10000 }), _jsx("p", { className: "text-xs text-gray-400", children: "Leave empty to fetch latest items automatically." })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("label", { className: "text-sm font-medium", children: "View Type" }), _jsxs("div", { className: "flex gap-4", children: [_jsx("button", { onClick: () => setViewType('grid'), className: cn("px-4 py-2 rounded-lg border text-sm font-medium transition-colors", viewType === 'grid'
86
+ }, children: _jsx(ModalContent, { children: _jsxs(_Fragment, { children: [_jsxs(ModalHeader, { children: ["Insert ", schema.modelName] }), _jsxs(ModalBody, { className: "py-6 flex flex-col gap-6", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(RefSelect, { name: "items", label: "Search & Select", refModel: schema.modelName, value: selectedIds, onChange: (ids) => setSelectedIds(ids), multiple: true }), _jsx("p", { className: "text-xs text-gray-400", children: "Leave empty to fetch latest items automatically." })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("label", { className: "text-sm font-medium", children: "View Type" }), _jsxs("div", { className: "flex gap-4", children: [_jsx("button", { onClick: () => setViewType('grid'), className: cn("px-4 py-2 rounded-lg border text-sm font-medium transition-colors", viewType === 'grid'
87
87
  ? "bg-primary-50 border-primary-500 text-primary-700 dark:bg-primary-900/20 dark:text-primary-400"
88
88
  : "bg-transparent border-gray-200 dark:border-gray-800 hover:bg-gray-50"), children: "Grid (Pills)" }), _jsx("button", { onClick: () => setViewType('list'), className: cn("px-4 py-2 rounded-lg border text-sm font-medium transition-colors", viewType === 'list'
89
89
  ? "bg-primary-50 border-primary-500 text-primary-700 dark:bg-primary-900/20 dark:text-primary-400"
@@ -1,3 +1,5 @@
1
+ @import url('https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=EB+Garamond:ital,wght@0,400..800;1,400..800&family=Figtree:ital,wght@0,300..900;1,300..900&family=Fira+Code:wght@300..700&family=Hind+Siliguri:wght@300;400;500;600;700&family=Inter:wght@100..900&family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&family=Lora:ital,wght@0,400..700;1,400..700&family=Merriweather:ital,wght@0,300;0,400;0,700;0,900;1,300;1,400;1,700;1,900&family=Noto+Sans+Bengali:wght@100..900&family=Nunito:ital,wght@0,200..1000;1,200..1000&family=Open+Sans:ital,wght@0,300..800;1,300..800&family=Outfit:wght@100..900&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&display=swap');
2
+
1
3
  .editor-input {
2
4
  min-height: 150px;
3
5
  resize: none;
@@ -0,0 +1,19 @@
1
+ import { Extension } from '@tiptap/core';
2
+ export interface FontSizeOptions {
3
+ types: string[];
4
+ }
5
+ declare module '@tiptap/core' {
6
+ interface Commands<ReturnType> {
7
+ fontSize: {
8
+ /**
9
+ * Set the font size attribute
10
+ */
11
+ setFontSize: (fontSize: string) => ReturnType;
12
+ /**
13
+ * Unset the font size attribute
14
+ */
15
+ unsetFontSize: () => ReturnType;
16
+ };
17
+ }
18
+ }
19
+ export declare const FontSize: Extension<FontSizeOptions, any>;