Compare commits

..

18 Commits

Author SHA1 Message Date
e6e67c2f47 fix version 2024-10-04 11:28:40 +03:00
9be52f1f9a update packages 2024-10-04 11:27:26 +03:00
cf5827bab1 update packages 2024-02-08 10:40:22 +03:00
36bdd23de4 update packages 2024-01-11 15:05:24 +03:00
146d14a1b8 find-filter delete char ? 2023-10-10 11:07:40 +03:00
acda0ff525 find filter helpers 2023-10-06 13:23:13 +03:00
17f05dd9e2 fix FindFilter 2023-10-06 12:43:46 +03:00
b1a4be4cf8 refactor pagination and FindFilter 2023-10-06 12:08:58 +03:00
31235b363c fix version 2023-10-05 17:03:38 +03:00
a68f3964e1 vite 2023-10-05 17:01:45 +03:00
5cc8f45f90 update version 2023-10-03 11:19:54 +03:00
55f9050dc9 notify 2023-10-03 11:18:30 +03:00
4e7d00db9b update zod 2023-10-03 11:18:08 +03:00
13e639fa8d select option type 2023-09-20 10:25:01 +03:00
20c21ea73d update ctrl 2023-09-19 22:43:11 +03:00
a6c220b499 merge obj 2023-09-19 20:42:13 +03:00
58770bf89f big fix 2023-09-14 18:19:36 +03:00
4bc155683b update packages 2023-08-17 09:20:12 +03:00
16 changed files with 305 additions and 105 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "axp-ts", "name": "axp-ts",
"version": "1.9.6", "version": "1.13.0",
"description": "TypeScript helper library", "description": "TypeScript helper library",
"author": "AntoXa PRO <info@antoxa.pro>", "author": "AntoXa PRO <info@antoxa.pro>",
"homepage": "https://antoxahub.ru/antoxa/axp-ts", "homepage": "https://antoxahub.ru/antoxa/axp-ts",
@@ -8,20 +8,36 @@
"type": "git", "type": "git",
"url": "https://antoxahub.ru/antoxa/axp-ts.git" "url": "https://antoxahub.ru/antoxa/axp-ts.git"
}, },
"main": "dist/index.js", "module": "./dist/index.mjs",
"main": "./dist/index.umd.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"requier": "./dist/index.umd.js"
},
"./dist/*": "./dist/*",
"./src/*": "./src/*",
"./package.json": "./package.json",
"./tsconfig.json": "./tsconfig.json"
},
"files": [ "files": [
"dist", "dist",
"tsconfig.ts" "src",
"tsconfig.json"
], ],
"scripts": { "scripts": {
"build": "tsc", "build": "vite build",
"prepare": "npm run build" "prepare": "npm run build"
}, },
"dependencies": { "peerDependencies": {
"zod": "^3.21.4" "zod": "^3.23.8"
}, },
"devDependencies": { "devDependencies": {
"prettier": "^2.8.8", "prettier": "^2.8.8",
"typescript": "^4.9.5" "typescript": "^5.6.2",
"vite": "^4.5.5",
"vite-plugin-dts": "^3.9.1"
} }
} }

View File

