Skip to content
Open
Show file tree
Hide file tree
Changes from 12 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
12 changes: 7 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@editorjs/image",
"version": "2.10.2",
"version": "2.10.3",
"keywords": [
"codex editor",
"image",
Expand All @@ -25,10 +25,12 @@
},
"scripts": {
"dev": "concurrently \"vite\" \"node ./dev/server.js\"",
"build": "vite build",
"lint": "eslint",
"lint:errors": "eslint --quiet",
"lint:fix": "eslint --fix"
"build": "vite build && tsc --emitDeclarationOnly",
"lint": "eslint ./src --ext .ts",
"lint:errors": "eslint ./src --ext .ts --quiet",
"lint:fix": "eslint ./src --ext .ts --fix",
"type-check": "tsc --noEmit",
"type-check:watch": "tsc --noEmit --watch"
},
"author": {
"name": "CodeX",
Expand Down
94 changes: 73 additions & 21 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,46 @@
*/

import type { TunesMenuConfig } from '@editorjs/editorjs/types/tools';
import type { API, ToolboxConfig, PasteConfig, BlockToolConstructorOptions, BlockTool, BlockAPI, PasteEvent, PatternPasteEventDetail, FilePasteEventDetail } from '@editorjs/editorjs';
import type {
API,
ToolboxConfig,
PasteConfig,
BlockToolConstructorOptions,
BlockTool,
BlockAPI,
PasteEvent,
PatternPasteEventDetail,
FilePasteEventDetail,
} from '@editorjs/editorjs';
import './index.css';

import Ui from './ui';
import Uploader from './uploader';

import { IconAddBorder, IconStretch, IconAddBackground, IconPicture, IconText } from '@codexteam/icons';
import type { ActionConfig, UploadResponseFormat, ImageToolData, ImageConfig, HTMLPasteEventDetailExtended, ImageSetterParam, FeaturesConfig } from './types/types';
import {
IconAddBorder,
IconStretch,
IconAddBackground,
IconPicture,
IconText,
} from '@codexteam/icons';
import type {
ActionConfig,
UploadResponseFormat,
ImageToolData,
ImageConfig,
HTMLPasteEventDetailExtended,
ImageSetterParam,
FeaturesConfig,
} from './types/types';

type ImageToolConstructorOptions = BlockToolConstructorOptions<ImageToolData, ImageConfig>;
/**
* Constructor options for ImageTool
*/
type ImageToolConstructorOptions = BlockToolConstructorOptions<
ImageToolData,
ImageConfig
>;

/**
* Implementation of ImageTool class
Expand All @@ -47,27 +77,27 @@ export default class ImageTool implements BlockTool {
/**
* Editor.js API instance
*/
private api: API;
private readonly api: API;

/**
* Current Block API instance
*/
private block: BlockAPI;
private readonly block: BlockAPI;

/**
* Configuration for the ImageTool
*/
private config: ImageConfig;
private readonly config: ImageConfig;

/**
* Uploader module instance
*/
private uploader: Uploader;
private readonly uploader: Uploader;

/**
* UI module instance
*/
private ui: Ui;
private readonly ui: Ui;

/**
* Stores current block data internally
Expand All @@ -90,7 +120,13 @@ export default class ImageTool implements BlockTool {
* @param tool.readOnly - read-only mode flag
* @param tool.block - current Block API
*/
constructor({ data, config, api, readOnly, block }: ImageToolConstructorOptions) {
constructor({
data,
config,
api,
readOnly,
block,
}: ImageToolConstructorOptions) {
this.api = api;
this.block = block;

Expand All @@ -103,7 +139,9 @@ export default class ImageTool implements BlockTool {
additionalRequestHeaders: config.additionalRequestHeaders,
field: config.field,
types: config.types,
captionPlaceholder: this.api.i18n.t(config.captionPlaceholder ?? 'Caption'),
captionPlaceholder: this.api.i18n.t(
config.captionPlaceholder ?? 'Caption'
),
buttonContent: config.buttonContent,
uploader: config.uploader,
actions: config.actions,
Expand Down Expand Up @@ -199,7 +237,11 @@ export default class ImageTool implements BlockTool {
* Renders Block content
*/
public render(): HTMLDivElement {
if (this.config.features?.caption === true || this.config.features?.caption === undefined || (this.config.features?.caption === 'optional' && this.data.caption)) {
if (
this.config.features?.caption === true ||
this.config.features?.caption === undefined ||
(this.config.features?.caption === 'optional' && this.data.caption)
) {
this.isCaptionEnabled = true;
}

Expand Down Expand Up @@ -251,13 +293,18 @@ export default class ImageTool implements BlockTool {
}

const availableTunes = tunes.filter((tune) => {
const featureKey = Object.keys(featureTuneMap).find(key => featureTuneMap[key] === tune.name);
const featureKey = Object.keys(featureTuneMap).find(
(key) => featureTuneMap[key] === tune.name
);

if (featureKey === 'caption') {
return this.config.features?.caption !== false;
}

return featureKey == null || this.config.features?.[featureKey as keyof FeaturesConfig] !== false;
return (
featureKey == null ||
this.config.features?.[featureKey as keyof FeaturesConfig] !== false
);
});

/**
Expand All @@ -274,7 +321,7 @@ export default class ImageTool implements BlockTool {
return currentState;
};

return availableTunes.map(tune => ({
return availableTunes.map((tune) => ({
icon: tune.icon,
label: this.api.i18n.t(tune.title),
name: tune.name,
Expand Down Expand Up @@ -382,7 +429,7 @@ export default class ImageTool implements BlockTool {

/**
* Private methods
* ̿̿ ̿̿ ̿̿ ̿'̿'\̵͇̿̿\з= ( ▀ ͜͞ʖ▀) =ε/̵͇̿̿/’̿̿ ̿ ̿̿ ̿̿ ̿̿
* ̿̿ ̿̿ ̿̿ ̿'̿'\̵͇̿̿\з= ( ▀ ͜͞ʖ▀) =ε/̵͇̿̿/’̿'̿ ̿ ̿̿ ̿̿ ̿̿
*/

/**
Expand All @@ -396,7 +443,11 @@ export default class ImageTool implements BlockTool {
this.ui.fillCaption(this._data.caption);

ImageTool.tunes.forEach(({ name: tune }) => {
const value = typeof data[tune as keyof ImageToolData] !== 'undefined' ? data[tune as keyof ImageToolData] === true || data[tune as keyof ImageToolData] === 'true' : false;
const value =
typeof data[tune as keyof ImageToolData] !== 'undefined'
? data[tune as keyof ImageToolData] === true ||
data[tune as keyof ImageToolData] === 'true'
: false;

this.setTune(tune as keyof ImageToolData, value);
});
Expand Down Expand Up @@ -445,7 +496,7 @@ export default class ImageTool implements BlockTool {
console.log('Image Tool: uploading failed because of', errorText);

this.api.notifier.show({
message: this.api.i18n.t('Couldnt upload image. Please try another.'),
message: this.api.i18n.t('Couldn't upload image. Please try another.'),
style: 'error',
});
this.ui.hidePreloader();
Expand Down Expand Up @@ -485,9 +536,10 @@ export default class ImageTool implements BlockTool {
/**
* Wait until the API is ready
*/
Promise.resolve().then(() => {
this.block.stretched = value;
})
Promise.resolve()
.then(() => {
this.block.stretched = value;
})
.catch((err) => {
console.error(err);
});
Expand Down
77 changes: 58 additions & 19 deletions src/types/codexteam__ajax.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
/**
* Module declaration for '@codexteam/ajax'.
*/
declare module '@codexteam/ajax' {
declare module "@codexteam/ajax" {
/**
* Options for configuring an Ajax request.
* Options for configuring an AJAX request
* @typedef {Object} AjaxOptions
* @property {string} [url] - The target URL for the request
* @property {Object} [data] - Payload to send with the request
* @property {string} [accept] - MIME type to accept in response
* @property {Object} [headers] - Custom headers to include
* @property {function(File[]): void} [beforeSend] - Callback before sending files
* @property {string} [fieldName] - Form data field name for file uploads
* @property {string} [type] - HTTP method type (GET/POST/etc)
*/
export interface AjaxOptions {
/**
Expand Down Expand Up @@ -37,7 +45,9 @@ declare module '@codexteam/ajax' {
}

/**
* Parameter type of selectFiles function in AjaxOptions interface
* File selection options structure
* @typedef {Object} AjaxFileOptionsParam
* @property {string} accept - Allowed file types (e.g. 'image/*')
*/
export type AjaxFileOptionsParam = {
/**
Expand All @@ -47,35 +57,64 @@ declare module '@codexteam/ajax' {
};

/**
* Represents the response from an Ajax request.
* @template T - The type of the response body.
* AJAX response wrapper with typed body
* @template T - Type of the response body content
* @typedef {Object} AjaxResponse
* @property {number} status - HTTP status code
* @property {T} body - Parsed response content
*/
export interface AjaxResponse<T = object> {
/** The body of the response. */
export interface AjaxResponse<T = unknown> {
status: number;
body: T;
}

/**
* Prompts the user to select files and returns a promise that resolves with the selected files.
* @param options - Options for file selection.
* @param options.accept - The accepted file types.
* @returns A promise that resolves with the selected files.
* File selection handler
* @function selectFiles
* @param {AjaxFileOptionsParam} options - File type restrictions
* @returns {Promise<File[]>} Array of selected files
*/
export function selectFiles(options: AjaxFileOptionsParam): Promise<File[]>;

/**
* Sends an Ajax request with the specified options.
* @param options - Options for the Ajax request.
* @returns A promise that resolves with the Ajax response.
* Core transport method for AJAX requests
* @function transport
* @template T - Expected response body type
* @param {Object} options - Transport configuration
* @param {string} options.url - Endpoint URL
* @param {FormData|Record<string, unknown>} [options.data] - Request payload
* @param {string} [options.accept] - Response MIME type
* @param {Record<string, string>} [options.headers] - Request headers
* @param {function(File[]): void} [options.beforeSend] - File preprocessor
* @param {string} [options.fieldName] - Form field name
* @returns {Promise<AjaxResponse<T>>} Response wrapper
*/
export function transport(options: AjaxOptions): Promise<AjaxResponse>;
export function transport<T = unknown>(options: {
url: string;
data?: FormData | Record<string, unknown>;
accept?: string;
headers?: Record<string, string>;
beforeSend?: (files: File[]) => void;
fieldName?: string;
}): Promise<AjaxResponse<T>>;

/**
* Sends a POST request with the specified options.
* @param options - Options for the POST request.
* @returns A promise that resolves with the Ajax response.
* POST request shortcut
* @function post
* @template T - Expected response body type
* @param {Object} options - Request configuration
* @param {string} options.url - Target endpoint
* @param {FormData|Record<string, unknown>} options.data - POST payload
* @param {string} [options.type] - Content type header
* @param {Record<string, string>} [options.headers] - Custom headers
* @returns {Promise<AjaxResponse<T>>} Response wrapper
*/
export function post(options: AjaxOptions): Promise<AjaxResponse>;
export function post<T = unknown>(options: {
url: string;
data: FormData | Record<string, unknown>;
type?: string;
headers?: Record<string, string>;
}): Promise<AjaxResponse<T>>;

/**
* Represents common content types.
Expand Down
Loading