@jackuait/blok 0.4.3-beta.7 → 0.4.3-beta.8

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.
@@ -1,4 +1,4 @@
1
- import { e as i } from "./blok-B-fFKCc3.mjs";
1
+ import { e as i } from "./blok-JdFwSTgC.mjs";
2
2
  const l = async (e, r) => {
3
3
  const n = (await import("./i18next-CugVlwWp.mjs")).default.createInstance(), s = {
4
4
  lng: e,
@@ -1,4 +1,4 @@
1
- import { t as f, q as i } from "./inline-tool-convert-BQzo9tk1.mjs";
1
+ import { t as f, q as i } from "./inline-tool-convert-MJUgGURB.mjs";
2
2
  const a = {
3
3
  wrapper: i(
4
4
  "fixed z-[2] bottom-5 left-5",
@@ -18,7 +18,7 @@ let nt = (o = 21) => {
18
18
  return t;
19
19
  };
20
20
  var ot = /* @__PURE__ */ ((o) => (o.VERBOSE = "VERBOSE", o.INFO = "INFO", o.WARN = "WARN", o.ERROR = "ERROR", o))(ot || {});
21
- const rt = () => "0.4.3-beta.7", Ct = {
21
+ const rt = () => "0.4.3-beta.8", Ct = {
22
22
  BACKSPACE: 8,
23
23
  TAB: 9,
24
24
  ENTER: 13,
package/dist/full.mjs CHANGED
@@ -10,10 +10,10 @@ var e = (a, l, o) => l in a ? n(a, l, { enumerable: !0, configurable: !0, writab
10
10
  d.call(l, o) && e(a, o, l[o]);
11
11
  return a;
12
12
  }, r = (a, l) => t(a, c(l));
13
- import { B as v, v as A } from "./chunks/blok-B-fFKCc3.mjs";
13
+ import { B as v, v as A } from "./chunks/blok-JdFwSTgC.mjs";
14
14
  import { List as p, Header as f, Paragraph as I, Link as k, Italic as u, Bold as B } from "./tools.mjs";
15
15
  import { defaultBlockTools as H, defaultInlineTools as P } from "./tools.mjs";
16
- import { D as _ } from "./chunks/inline-tool-convert-BQzo9tk1.mjs";
16
+ import { D as _ } from "./chunks/inline-tool-convert-MJUgGURB.mjs";
17
17
  const m = {
18
18
  paragraph: {
19
19
  class: I,
package/dist/tools.mjs CHANGED
@@ -10,8 +10,8 @@ var U = (f, t, e) => t in f ? rt(f, t, { enumerable: !0, configurable: !0, writa
10
10
  at.call(t, e) && U(f, e, t[e]);
11
11
  return f;
12
12
  }, P = (f, t) => st(f, ot(t));
13
- import { t as L, D as g, a9 as nt, aa as lt, ab as G, ac as ct, ad as dt, ae as ut, af as ht, ag as ft, ah as pt, ai as j, aj as $, ak as X, f as A, al as gt, am as mt, S as H, P as Et, an as Tt, l as Ct, J as At } from "./chunks/inline-tool-convert-BQzo9tk1.mjs";
14
- import { a0 as Ot } from "./chunks/inline-tool-convert-BQzo9tk1.mjs";
13
+ import { t as L, D as g, a9 as nt, aa as lt, ab as G, ac as ct, ad as dt, ae as ut, af as ht, ag as ft, ah as pt, ai as j, aj as $, ak as X, f as A, al as gt, am as mt, S as H, P as Et, an as Tt, l as Ct, J as At } from "./chunks/inline-tool-convert-MJUgGURB.mjs";
14
+ import { a0 as Ot } from "./chunks/inline-tool-convert-MJUgGURB.mjs";
15
15
  const W = [
16
16
  "empty:before:pointer-events-none",
17
17
  "empty:before:text-gray-text",
@@ -3093,9 +3093,9 @@ const D = class D {
3093
3093
  D.isInline = !0, D.title = "Link", D.titleKey = "link", D.shortcut = "CMD+K";
3094
3094
  let et = D;
3095
3095
  const xt = {
3096
- paragraph: { inlineToolbar: !0, config: { preserveBlank: !0 } },
3097
- header: { inlineToolbar: !0 },
3098
- list: { inlineToolbar: !0 }
3096
+ paragraph: { preserveBlank: !0 },
3097
+ header: {},
3098
+ list: {}
3099
3099
  }, vt = {
3100
3100
  bold: {},
3101
3101
  italic: {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jackuait/blok",
3
- "version": "0.4.3-beta.7",
3
+ "version": "0.4.3-beta.8",
4
4
  "description": "Blok — headless, highly extensible rich text editor built for developers who need to implement a block-based editing experience (similar to Notion) without building it from scratch",
5
5
  "module": "dist/blok.mjs",
6
6
  "types": "./types/index.d.ts",
@@ -35,6 +35,8 @@ const DRAG_CONFIG = {
35
35
  autoScrollSpeed: 10,
36
36
  /** Throttle delay for drop position announcements (ms) */
37
37
  announcementThrottleMs: 300,
38
+ /** Horizontal distance to the left of blocks where drop is still valid */
39
+ leftDropZone: 50,
38
40
  };
39
41
 
40
42
  /**
@@ -424,20 +426,10 @@ export class DragManager extends Module {
424
426
  return;
425
427
  }
426
428
 
427
- // Find block holder
428
- const blockHolder = elementUnderCursor.closest(createSelector(DATA_ATTR.element)) as HTMLElement | null;
429
+ // Find block holder - first try direct hit, then left drop zone fallback
430
+ const { block: targetBlock, holder: blockHolder } = this.findDropTargetBlock(elementUnderCursor, clientX, clientY);
429
431
 
430
- if (!blockHolder) {
431
- this.dragState.targetBlock = null;
432
- this.dragState.targetEdge = null;
433
-
434
- return;
435
- }
436
-
437
- // Find the block instance
438
- const targetBlock = this.Blok.BlockManager.blocks.find(b => b.holder === blockHolder);
439
-
440
- if (!targetBlock || targetBlock === this.dragState.sourceBlock) {
432
+ if (!blockHolder || !targetBlock || targetBlock === this.dragState.sourceBlock) {
441
433
  this.dragState.targetBlock = null;
442
434
  this.dragState.targetEdge = null;
443
435
 
@@ -1132,6 +1124,72 @@ export class DragManager extends Module {
1132
1124
  return collectDescendants(blockIndex + 1, []);
1133
1125
  }
1134
1126
 
1127
+ /**
1128
+ * Finds the drop target block from an element or by checking the left drop zone.
1129
+ * @param elementUnderCursor - Element directly under the cursor
1130
+ * @param clientX - Cursor X position
1131
+ * @param clientY - Cursor Y position
1132
+ * @returns Object with block and holder, or nulls if no valid target found
1133
+ */
1134
+ private findDropTargetBlock(
1135
+ elementUnderCursor: Element,
1136
+ clientX: number,
1137
+ clientY: number
1138
+ ): { block: Block | undefined; holder: HTMLElement | null } {
1139
+ // First try: find block holder directly under cursor
1140
+ const directHolder = elementUnderCursor.closest(createSelector(DATA_ATTR.element)) as HTMLElement | null;
1141
+
1142
+ if (directHolder) {
1143
+ const block = this.Blok.BlockManager.blocks.find(b => b.holder === directHolder);
1144
+
1145
+ return { block, holder: directHolder };
1146
+ }
1147
+
1148
+ // Fallback: check if cursor is in the left drop zone
1149
+ const leftZoneBlock = this.findBlockInLeftDropZone(clientX, clientY);
1150
+
1151
+ if (leftZoneBlock) {
1152
+ return { block: leftZoneBlock, holder: leftZoneBlock.holder };
1153
+ }
1154
+
1155
+ return { block: undefined, holder: null };
1156
+ }
1157
+
1158
+ /**
1159
+ * Finds a block by vertical position when cursor is in the left drop zone.
1160
+ * Used as a fallback when elementFromPoint doesn't find a block directly.
1161
+ * @param clientX - Cursor X position
1162
+ * @param clientY - Cursor Y position
1163
+ * @returns Block at the vertical position, or null if not in left zone or no block found
1164
+ */
1165
+ private findBlockInLeftDropZone(clientX: number, clientY: number): Block | null {
1166
+ const contentRect = this.Blok.UI.contentRect;
1167
+ const leftEdge = contentRect.left;
1168
+
1169
+ // Check if cursor is within left drop zone (between leftEdge - leftDropZone and leftEdge)
1170
+ const distanceFromEdge = leftEdge - clientX;
1171
+
1172
+ if (distanceFromEdge < 0 || distanceFromEdge > DRAG_CONFIG.leftDropZone) {
1173
+ return null;
1174
+ }
1175
+
1176
+ // Find block by Y position
1177
+ for (const block of this.Blok.BlockManager.blocks) {
1178
+ // Skip source blocks
1179
+ if (this.dragState?.sourceBlocks.includes(block)) {
1180
+ continue;
1181
+ }
1182
+
1183
+ const rect = block.holder.getBoundingClientRect();
1184
+
1185
+ if (clientY >= rect.top && clientY <= rect.bottom) {
1186
+ return block;
1187
+ }
1188
+ }
1189
+
1190
+ return null;
1191
+ }
1192
+
1135
1193
  /**
1136
1194
  * Module destruction
1137
1195
  */
@@ -334,18 +334,18 @@ export class Tools extends Module {
334
334
  .map(([toolName, settings]): ChainData => {
335
335
  const toolData: ToolPrepareData = {
336
336
  toolName,
337
- config: (settings.config ?? {}) as ToolConfig,
337
+ config: this.extractToolConfig(settings),
338
338
  };
339
339
 
340
340
  const prepareFunction: ChainData['function'] = async (payload?: unknown) => {
341
- const constructable = settings.class;
341
+ const constructable = settings.class as ToolConstructable | undefined;
342
342
 
343
343
  if (!constructable || !isFunction(constructable.prepare)) {
344
344
  return;
345
345
  }
346
346
 
347
347
  const data = (payload ?? toolData) as ToolPrepareData;
348
- const prepareMethod = constructable.prepare as unknown as ToolPrepareFunction;
348
+ const prepareMethod = constructable.prepare as ToolPrepareFunction;
349
349
 
350
350
  return prepareMethod.call(constructable, data);
351
351
  };
@@ -357,6 +357,42 @@ export class Tools extends Module {
357
357
  });
358
358
  }
359
359
 
360
+ /**
361
+ * Keys that are Blok-level settings (not passed to tool constructor)
362
+ */
363
+ private static readonly BLOK_SETTINGS_KEYS = new Set([
364
+ 'class',
365
+ 'inlineToolbar',
366
+ 'tunes',
367
+ 'shortcut',
368
+ 'toolbox',
369
+ 'config',
370
+ 'isInternal',
371
+ ]);
372
+
373
+ /**
374
+ * Extracts tool configuration from settings.
375
+ * Merges nested config with flat tool-specific options (flat takes precedence).
376
+ * @param settings - Tool settings from user config
377
+ * @returns Merged tool configuration
378
+ */
379
+ private extractToolConfig(settings: ToolSettings): ToolConfig {
380
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- Internal: reading legacy config for backwards compatibility
381
+ const nestedConfig = (settings.config ?? {}) as ToolConfig;
382
+
383
+ // Extract non-Blok keys as tool-specific config
384
+ const flatConfig: Record<string, unknown> = {};
385
+
386
+ for (const key of Object.keys(settings)) {
387
+ if (!Tools.BLOK_SETTINGS_KEYS.has(key)) {
388
+ flatConfig[key] = settings[key as keyof typeof settings];
389
+ }
390
+ }
391
+
392
+ // Merge: nested config first, flat config overrides
393
+ return { ...nestedConfig, ...flatConfig } as ToolConfig;
394
+ }
395
+
360
396
  /**
361
397
  * Assign enabled Inline Tools and Block Tunes for Block Tool
362
398
  */
@@ -7,6 +7,19 @@ import type { InlineToolAdapter as InlineToolAdapterInterface } from '@/types/to
7
7
  import type { BlockToolAdapter as BlockToolAdapterInterface } from '@/types/tools/adapters/block-tool-adapter';
8
8
  import type { BlockTuneAdapter as BlockTuneAdapterInterface } from '@/types/tools/adapters/block-tune-adapter';
9
9
 
10
+ /**
11
+ * Keys that are Blok-level settings (not passed to tool constructor)
12
+ */
13
+ const BLOK_SETTINGS_KEYS = new Set([
14
+ 'class',
15
+ 'inlineToolbar',
16
+ 'tunes',
17
+ 'shortcut',
18
+ 'toolbox',
19
+ 'config',
20
+ 'isInternal',
21
+ ]);
22
+
10
23
  /**
11
24
  * Enum of Tool options provided by user
12
25
  */
@@ -188,10 +201,24 @@ export abstract class BaseToolAdapter<Type extends ToolType = ToolType, ToolClas
188
201
  }
189
202
 
190
203
  /**
191
- * Returns Tool user configuration
204
+ * Returns Tool user configuration.
205
+ * Extracts tool-specific options from flat config and merges with nested config.
192
206
  */
193
207
  public get settings(): ToolConfig {
194
- const config = (this.config[UserSettings.Config] ?? {}) as ToolConfig;
208
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- Internal: reading legacy config for backwards compatibility
209
+ const nestedConfig = (this.config[UserSettings.Config] ?? {}) as ToolConfig;
210
+
211
+ // Extract non-Blok keys as tool-specific config
212
+ const flatConfig: Record<string, unknown> = {};
213
+
214
+ for (const key of Object.keys(this.config)) {
215
+ if (!BLOK_SETTINGS_KEYS.has(key)) {
216
+ flatConfig[key] = this.config[key as keyof typeof this.config];
217
+ }
218
+ }
219
+
220
+ // Merge: nested config first, flat config overrides
221
+ const config = { ...nestedConfig, ...flatConfig } as ToolConfig;
195
222
 
196
223
  if (this.isDefault && !('placeholder' in config) && this.defaultPlaceholder) {
197
224
  config.placeholder = this.defaultPlaceholder;
@@ -203,10 +203,18 @@ export class BlockToolAdapter extends BaseToolAdapter<ToolType.Block, IBlockTool
203
203
  }
204
204
 
205
205
  /**
206
- * Returns enabled inline tools for Tool
206
+ * Returns enabled inline tools for Tool.
207
+ * Defaults to true (all inline tools) unless explicitly set to false or array.
207
208
  */
208
209
  public get enabledInlineTools(): boolean | string[] {
209
- return this.config[UserSettings.EnabledInlineTools] || false;
210
+ const setting = this.config[UserSettings.EnabledInlineTools];
211
+
212
+ // Default to true if not specified
213
+ if (setting === undefined) {
214
+ return true;
215
+ }
216
+
217
+ return setting;
210
218
  }
211
219
 
212
220
  /**
@@ -206,6 +206,7 @@ export class Header implements BlockTool {
206
206
  /**
207
207
  * If user provided custom toolbox entries, use them to build settings menu.
208
208
  * This ensures block settings match the toolbox configuration.
209
+ * Fall back to levels config only when _toolboxEntries is empty.
209
210
  */
210
211
  if (toolboxEntries !== undefined && toolboxEntries.length > 0) {
211
212
  return this.buildSettingsFromToolboxEntries(toolboxEntries);
@@ -6,10 +6,16 @@
6
6
  * // Import specific tools
7
7
  * import { Paragraph, Header, List, Bold, Italic, Link } from '@jackuait/blok/tools';
8
8
  *
9
- * // Use in Blok configuration
9
+ * // Use in Blok configuration (flat config style)
10
10
  * new Blok({
11
- * tools: { paragraph: Paragraph, header: Header, list: List },
12
- * inlineTools: { bold: Bold, italic: Italic, link: Link }
11
+ * tools: {
12
+ * paragraph: Paragraph,
13
+ * header: { class: Header, levels: [1, 2, 3] },
14
+ * list: List,
15
+ * bold: Bold,
16
+ * italic: Italic,
17
+ * link: Link,
18
+ * }
13
19
  * });
14
20
  */
15
21
 
@@ -25,10 +31,11 @@ export { LinkInlineTool as Link } from '../components/inline-tools/inline-tool-l
25
31
  export { ConvertInlineTool as Convert } from '../components/inline-tools/inline-tool-convert';
26
32
 
27
33
  // Default tools configuration for convenience
34
+ // Note: inlineToolbar defaults to true, so it doesn't need to be specified
28
35
  export const defaultBlockTools = {
29
- paragraph: { inlineToolbar: true, config: { preserveBlank: true } },
30
- header: { inlineToolbar: true },
31
- list: { inlineToolbar: true },
36
+ paragraph: { preserveBlank: true },
37
+ header: {},
38
+ list: {},
32
39
  } as const;
33
40
 
34
41
  export const defaultInlineTools = {
@@ -1,6 +1,12 @@
1
1
  import { ToolConfig } from './tool-config';
2
2
  import { ToolConstructable, BlockToolData, MenuConfig, MenuConfigItem } from './index';
3
3
 
4
+ /**
5
+ * Permissive type for tool class - accepts any constructor.
6
+ * Runtime validation ensures the tool has required methods.
7
+ */
8
+ export type ToolClass = new (...args: any[]) => any;
9
+
4
10
  /**
5
11
  * Tool may specify its toolbox configuration
6
12
  * It may include several entries as well
@@ -49,9 +55,9 @@ export interface ToolboxConfigEntry {
49
55
  export interface ExternalToolSettings<Config extends object = any> {
50
56
 
51
57
  /**
52
- * Tool's class
58
+ * Tool's class - accepts any constructor, validated at runtime
53
59
  */
54
- class: ToolConstructable;
60
+ class: ToolConstructable | ToolClass;
55
61
 
56
62
  /**
57
63
  * User configuration object that will be passed to the Tool's constructor
@@ -88,6 +94,58 @@ export interface ExternalToolSettings<Config extends object = any> {
88
94
  export type InternalToolSettings<Config extends object = any> = Omit<ExternalToolSettings<Config>, 'class'> & Partial<Pick<ExternalToolSettings<Config>, 'class'>>;
89
95
 
90
96
  /**
91
- * Union of external and internal Tools settings
97
+ * Keys that Blok extracts from tool settings (not passed to tool constructor)
98
+ */
99
+ export type BlokToolSettingsKeys = 'class' | 'inlineToolbar' | 'tunes' | 'shortcut' | 'toolbox' | 'config';
100
+
101
+ /**
102
+ * Flat tool settings - tool-specific options at top level.
103
+ * Blok extracts known keys and passes the rest as `config` to the tool.
104
+ */
105
+ export interface FlatToolSettings<Config extends object = any> {
106
+ /**
107
+ * Tool's class
108
+ */
109
+ class: ToolConstructable | ToolClass;
110
+
111
+ /**
112
+ * Is need to show Inline Toolbar.
113
+ * Defaults to true for block tools.
114
+ */
115
+ inlineToolbar?: boolean | string[];
116
+
117
+ /**
118
+ * BlockTunes for Tool
119
+ */
120
+ tunes?: boolean | string[];
121
+
122
+ /**
123
+ * Define shortcut that will render Tool
124
+ */
125
+ shortcut?: string;
126
+
127
+ /**
128
+ * Tool's Toolbox settings
129
+ */
130
+ toolbox?: ToolboxConfig | false;
131
+
132
+ /**
133
+ * Legacy nested config - merged with top-level tool options
134
+ * @deprecated Use flat config instead
135
+ */
136
+ config?: ToolConfig<Config>;
137
+
138
+ /**
139
+ * Tool-specific options (placeholder, levels, etc.)
140
+ * These are passed to the tool constructor as `config`
141
+ */
142
+ [key: string]: unknown;
143
+ }
144
+
145
+ /**
146
+ * Union of all tool settings formats
92
147
  */
93
- export type ToolSettings<Config extends object = any> = InternalToolSettings<Config> | ExternalToolSettings<Config>;
148
+ export type ToolSettings<Config extends object = any> =
149
+ | InternalToolSettings<Config>
150
+ | ExternalToolSettings<Config>
151
+ | FlatToolSettings<Config>;