@@ -12,12 +12,17 @@ import {
} from './helpers' } from './helpers'
import { FormSchemaCtrl } from './ctrl' import { FormSchemaCtrl } from './ctrl'
export type TUiFieldSelectOption = {
text: string
value: string
}
/** /**
* Create field schema. * Create field schema.
*/ */
export const fieldSchema = <T extends ZodTypeAny>( export const fieldSchema = <T extends ZodTypeAny>(
base: T, base: T,
args?: TFormSchemaCtrlArgs args: TFormSchemaCtrlArgs = {}
) => base.describe(FormSchemaCtrl.toString(args, base.description)) ) => base.describe(FormSchemaCtrl.toString(args, base.description))
/** /**
@@ -36,8 +41,6 @@ export const bFieldsSchema = {
string: z string: z
.string() .string()
.trim() .trim()
.min(2)
.max(64)
.describe( .describe(
FormSchemaCtrl.toString({ FormSchemaCtrl.toString({
label: 'Строка', label: 'Строка',
@@ -79,16 +82,6 @@ export const cFieldsSchema = z.object({
dateUpdate: fieldSchema(bFieldsSchema.date, { dateUpdate: fieldSchema(bFieldsSchema.date, {
label: 'Дата изменения' label: 'Дата изменения'
}), }),
q: fieldSchema(
z.preprocess(
val => String(val).replace(regexSearch, ''),
bFieldsSchema.string
),
{
label: 'Поиск',
component: 'ui-field-search'
}
),
name: fieldSchema(bFieldsSchema.string, { name: fieldSchema(bFieldsSchema.string, {
label: 'Название' label: 'Название'
}), }),
@@ -187,5 +180,23 @@ export const cFieldsSchema = z.object({
days: fieldSchema(bFieldsSchema.number.array(), { days: fieldSchema(bFieldsSchema.number.array(), {
label: 'Дни недели', label: 'Дни недели',
component: 'ui-picker-days' component: 'ui-picker-days'
}) }),
q: fieldSchema(
z.preprocess(
val => String(val).replace(regexSearch, ''),
bFieldsSchema.string
),
{
label: 'Поиск',
component: 'ui-field-search'
}
),
page: fieldSchema(
z.preprocess(val => Math.abs(Number(val)), bFieldsSchema.number),
{ label: 'Страница' }
),
limit: fieldSchema(
z.preprocess(val => Math.abs(Number(val)), bFieldsSchema.number),
{ label: 'Лимит' }
)
}) })

View File

@@ -3,7 +3,6 @@ import type { TNotificationItem } from '../notification'
import { FormSchemaCtrl } from './ctrl' import { FormSchemaCtrl } from './ctrl'
/** /**
* Интерфейс базовой формы для сущностей в БД. * Интерфейс базовой формы для сущностей в БД.
*/ */
@@ -27,7 +26,7 @@ export interface IFormModel<T> {
/** /**
* Базовая модель для валидирования форм. * Базовая модель для валидирования форм.
*/ */
export class BaseFormModel<T extends Object = {}> implements IFormModel<T> { export class BaseFormModel<T extends object = {}> implements IFormModel<T> {
_id: string _id: string
dateCreate?: Date dateCreate?: Date
@@ -74,7 +73,7 @@ export class BaseFormModel<T extends Object = {}> implements IFormModel<T> {
let items: TNotificationItem[] = [] let items: TNotificationItem[] = []
for (const code in this._errors) { for (const code in this._errors) {
const text = this._errors[code] const text = this._errors[code]
items.push({ code, text }) if (text) items.push({ code, text })
} }
return items return items
} }
@@ -95,6 +94,17 @@ export class BaseFormModel<T extends Object = {}> implements IFormModel<T> {
} }
setValidError(code: string, text: string) { setValidError(code: string, text: string) {
this._errors[code] = code + ' - ' + text let obj:any = {}
obj[code] = code + ' - ' + text
this._errors = Object.assign(this._errors, obj)
}
mergeObj(obj: any) {
this.obj = Object.assign(this.obj, obj)
}
updateCtrl(key: keyof T, ctrl: Partial<FormSchemaCtrl>) {
const fieldIndex = this.ctrls.findIndex(e => e.key === key)
this.ctrls[fieldIndex] = Object.assign(this.ctrls[fieldIndex], ctrl)
} }
} }

View File

@@ -1,5 +1,5 @@
export * from './entities' export * from './utils'
export * from './form'
export * from './notification' export * from './notification'
export * from './pagination' export * from './entities'
export * from './data-result' export * from './requests'
export * from './forms'

View File

@@ -7,8 +7,8 @@ export class NotificationItem implements TNotificationItem {
code: string code: string
text: string text: string
constructor(args: {code: string, text: string }) { constructor(args: { text: string, code?: string }) {
this.code = args.code
this.text = args.text this.text = args.text
this.code = args.code || 'info'
} }
} }

View File

@@ -1,5 +1,5 @@
import type { TPagination } from './pagination' import type { TPagination } from './pagination'
import type { TNotificationItem } from './notification' import type { TNotificationItem } from '../notification'
import { Pagination } from './pagination' import { Pagination } from './pagination'
@@ -30,7 +30,7 @@ export class DataResultEntity<T> implements IDataResult<T> {
this.setData() this.setData()
} }
setData(data: T = null, pagination?: TPagination): void { setData(data: T | null = null, pagination?: TPagination): void {
if (data !== null) { if (data !== null) {
this.data = data this.data = data
} }

122
src/requests/find-filter.ts Normal file
View File

@@ -0,0 +1,122 @@
import type { TPaginationQuery } from './pagination'
import { z } from 'zod'
import { isEqual } from '../utils'
import { Pagination, paginationQuerySchema } from './pagination'
import { bFieldsSchema, cFieldsSchema, fieldSchema } from '../forms'
export const querySchema = cFieldsSchema
.pick({ q: true })
.extend(paginationQuerySchema.shape)
.extend({
sort: fieldSchema(bFieldsSchema.string.min(1).max(64), {
label: 'Сортировка'
})
})
.partial()
.describe('Параметры базового запроса')
export type TQuery = z.infer<typeof querySchema>
/**
* Класс для работы с запросами (для удобства).
*/
export type TFindFilter<T extends TQuery> = {
obj?: Omit<T, 'page' | 'limit' | 'sort'>
pagination?: TPaginationQuery
sort?: string
}
/**
* Класс для работы с запросами (для удобства).
*/
export class FindFilter<T extends TQuery> implements TFindFilter<T> {
obj?: Omit<T, 'page' | 'limit' | 'sort'>
pagination?: TPaginationQuery
sort?: string
static getQuery<T extends TQuery>(filter: TFindFilter<T>): T {
return Object.assign(
{ ...filter.obj, ...filter.pagination },
{ sort: filter.sort }
) as T
}
static parse<T extends TQuery>(filter: TFindFilter<T>): FindFilter<T> {
return new FindFilter<T>(this.getQuery(filter))
}
constructor(query?: T) {
// Copy fiends.
let queryCopy: T = Object.assign({}, query)
// Pagination.
this.pagination = new Pagination(queryCopy).getQuery()
// Delete pagination fields.
if (this.pagination as TPaginationQuery) {
const paginationKeys = Object.keys(this.pagination) as [keyof T]
for (const key of paginationKeys) {
if (queryCopy[key]) delete queryCopy[key]
}
}
// Sort.
if (queryCopy.sort) {
this.sort = queryCopy.sort
delete queryCopy.sort
}
// Obj.
this.obj = queryCopy
}
/**
* @deprecated.
*/
setPagination(pagination?: TPaginationQuery) {
this.pagination = new Pagination(pagination).getQuery()
}
toObject(): TFindFilter<T> {
return {
obj: this.obj,
pagination: this.pagination,
sort: this.sort
}
}
toString(opt?: { base?: string }): string {
let url = opt?.base?.replace(/[?]$/, '') || ''
const query: string[] = []
// Params and Pagination.
for (const [key, val] of Object.entries({
...this.obj,
...this.pagination
})) {
if (val) {
const keyEncode = encodeURIComponent(key)
const valEncode = encodeURIComponent(val)
query.push(keyEncode + '=' + valEncode)
}
}
// Sort.
if (this.sort) {
try {
query.push('sort=' + encodeURIComponent(this.sort))
} catch (e) {}
}
if (query.length) {
url += query.join('&')
}
return url
}
isEqual(filters: TFindFilter<T>[]) {
return isEqual([this, ...filters])
}
}

3
src/requests/index.ts Normal file
View File

@@ -0,0 +1,3 @@
export * from './pagination'
export * from './data-result'
export * from './find-filter'

View File

@@ -1,13 +1,15 @@
import { z } from 'zod' import { z } from 'zod'
import { cFieldsSchema, fieldSchema } from './form' import { cFieldsSchema, fieldSchema } from '../forms'
export const paginationSchema = z.object({ /**
page: fieldSchema(cFieldsSchema.shape.number, { * Модель данных для пагинайи.
label: 'Номер страницы' */
}), export const paginationSchema = cFieldsSchema
limit: fieldSchema(cFieldsSchema.shape.number, { .pick({
label: 'Лимит на странице' page: true,
}), limit: true
})
.extend({
total: fieldSchema(cFieldsSchema.shape.number, { total: fieldSchema(cFieldsSchema.shape.number, {
label: 'Общее кол-во' label: 'Общее кол-во'
}), }),
@@ -18,57 +20,48 @@ export const paginationSchema = z.object({
label: 'Кол-во всех страниц' label: 'Кол-во всех страниц'
}) })
}) })
.describe('Данные пагинации')
/**
* Модель данных для пагинайи.
*/
export type TPagination = z.infer<typeof paginationSchema> export type TPagination = z.infer<typeof paginationSchema>
// Константы. /**
* Модель данных для пагинайи для запросе.
*/
export const paginationQuerySchema = paginationSchema
.pick({
page: true,
limit: true
})
.partial()
.describe('Параметры разбиения на страницы')
/**
* Модель данных для пагинайи для запросе.
*/
export type TPaginationQuery = z.infer<typeof paginationQuerySchema>
//
// Переделать.
//
const DEFAULTS = { page: 1, limit: 10, maxLimit: 100 } const DEFAULTS = { page: 1, limit: 10, maxLimit: 100 }
export type TPaginationParseArg = number | string | undefined export type TPaginationParseArg = number | string | undefined
export type TPaginationArguments = { export type TPaginationArguments = {
page?: TPaginationParseArg page?: TPaginationParseArg
limit?: TPaginationParseArg limit?: TPaginationParseArg
total?: number total?: number
} }
export interface IPagination extends TPagination { /**
* @todo Переделать логику.
*/
export class Pagination implements TPagination {
/** /**
* Максимальный лимит элементов. * Максимальный лимит элементов.
*/ */
get maxLimit(): number #maxLimit: number
/**
* Парсинг аргументов.
* @param arg - Значение аргумента для парсинга.
* @param defaultReturnValue - Возвращаемое значение по умолчанию.
* @returns Возвращает абсолютное значение числа аргумента.
*/
parseArg(
arg: number | string | undefined,
defaultReturnValue: number
): number
/**
* Присваивает значения для основных свойств класса и считает кол-во
* пропускаемых элементов в зависимости от полученных аргументов.
* @param args - Аргументы пагинации.
* @returns Возвращает текущий экземпляр класса.
*/
set(args: TPaginationArguments): this
/**
* Возвращает простой объект пагинации.
* @returns Объект пагинации.
*/
toObject(): TPagination
}
export class Pagination implements IPagination {
/**
* Максимальный лимит элементов.
*/
private _maxLimit: number
page: number = DEFAULTS.page page: number = DEFAULTS.page
limit: number = DEFAULTS.limit limit: number = DEFAULTS.limit
@@ -89,14 +82,10 @@ export class Pagination implements IPagination {
} }
constructor(args: TPaginationArguments = {}, maxLimit?: number) { constructor(args: TPaginationArguments = {}, maxLimit?: number) {
this._maxLimit = this.parseArg(maxLimit, DEFAULTS.maxLimit) this.#maxLimit = this.parseArg(maxLimit, DEFAULTS.maxLimit)
this.set(args) this.set(args)
} }
get maxLimit() {
return this._maxLimit
}
parseArg(arg: TPaginationParseArg, defaultReturnValue: number): number { parseArg(arg: TPaginationParseArg, defaultReturnValue: number): number {
return Pagination.parseArg(arg, defaultReturnValue) return Pagination.parseArg(arg, defaultReturnValue)
} }
@@ -118,7 +107,7 @@ export class Pagination implements IPagination {
} }
// Проверка лимита. // Проверка лимита.
if (this.limit > this.maxLimit) this.limit = this.maxLimit if (this.limit > this.#maxLimit) this.limit = this.#maxLimit
// Общее кол-во. // Общее кол-во.
if (args.total && args.total !== this.total) { if (args.total && args.total !== this.total) {
@@ -143,9 +132,12 @@ export class Pagination implements IPagination {
page: this.page, page: this.page,
limit: this.limit, limit: this.limit,
total: this.total, total: this.total,
skip: this.skip, skip: this.skip,
pages: this.pages pages: this.pages
} }
} }
getQuery(): TPaginationQuery {
return { page: this.page, limit: this.limit }
}
} }

1
src/utils/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from './objects'

30
src/utils/objects.ts Normal file
View File

@@ -0,0 +1,30 @@
export const isEqual = <T extends object>(objects: T[]) => {
for (let i = 0; i < objects.length; i++) {
const obj1: T = objects[i]
const obj2: T = objects[i + 1]
if (obj2) {
const keys1 = Object.keys(obj1) as [keyof T]
const keys2 = Object.keys(obj2) as [keyof T]
if (keys1.length !== keys2.length) {
return false
}
for(const key of keys1) {
if (typeof obj1[key] !== 'object') {
if (obj1[key] !== obj2[key]) {
return false
}
} else {
if (!isEqual([obj1[key], obj2[key]] as T[])) {
return false
}
}
}
}
}
return true
}

View File

@@ -1,13 +1,15 @@
{ {
"compilerOptions": { "compilerOptions": {
"rootDir": "src", "target": "ESNext",
"outDir": "dist", "module": "ESNext",
"target": "ES6",
"module": "CommonJS", "strict": true,
"moduleResolution": "node", "moduleResolution": "Node",
"lib": ["es2017"], "isolatedModules": true,
"declaration": true, "esModuleInterop": true,
"sourceMap": true "useDefineForClassFields": true,
"declaration": true
}, },
"exclude": ["node_modules", "dist"] "include": ["src/**/*.ts", "src/**/*.d.ts"]
} }

13
vite.config.ts Normal file
View File

@@ -0,0 +1,13 @@
import { defineConfig } from 'vite'
import dts from 'vite-plugin-dts'
export default defineConfig({
plugins: [dts()],
build: {
lib: {
entry: 'src/index.ts',
name: 'axp-ts',
fileName: 'index'
}
}
})