Просмотр исходного кода

Merge branch 'release/1.0.6' into develop

lfabl 1 неделя назад
Родитель
Сommit
fbf09de7da

+ 0 - 20
.eslintrc.json

@@ -1,20 +0,0 @@
-{
-    "parser": "@typescript-eslint/parser",
-    "env": {
-      "browser": true,
-      "es6": true
-    },
-    "parserOptions": {
-      "ecmaFeatures": {
-        "jsx": true
-      },
-      "ecmaVersion": 2018,
-      "sourceType": "module"
-    },
-    "rules": {
-      "react/react-in-jsx-scope": "off",
-      "object-curly-newline": ["error", "always"],
-      "indent": ["error", 4],
-      "semi": "error"
-    }
-}

+ 2 - 0
.gitignore

@@ -6,3 +6,5 @@ build-storybook.log
 /.history
 /docs
 /docs-build
+
+*storybook.log

+ 0 - 1
.husky/.gitignore

@@ -1 +0,0 @@
-_

+ 1 - 3
.npmignore

@@ -10,11 +10,9 @@ docs
 .changeset
 __mocks__
 stories
-.eslintrc.js
+eslint.config.mjs
 .eslintignore
 .gitignore
 .prettierrc.js
-jest-setup.ts
-jest.config.js
 docs-build
 /.history

+ 22 - 13
.storybook/main.js

@@ -1,14 +1,23 @@
-const path = require('path');
-module.exports = {
-  stories: [
-    "../src/**/*.stories.@(js|jsx|ts|tsx)",
-    "../stories/**/*.stories.mdx",
-  ],
-  addons: [
-    "@storybook/addon-links",
-    "@storybook/addon-essentials",
-    "@storybook/addon-a11y",
-    "storybook-addon-styled-component-theme/dist/preset",
-    "@whitespace/storybook-addon-html"
-  ]
+/** @type { import('@storybook/react-webpack5').StorybookConfig } */
+const config = {
+    "stories": [
+        "../stories/**/*.mdx",
+        "../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)",
+        "../src/**/*.mdx",
+        "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"
+    ],
+    "addons": [
+        "@storybook/addon-links",
+        "@storybook/addon-webpack5-compiler-swc",
+        "@storybook/addon-a11y",
+        "@storybook/addon-docs"
+    ],
+    "framework": "@storybook/react-webpack5",
+    staticDirs: [
+        "./"
+    ],
+    features: {
+        onboarding: false
+    }
 };
+export default config;

+ 18 - 0
.storybook/manager-head.html

@@ -0,0 +1,18 @@
+<link rel="shortcut icon" href="./logo.ico" />
+<style>
+    #storybook-checklist-widget {
+        display: none !important;
+    }
+
+    div:has(> div >#storybook-checklist-widget) {
+        display: none !important;
+    }
+
+    button[aria-label="Settings"][role="switch"] {
+        display: none !important;
+    }
+
+    #sidebar-bottom-wrapper {
+        display: none !important
+    }
+</style>

+ 2 - 8
.storybook/manager.js

@@ -1,14 +1,8 @@
 import {
     addons
-} from '@storybook/addons';
-import storybookTheme from './storybookTheme';
-import favicon from './logo.ico';
+} from "storybook/manager-api";
+import storybookTheme from "./storybookTheme";
 
 addons.setConfig({
     theme: storybookTheme
 });
-
-const link = document.createElement('link');
-link.setAttribute('rel', 'shortcut icon');
-link.setAttribute('href', favicon);
-document.head.appendChild(link);

+ 0 - 37
.storybook/preview.js

@@ -1,37 +0,0 @@
-export const parameters = {
-    actions: { argTypesRegex: '^on[A-Z].*' },
-    controls: {
-        matchers: {
-            color: /(background|color)$/i,
-            date: /Date$/,
-        },
-    },
-    options: {
-        storySort: {
-            order: [
-                'Docs',
-                [
-                    'Intro',
-                    'Getting Started',
-                    [
-                        'Quick Start',
-                        'Schemes',
-                        'Configs'
-                    ]
-                ],
-                'Components',
-                [
-                    'Text',
-                    'Button',
-                    'TextInput',
-                    '*'
-                ],
-                '*'
-            ]
-        }
-    }
-};
-
-export const decorators = [
-    (Story) => <Story/>
-];

+ 29 - 0
.storybook/preview.jsx

@@ -0,0 +1,29 @@
+import React from "react";
+
+/** @type { import('@storybook/react-webpack5').Preview } */
+const preview = {
+    parameters: {
+        controls: {
+            matchers: {
+              color: /(background|color)$/i,
+              date: /Date$/i
+            }
+        },
+        options: {
+            storySort: {
+                order: [
+                    "Docs",
+                    [
+                        "Intro",
+                        "Getting Started"
+                    ],
+                    "*"
+                ]
+            }
+        }
+    },
+    decorators: [
+        (Story) => <Story/>
+    ]
+};
+export default preview;

+ 6 - 6
.storybook/storybookTheme.js

@@ -1,11 +1,11 @@
 import {
     create
-} from '@storybook/theming';
+} from "storybook/theming";
 
 export default create({
-    base: 'light',
-    brandTitle: 'NCore Web',
-    brandUrl: 'https://ncore.nibgat.space/web',
-    brandImage: 'https://ncore.nibgat.space/assets/images/ncoreweb.png',
-    brandTarget: '_self',
+    base: "light",
+    brandTitle: "NCore Web",
+    brandUrl: "https://ncore.nibgat.space",
+    brandImage: "https://ncore.nibgat.space/assets/images/ndscl.png",
+    brandTarget: "_self",
 });

+ 45 - 6
.vscode/settings.json

@@ -1,11 +1,50 @@
 {
-    "editor.formatOnSave": false,
+    "editor.formatOnSave": true,
+    "editor.defaultFormatter": "dbaeumer.vscode-eslint",
     "editor.codeActionsOnSave": {
-      "source.fixAll.eslint": "explicit"
+        "source.fixAll.eslint": "always"
     },
-    "eslint.options": {
-      "extensions": [".html", ".js", ".jsx", ".ts", ".tsx"]
+    "eslint.probe": [
+        "javascript",
+        "javascriptreact",
+        "typescript",
+        "typescriptreact",
+        "json"
+    ],
+    "eslint.validate": [
+        "javascript",
+        "javascriptreact",
+        "typescript",
+        "typescriptreact",
+        "json"
+    ],
+    "eslint.run": "onType",
+    "eslint.workingDirectories": [
+        {
+            "mode": "auto"
+        },
+        {
+            "pattern": "./src"
+        }
+    ],
+    "editor.tabSize": 4,
+    "editor.insertSpaces": true,
+    "files.eol": "\n",
+    "files.trimTrailingWhitespace": true,
+    "files.insertFinalNewline": true,
+    "files.trimFinalNewlines": true,
+    "eslint.lintTask.enable": true,
+    "eslint.format.enable": true,
+    "[typescript]": {
+        "editor.defaultFormatter": "dbaeumer.vscode-eslint"
     },
-    "eslint.validate": ["javascript", "typescript", "html"],
-    "files.eol": "auto"
+    "[typescriptreact]": {
+        "editor.defaultFormatter": "dbaeumer.vscode-eslint"
+    },
+    "[json]": {
+        "editor.defaultFormatter": "dbaeumer.vscode-eslint"
+    },
+    "[jsonc]": {
+        "editor.defaultFormatter": "dbaeumer.vscode-eslint"
+    }
 }

+ 0 - 1
TODO

@@ -1 +0,0 @@
-All is done.

+ 454 - 0
eslint-local-rules/index.js

@@ -0,0 +1,454 @@
+module.exports = {
+    "multiline-import-specifiers": {
+        meta: {
+            type: "layout",
+            fixable: "whitespace",
+            schema: []
+        },
+        create(context) {
+            return {
+                ImportDeclaration(node) {
+                    const specifiers = node.specifiers.filter(
+                        s => s.type === "ImportSpecifier"
+                    );
+
+                    if (specifiers.length <= 1) return;
+
+                    const sourceCode = context.getSourceCode();
+
+                    for (let i = 0; i < specifiers.length - 1; i++) {
+                        const current = specifiers[i];
+                        const next = specifiers[i + 1];
+
+                        if (current.loc.end.line === next.loc.start.line) {
+                            context.report({
+                                node: next,
+                                message: "Each import specifier should be on a new line",
+                                fix(fixer) {
+                                    const comma = sourceCode.getTokenBefore(next);
+                                    return fixer.replaceTextRange(
+                                        [
+                                            comma.range[1],
+                                            next.range[0]
+                                        ],
+                                        "\n    "
+                                    );
+                                }
+                            });
+                        }
+                    }
+
+                    const lastSpecifier = specifiers[specifiers.length - 1];
+                    const tokenAfter = sourceCode.getTokenAfter(lastSpecifier);
+
+                    if (tokenAfter && tokenAfter.value === ",") {
+                        context.report({
+                            node: lastSpecifier,
+                            message: "No trailing comma in imports",
+                            fix(fixer) {
+                                return fixer.remove(tokenAfter);
+                            }
+                        });
+                    }
+                }
+            };
+        }
+    },
+    "multiline-object-properties": {
+        meta: {
+            type: "layout",
+            fixable: "whitespace",
+            schema: []
+        },
+        create(context) {
+            const sourceCode = context.getSourceCode();
+
+            function checkProperties(node, properties) {
+                if (properties.length <= 1) return;
+
+                for (let i = 0; i < properties.length - 1; i++) {
+                    const current = properties[i];
+                    const next = properties[i + 1];
+
+                    if (current.loc.end.line === next.loc.start.line) {
+                        context.report({
+                            node: next,
+                            message: "Each property should be on a new line",
+                            fix(fixer) {
+                                const openBrace = sourceCode.getFirstToken(node);
+                                const openBraceLine = sourceCode.lines[openBrace.loc.start.line - 1];
+                                const baseIndent = openBraceLine.match(/^\s*/)[0];
+                                const indent = baseIndent + "    ";
+
+                                const comma = sourceCode.getTokenBefore(next);
+
+                                return fixer.replaceTextRange(
+                                    [
+                                        comma.range[1],
+                                        next.range[0]
+                                    ],
+                                    "\n" + indent
+                                );
+                            }
+                        });
+                    }
+                }
+
+                const lastProperty = properties[properties.length - 1];
+                const tokenAfter = sourceCode.getTokenAfter(lastProperty);
+
+                if (tokenAfter && tokenAfter.value === ",") {
+                    context.report({
+                        node: lastProperty,
+                        message: "No trailing comma in objects",
+                        fix(fixer) {
+                            return fixer.remove(tokenAfter);
+                        }
+                    });
+                }
+            }
+
+            return {
+                ObjectExpression(node) {
+                    checkProperties(node, node.properties);
+                },
+
+                ObjectPattern(node) {
+                    checkProperties(node, node.properties);
+                }
+            };
+        }
+    },
+    "multiline-jsx-attributes": {
+        meta: {
+            type: "layout",
+            fixable: "whitespace",
+            schema: []
+        },
+        create(context) {
+            const sourceCode = context.getSourceCode();
+
+            return {
+                JSXOpeningElement(node) {
+                    if (node.attributes.length <= 1) return;
+
+                    const firstToken = sourceCode.getFirstToken(node);
+                    const tagNameToken = sourceCode.getTokenAfter(firstToken);
+                    const firstAttr = node.attributes[0];
+
+                    if (tagNameToken.loc.end.line === firstAttr.loc.start.line) {
+                        context.report({
+                            node: firstAttr,
+                            message: "First JSX attribute should be on a new line",
+                            fix(fixer) {
+                                const openBraceLine = sourceCode.lines[firstToken.loc.start.line - 1];
+                                const baseIndent = openBraceLine.match(/^\s*/)[0];
+                                const indent = baseIndent + "    ";
+
+                                return fixer.replaceTextRange(
+                                    [
+                                        tagNameToken.range[1],
+                                        firstAttr.range[0]
+                                    ],
+                                    "\n" + indent
+                                );
+                            }
+                        });
+                    }
+
+                    for (let i = 0; i < node.attributes.length - 1; i++) {
+                        const current = node.attributes[i];
+                        const next = node.attributes[i + 1];
+
+                        if (current.loc.end.line === next.loc.start.line) {
+                            context.report({
+                                node: next,
+                                message: "Each JSX attribute should be on a new line",
+                                fix(fixer) {
+                                    const openBraceLine = sourceCode.lines[firstToken.loc.start.line - 1];
+                                    const baseIndent = openBraceLine.match(/^\s*/)[0];
+                                    const indent = baseIndent + "    ";
+
+                                    return fixer.replaceTextRange(
+                                        [
+                                            current.range[1],
+                                            next.range[0]
+                                        ],
+                                        "\n" + indent
+                                    );
+                                }
+                            });
+                        }
+                    }
+
+                    const lastAttr = node.attributes[node.attributes.length - 1];
+
+                    const allTokens = [];
+                    let currentToken = sourceCode.getTokenAfter(lastAttr);
+
+                    while (currentToken && currentToken.range[1] <= node.range[1]) {
+                        allTokens.push(currentToken);
+                        currentToken = sourceCode.getTokenAfter(currentToken);
+                    }
+
+                    const closingBracket = allTokens[allTokens.length - 1];
+
+                    if (lastAttr.loc.end.line === closingBracket.loc.start.line) {
+                        context.report({
+                            node: closingBracket,
+                            message: "JSX closing bracket should be on a new line",
+                            fix(fixer) {
+                                const openBraceLine = sourceCode.lines[firstToken.loc.start.line - 1];
+                                const baseIndent = openBraceLine.match(/^\s*/)[0];
+
+                                if (node.selfClosing && allTokens.length >= 2) {
+                                    const slashToken = allTokens[allTokens.length - 2];
+
+                                    if (slashToken && slashToken.value === "/") {
+                                        return fixer.replaceTextRange(
+                                            [
+                                                lastAttr.range[1],
+                                                slashToken.range[0]
+                                            ],
+                                            "\n" + baseIndent
+                                        );
+                                    }
+                                }
+
+                                return fixer.replaceTextRange(
+                                    [
+                                        lastAttr.range[1],
+                                        closingBracket.range[0]
+                                    ],
+                                    "\n" + baseIndent
+                                );
+                            }
+                        });
+                    }
+                }
+            };
+        }
+    },
+    "multiline-array-elements": {
+        meta: {
+            type: "layout",
+            fixable: "whitespace",
+            schema: []
+        },
+        create(context) {
+            const sourceCode = context.getSourceCode();
+
+            return {
+                ArrayExpression(node) {
+                    if (node.elements.length <= 1) return;
+
+                    const openBracket = sourceCode.getFirstToken(node);
+                    const firstElement = node.elements[0];
+
+                    if (openBracket.loc.end.line === firstElement.loc.start.line) {
+                        context.report({
+                            node: firstElement,
+                            message: "First array element should be on a new line",
+                            fix(fixer) {
+                                const openBracketLine = sourceCode.lines[openBracket.loc.start.line - 1];
+                                const baseIndent = openBracketLine.match(/^\s*/)[0];
+                                const indent = baseIndent + "    ";
+
+                                return fixer.replaceTextRange(
+                                    [
+                                        openBracket.range[1],
+                                        firstElement.range[0]
+                                    ],
+                                    "\n" + indent
+                                );
+                            }
+                        });
+                    }
+
+                    for (let i = 0; i < node.elements.length - 1; i++) {
+                        const current = node.elements[i];
+                        const next = node.elements[i + 1];
+
+                        if (!current || !next) continue;
+
+                        if (current.loc.end.line === next.loc.start.line) {
+                            context.report({
+                                node: next,
+                                message: "Each array element should be on a new line",
+                                fix(fixer) {
+                                    const openBracketLine = sourceCode.lines[openBracket.loc.start.line - 1];
+                                    const baseIndent = openBracketLine.match(/^\s*/)[0];
+                                    const indent = baseIndent + "    ";
+
+                                    const comma = sourceCode.getTokenBefore(next);
+
+                                    return fixer.replaceTextRange(
+                                        [
+                                            comma.range[1],
+                                            next.range[0]
+                                        ],
+                                        "\n" + indent
+                                    );
+                                }
+                            });
+                        }
+                    }
+
+                    const lastElement = node.elements[node.elements.length - 1];
+                    const closingBracket = sourceCode.getLastToken(node);
+
+                    if (lastElement && lastElement.loc.end.line === closingBracket.loc.start.line) {
+                        context.report({
+                            node: closingBracket,
+                            message: "Array closing bracket should be on a new line",
+                            fix(fixer) {
+                                const openBracketLine = sourceCode.lines[openBracket.loc.start.line - 1];
+                                const baseIndent = openBracketLine.match(/^\s*/)[0];
+
+                                return fixer.replaceTextRange(
+                                    [
+                                        lastElement.range[1],
+                                        closingBracket.range[0]
+                                    ],
+                                    "\n" + baseIndent
+                                );
+                            }
+                        });
+                    }
+
+                    const tokenAfter = sourceCode.getTokenAfter(lastElement);
+
+                    if (tokenAfter && tokenAfter.value === "," && tokenAfter.range[1] <= closingBracket.range[0]) {
+                        context.report({
+                            node: lastElement,
+                            message: "No trailing comma in arrays",
+                            fix(fixer) {
+                                return fixer.remove(tokenAfter);
+                            }
+                        });
+                    }
+                }
+            };
+        }
+    },
+    "custom-import-order": {
+        meta: {
+            type: "suggestion",
+            fixable: "code",
+            schema: []
+        },
+        create(context) {
+            const sourceCode = context.getSourceCode();
+            const imports = [];
+
+            return {
+                ImportDeclaration(node) {
+                    imports.push(node);
+                },
+
+                "Program:exit"() {
+                    if (imports.length <= 1) return;
+
+                    const categorized = categorizeImports(imports, sourceCode);
+
+                    for (let i = 0; i < imports.length - 1; i++) {
+                        const current = imports[i];
+                        const next = imports[i + 1];
+
+                        const currentCategory = getImportCategory(current, sourceCode);
+                        const nextCategory = getImportCategory(next, sourceCode);
+
+                        if (currentCategory > nextCategory) {
+                            context.report({
+                                node: next,
+                                message: `Import from "${next.source.value}" should come before "${current.source.value}"`,
+                                fix(fixer) {
+                                    return fixImportOrder(fixer, imports, categorized, sourceCode);
+                                }
+                            });
+                            break;
+                        }
+                    }
+                }
+            };
+
+            function getImportCategory(node, sourceCode) {
+                const source = node.source.value;
+                const code = sourceCode.getText(node);
+
+                if (source === "react") return 1;
+
+                if (source === "react-native") return 2;
+
+                if (source.startsWith("./")) {
+                    if (code.includes("import type") || source.includes(".types") || source.includes("/type")) {
+                        return 3.1;
+                    }
+
+                    if (source.includes("style") || source.includes("Style")) {
+                        return 3.2;
+                    }
+
+                    return 3.3;
+                }
+
+                const specifiers = node.specifiers
+                    .filter(s => s.type === "ImportSpecifier")
+                    .map(s => s.imported ? s.imported.name : "");
+
+                const hasNCoreHook = specifiers.some(name => name.startsWith("NCore"));
+                const hasUseHook = specifiers.some(name => name.startsWith("use"));
+
+                if (hasNCoreHook) return 4.1;
+                if (hasUseHook) return 4.2;
+
+                if (code.includes("import type") || source.includes("/types/") || source.includes("/type") || source.includes(".types")) {
+                    return 5;
+                }
+
+                if (source.startsWith("@/") || source.startsWith("~/")) return 6.3;
+                if (!source.startsWith(".")) return 6.1;
+                if (source.startsWith("../")) return 6.2;
+
+                return 6.4;
+            }
+
+            function categorizeImports(imports, sourceCode) {
+                return imports.map(imp => ({
+                    node: imp,
+                    category: getImportCategory(imp, sourceCode),
+                    source: imp.source.value
+                })).sort((a, b) => {
+                    if (a.category !== b.category) {
+                        return a.category - b.category;
+                    }
+
+                    return a.source.localeCompare(b.source);
+                });
+            }
+
+            function fixImportOrder(fixer, imports, categorized, sourceCode) {
+                const firstImport = imports[0];
+                const lastImport = imports[imports.length - 1];
+
+                const rangeStart = firstImport.range[0];
+                const rangeEnd = lastImport.range[1];
+
+                let newImports = "";
+
+                categorized.forEach((imp, index) => {
+                    newImports += sourceCode.getText(imp.node);
+
+                    if (index < categorized.length - 1) {
+                        newImports += "\n";
+                    }
+                });
+
+                return fixer.replaceTextRange([
+                    rangeStart,
+                    rangeEnd
+                ], newImports);
+            }
+        }
+    }
+};

+ 226 - 0
eslint.config.mjs

@@ -0,0 +1,226 @@
+// For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format
+import storybook from "eslint-plugin-storybook";
+
+import localRulesPlugin from "eslint-plugin-local-rules";
+import reactHooks from "eslint-plugin-react-hooks";
+import importPlugin from "eslint-plugin-import";
+import jsoncParser from "jsonc-eslint-parser";
+import jsxA11y from "eslint-plugin-jsx-a11y";
+import tseslint from "typescript-eslint";
+import react from "eslint-plugin-react";
+import jsonc from "eslint-plugin-jsonc";
+import globals from "globals";
+import js from "@eslint/js";
+
+export default tseslint.config({
+    ignores: [
+        "node_modules",
+        "dist",
+        "lib",
+        ".history",
+        "version.mjs"
+    ]
+}, js.configs.recommended, ...tseslint.configs.recommended, {
+    files: [
+        "**/*.{ts,tsx,js,jsx}"
+    ],
+    plugins: {
+        react,
+        "react-hooks": reactHooks,
+        "jsx-a11y": jsxA11y,
+        import: importPlugin,
+        "local-rules": {
+            rules: localRulesPlugin.rules
+        }
+    },
+    languageOptions: {
+        ecmaVersion: "latest",
+        sourceType: "module",
+        parser: tseslint.parser,
+        parserOptions: {
+            ecmaFeatures: {
+                jsx: true
+            }
+        },
+        globals: {
+            "clearInterval": "readonly",
+            "clearTimeout": "readonly",
+            "setInterval": "readonly",
+            "setTimeout": "readonly",
+            "FormData": "readonly",
+            "console": "readonly",
+            "__DEV__": "readonly",
+            "fetch": "readonly"
+        }
+    },
+    settings: {
+        react: {
+            version: "detect"
+        },
+        "import/resolver": {
+            typescript: {
+                alwaysTryTypes: true,
+                project: "./tsconfig.json"
+            }
+        }
+    },
+    rules: {
+        "local-rules/multiline-import-specifiers": "error",
+        "local-rules/multiline-object-properties": "error",
+        "local-rules/multiline-jsx-attributes": "error",
+        "local-rules/multiline-array-elements": "error",
+        "local-rules/custom-import-order": "error",
+        "@typescript-eslint/no-namespace": "off",
+        "quotes": [
+            "error",
+            "double",
+            {
+                "avoidEscape": true
+            }
+        ],
+        "semi": [
+            "error",
+            "always"
+        ],
+        "no-var": "error",
+        "prefer-const": "error",
+        "comma-spacing": [
+            "error",
+            {
+                "before": false,
+                "after": true
+            }
+        ],
+        "object-curly-spacing": [
+            "error",
+            "always"
+        ],
+        "indent": [
+            "error",
+            4,
+            {
+                "SwitchCase": 1
+            }
+        ],
+        "no-trailing-spaces": [
+            "error",
+            {
+                "skipBlankLines": false
+            }
+        ],
+        "@typescript-eslint/no-empty-object-type": [
+            "error",
+            {
+                "allowInterfaces": "with-single-extends"
+            }
+        ],
+        "eol-last": [
+            "error",
+            "always"
+        ],
+        "object-curly-newline": [
+            "error",
+            {
+                "ObjectExpression": {
+                    "minProperties": 1,
+                    "consistent": true,
+                    "multiline": true
+                },
+                "ObjectPattern": {
+                    "minProperties": 1,
+                    "consistent": true,
+                    "multiline": true
+                },
+                "ImportDeclaration": {
+                    "minProperties": 1,
+                    "consistent": true,
+                    "multiline": true
+                },
+                "ExportDeclaration": {
+                    "minProperties": 1,
+                    "consistent": true,
+                    "multiline": true
+                }
+            }
+        ],
+        "object-property-newline": [
+            "error",
+            {
+                "allowAllPropertiesOnSameLine": false
+            }
+        ],
+        "array-element-newline": [
+            "error",
+            {
+                "multiline": true,
+                "minItems": 1
+            }
+        ],
+        "array-bracket-newline": [
+            "error",
+            "consistent"
+        ],
+        "react/jsx-indent": [
+            "error",
+            4
+        ],
+        "react/jsx-indent-props": [
+            "error",
+            4
+        ],
+        "react/react-in-jsx-scope": "off"
+    }
+}, {
+    files: [
+        "eslint-local-rules/**/*.js",
+        "babel.config.js",
+        "**/*.config.mjs",
+        "**/*.config.js"
+    ],
+    languageOptions: {
+        globals: {
+            ...globals.node
+        }
+    }
+}, {
+    files: [
+        "**/*.json"
+    ],
+    plugins: {
+        jsonc
+    },
+    languageOptions: {
+        parser: jsoncParser
+    },
+    rules: {
+        "jsonc/indent": [
+            "error",
+            4
+        ],
+        "jsonc/object-property-newline": [
+            "error",
+            {
+                "allowAllPropertiesOnSameLine": false
+            }
+        ],
+        "jsonc/object-curly-newline": [
+            "error",
+            {
+                "minProperties": 0,
+                "consistent": true,
+                "multiline": true
+            }
+        ],
+        "jsonc/array-element-newline": [
+            "error",
+            {
+                "multiline": true,
+                "minItems": 1
+            }
+        ],
+        "jsonc/array-bracket-newline": [
+            "error",
+            "always"
+        ]
+    }
+}, storybook.configs["flat/recommended"]);

+ 0 - 1
jest-setup.ts

@@ -1 +0,0 @@
-import '@testing-library/jest-dom';

+ 0 - 9
jest.config.js

@@ -1,9 +0,0 @@
-module.exports = {
-    preset: "ts-jest",
-    testEnvironment: "jsdom",
-    moduleNameMapper: {
-        "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
-        "\\.(css|less|scss)$": "identity-obj-proxy",
-    },
-    setupFilesAfterEnv: ["<rootDir>/jest-setup.ts"],
-};

+ 38 - 44
package.json

@@ -1,25 +1,21 @@
 {
     "name": "ncore-context",
-    "version": "1.0.5",
+    "version": "1.0.6",
     "description": "NİBGAT® | NCore - Context",
-    "main": "lib/index.js",
-    "module": "lib/index.js",
+    "main": "./lib/index.js",
+    "module": "./lib/index.js",
+    "types": "./lib/index.d.ts",
     "react-native": "src/index",
     "source": "src/index",
     "scripts": {
-        "test": "jest",
-        "test:watch": "jest --watch",
-        "storybook": "cross-env NODE_OPTIONS='--openssl-legacy-provider' start-storybook -p 6006",
-        "build-storybook": "build-storybook -o docs-build -s ./stories/assets",
-        "chromatic": "chromatic --exit-zero-on-changes",
+        "storybook": "storybook dev -p 6006",
+        "build-storybook": "storybook build",
         "lint": "eslint --fix '*/**/*.{js,ts,tsx}'",
-        "prepare": "husky install",
-        "release": "npm run build && npm run build-storybook && npm publish",
-        "build": "rollup -c"
+        "build": "yarn tsc"
     },
     "repository": {
         "type": "git",
-        "url": "git+https://github.com/nibgat/ncore-context.git"
+        "url": "git+https://git.nibgat.space/nibgat/ncore-context.git"
     },
     "keywords": [
         "ncore",
@@ -27,6 +23,7 @@
         "ncore-web",
         "ncore-mobile",
         "mobile",
+        "ncore-ui-kit",
         "nibgat",
         "react",
         "library",
@@ -40,60 +37,57 @@
     "author": "nibgat",
     "license": "MIT",
     "bugs": {
-        "url": "https://github.com/nibgat/ncore-context/issues"
+        "url": "https://git.nibgat.space/nibgat/ncore-context/issues"
     },
-    "homepage": "https://github.com/nibgat/ncore-context#readme",
+    "homepage": "https://git.nibgat.space/nibgat/ncore-context#readme",
     "devDependencies": {
         "@babel/core": "7.14.2",
         "@changesets/cli": "2.16.0",
+        "@react-native/eslint-config": "0.85.3",
         "@rollup/plugin-commonjs": "19.0.0",
         "@rollup/plugin-node-resolve": "15.2.3",
-        "@storybook/addon-a11y": "6.2.9",
-        "@storybook/addon-actions": "6.2.9",
-        "@storybook/addon-docs": "6.5.9",
-        "@storybook/addon-essentials": "6.2.9",
-        "@storybook/addon-links": "6.2.9",
-        "@storybook/react": "6.2.9",
-        "@storybook/storybook-deployer": "2.8.8",
-        "@storybook/theming": "6.5.9",
-        "@testing-library/jest-dom": "5.12.0",
+        "@storybook/addon-a11y": "10.4.4",
+        "@storybook/addon-docs": "10.4.4",
+        "@storybook/addon-links": "10.4.4",
+        "@storybook/addon-webpack5-compiler-swc": "4.0.3",
+        "@storybook/react-webpack5": "10.4.4",
         "@testing-library/react": "11.2.7",
         "@testing-library/user-event": "13.1.9",
-        "@types/jest": "26.0.23",
+        "@types/eslint-plugin-jsx-a11y": "6",
         "@types/styled-components": "5.1.9",
         "@types/styled-system": "5.1.11",
-        "@typescript-eslint/eslint-plugin": "4.24.0",
-        "@typescript-eslint/parser": "5.30.3",
-        "@whitespace/storybook-addon-html": "5.0.0",
         "babel-loader": "8.2.2",
         "chromatic": "5.8.0",
         "cross-env": "7.0.3",
-        "eslint": "7.26.0",
-        "eslint-config-airbnb": "18.2.1",
-        "eslint-plugin-import": "2.23.0",
-        "eslint-plugin-jsx-a11y": "6.4.1",
-        "eslint-plugin-react": "7.23.2",
-        "eslint-plugin-react-hooks": "4.2.0",
+        "eslint": "9.37.0",
+        "eslint-import-resolver-typescript": "4.4.4",
+        "eslint-plugin-import": "2.32.0",
+        "eslint-plugin-jsonc": "2.21.0",
+        "eslint-plugin-jsx-a11y": "6.10.2",
+        "eslint-plugin-local-rules": "3.0.2",
+        "eslint-plugin-react": "7.37.5",
+        "eslint-plugin-react-hooks": "7.0.0",
+        "eslint-plugin-react-native": "5.0.0",
+        "eslint-plugin-storybook": "10.4.4",
         "eslint-plugin-testing-library": "4.4.0",
         "husky": "6.0.0",
         "identity-obj-proxy": "3.0.0",
-        "jest": "26.6.3",
+        "jsonc-eslint-parser": "2.4.1",
         "prop-types": "15.7.2",
-        "react": "17.0.2",
-        "react-dom": "17.0.2",
+        "react": "19.2.3",
+        "react-dom": "19.2.3",
         "rollup": "4.17.2",
         "rollup-plugin-cleaner": "1.0.0",
         "rollup-plugin-peer-deps-external": "2.2.4",
-        "rollup-plugin-typescript2": "0.31.0",
-        "storybook-addon-styled-component-theme": "2.0.0",
+        "rollup-plugin-typescript2": "^0.37.0",
+        "storybook": "10.4.4",
         "styled-components": "5.3.0",
         "styled-system": "5.1.5",
-        "ts-jest": "26.5.6",
-        "typescript": "4.2.4"
+        "typescript": "5.9.3",
+        "typescript-eslint": "8.46.1"
     },
     "peerDependencies": {
-        "react": ">=17.0.1",
-        "react-dom": ">=17.0.1"
-    },
-    "dependencies": {}
+        "react": ">= 19.2.3",
+        "react-dom": ">= 19.2.3"
+    }
 }

+ 0 - 33
rollup.config.js

@@ -1,33 +0,0 @@
-const typescript = require('rollup-plugin-typescript2');
-const peerDepsExternal = require('rollup-plugin-peer-deps-external');
-const cleaner = require('rollup-plugin-cleaner');
-const commonjs = require('@rollup/plugin-commonjs');
-const nodeResolve = require('@rollup/plugin-node-resolve');
-const packageJson = require('./package.json');
-
-module.exports = {
-    input: 'src/index.ts',
-    output: [
-        {
-            file: packageJson.main,
-            format: 'cjs',
-            sourcemap: true,
-        },
-        {
-            file: packageJson.module,
-            format: 'esm',
-            sourcemap: true,
-        },
-    ],
-    plugins: [
-        cleaner({
-            targets: ['./lib'],
-        }),
-        peerDepsExternal(),
-        nodeResolve(),
-        commonjs(),
-        typescript({
-            exclude: ['**/*.stories.tsx', '**/*.test.tsx'],
-        }),
-    ],
-};

+ 4 - 4
src/core/index.tsx

@@ -1,13 +1,13 @@
 import {
+    type ReactNode,
     createContext,
+    type Dispatch,
     useContext,
     useReducer,
-    useEffect,
-    ReactNode,
-    Dispatch
+    useEffect
 } from "react";
 import {
-    ConfigType
+    type ConfigType
 } from "../types";
 
 class NCoreContext<T extends {} | undefined, K extends ConfigType<T>> {

+ 0 - 16
src/tests/core/index.test.js

@@ -1,16 +0,0 @@
-import NCoreContext from "../../core";
-
-const Context = new NCoreContext(
-    {
-        a: "x"
-    },
-    {
-        key: "a"
-    }
-);
-
-describe("Core Class Test", () => {
-    test("State", () => {
-        expect(Context.state.a).toEqual("x");
-    });
-});

+ 4 - 4
stories/Introduction.stories.mdx → stories/Introduction.mdx

@@ -5,17 +5,17 @@ import { Meta } from '@storybook/addon-docs/blocks';
 <div style={{ margin: '0 auto', maxWidth: '600px', color: 'white', textAlign: 'center' }}>
   <img
     style={{ width: '75%', margin: '0 auto 2em auto' }}
-    src="https://ncore.nibgat.space/assets/images/lightlogo.png"
+    src="https://ncore.nibgat.space/assets/images/ncorelogo.png"
   />
   <br/>
-  <h1>NİBGAT® | NCore Web Component Library</h1>
+  <h1>NİBGAT® | NCore Context - Global State and Service Management</h1>
   <br />
   <br />
 
 ---
 
-Version: <bold>1.0.5-pre-alpha.13</bold>
+Version: <bold>1.0.6</bold>
 
-It is the NCore Design System developed by the NİBGAT® team and the NCore Web Component Library developed for React.
+It is the NCore Context developed by the NİBGAT® team.
 
 </div>

+ 176 - 0
stories/quick-start.mdx

@@ -0,0 +1,176 @@
+import { Meta } from '@storybook/addon-docs/blocks';
+
+<Meta title="Docs/Getting Started/Quick Start" />
+
+# Getting Started
+
+## Installing
+
+You can install NCore Context (ncore-context) with [NPM](https://www.npmjs.com/package/ncore-context) or Yarn.
+
+-   NPM: `npm install ncore-context`
+-   YARN: `yarn add ncore-context`
+
+## Configuration
+
+You must wrap with NCoreProvider your outermost component. This is important for tools such as theme and localization to be active.
+NCoreProvider allows with a prop named "configs" you to make changes to themes and language packs and select initial theme and initial language.
+
+```tsx
+import {
+    type ReactNode
+} from "react";
+import {
+    type NCoreUIKitConfig
+} from "../types";
+
+class CoreContext {
+    NCoreUIKitDialog: NCoreUIKitDialog;
+
+    constructor() {
+        this.NCoreUIKitDialog = new NCoreUIKitDialog({
+            data: []
+        });
+    }
+
+    Provider = ({
+        children
+    }: {
+        children: ReactNode
+    }) => {
+        const DialogContext = this.NCoreUIKitDialog;
+
+        return <DialogContext.Provider>
+            <DialogContext.Render>
+                {children}
+            </DialogContext.Render>
+        </DialogContext.Provider>
+    };
+};
+export default CoreContext;
+```
+
+```tsx
+import {
+    type ReactNode,
+    Fragment
+} from "react";
+import {
+    type DialogContextType,
+    type DialogDataType
+} from "../types/dialog";
+import NCoreContext, {
+    type ConfigType
+} from "ncore-context";
+import Dialog from "../components/dialog";
+import {
+    uuid
+} from "../utils";
+
+class NCoreUIKitDialog extends NCoreContext<DialogContextType, ConfigType<DialogContextType>> {
+    constructor({
+        data = []
+    }: {
+        data?: Array<DialogDataType>
+    }) {
+        super({
+            close: () => {},
+            open: () => "",
+            data: data
+        }, {
+            key: "NCoreUIKit-DialogContext"
+        });
+    };
+
+    open = (dialogData: DialogDataType) => {
+        const currentData = this.state.data;
+
+        const dialogID = dialogData.id ? dialogData.id : uuid();
+
+        currentData.push({
+            ...dialogData,
+            id: dialogID
+        });
+
+        this.setState({
+            data: currentData
+        });
+
+        return dialogID;
+    };
+
+    close = (props?: {
+        index?: number;
+        id?: string;
+    }) => {
+        const currentData = this.state.data;
+
+        if (props && props.id) {
+            const keyIndex = currentData.findIndex((dialog) => dialog.id === props.id);
+
+            if (keyIndex !== -1) {
+                currentData.splice(keyIndex, 1);
+
+                this.setState({
+                    data: currentData
+                });
+            }
+
+            return;
+        }
+
+        if (props && props.index !== undefined) {
+            currentData.splice(props.index, 1);
+
+            this.setState({
+                data: currentData
+            });
+
+            return;
+        }
+
+        currentData.pop();
+
+        this.setState({
+            data: currentData
+        });
+    };
+
+    Render = ({
+        children
+    }: {
+        children: ReactNode;
+    }) => {
+        const {
+            data
+        } = this.useContext();
+
+        return <Fragment>
+            {children}
+            {data.map((item: DialogDataType) => {
+                return <Dialog
+                    key={`NCoreUIKit-Dialog-${item.id}`}
+                    id={item.id as string}
+                    onClosed={() => {
+                        if(item.onClosed) {
+                            item.onClosed({
+                                id: item.id as string
+                            });
+                        }
+
+                        if(item.isAutoClosed === undefined || item.isAutoClosed === true) {
+                            this.close({
+                                id: item.id
+                            });
+                        }
+                    }}
+                    {...item}
+                />;
+            })}
+        </Fragment>;
+    };
+}
+export default NCoreUIKitDialog;
+```
+
+If you want to set config, please look at Configs and Schemes Pages.

+ 0 - 39
stories/quick-start.stories.mdx

@@ -1,39 +0,0 @@
-import { Meta } from '@storybook/addon-docs/blocks';
-
-<Meta title="Docs/Getting Started/Quick Start" />
-
-# Getting Started
-
-## Installing
-
-You can install NCore Web (ncore-web) with [NPM](https://www.npmjs.com/package/ncore-web) or Yarn.
-
--   NPM: `npm install ncore-web --save`
--   YARN: `yarn add ncore-web`
-
-## Configuration
-
-You must wrap with NCoreProvider your outermost component. This is important for tools such as theme and localization to be active.
-NCoreProvider allows with a prop named "configs" you to make changes to themes and language packs and select initial theme and initial language.
-
-```jsx
-<NCoreProvider
-    configs={{
-        themes: [
-            // ... (theme schemes)
-        ],
-        designTokens: {
-            // ... (design token schemes)
-        },
-        localeS: [
-            // ... (locale schemes)
-        ],
-        initialThemeKey: string // initial theme key.
-        initialLanguage: string // initial language key.
-    }}
->
-    <Button title="Button"/>
-</NCoreProvider>
-```
-
-If you want to set config, please look at Configs and Schemes Pages.

+ 35 - 20
tsconfig.json

@@ -1,30 +1,45 @@
 {
     "compilerOptions": {
-        "baseUrl": "./",
-        "target": "es5",
-        "lib": ["dom", "dom.iterable", "esnext"],
-        "allowJs": true,
-        "skipLibCheck": true,
-        "esModuleInterop": true,
-        "allowSyntheticDefaultImports": true,
-        "strict": true,
         "forceConsistentCasingInFileNames": true,
         "noFallthroughCasesInSwitch": true,
-        "module": "esnext",
-        "moduleResolution": "node",
-        "resolveJsonModule": true,
-        "isolatedModules": true,
-        "jsx": "react-jsx",
-        "declaration": true,
-        "outDir": "./lib",
+        "noUncheckedIndexedAccess": true,
+        "noStrictGenericChecks": false,
         "allowUnreachableCode": false,
+        "verbatimModuleSyntax": false,
+        "noImplicitUseStrict": false,
+        "noUnusedParameters": true,
         "allowUnusedLabels": false,
+        "moduleResolution": "node",
         "noImplicitReturns": true,
-        "noImplicitUseStrict": false,
-        "noStrictGenericChecks": false,
+        "resolveJsonModule": true,
+        "esModuleInterop": true,
         "noUnusedLocals": true,
-        "noUnusedParameters": true
+        "inlineSources": true,
+        "skipLibCheck": true,
+        "declaration": true,
+        "jsx": "react-jsx",
+        "composite": false,
+        "module": "ESNext",
+        "target": "ESNext",
+        "lib": [
+            "dom.iterable",
+            "ESNext",
+            "dom"
+        ],
+        "sourceMap": true,
+        "outDir": "./lib",
+        "rootDir": "src",
+        "noEmit": false,
+        "strict": true,
+        "types": [
+            "node"
+        ]
     },
-    "include": ["src/**/*"],
-    "exclude": ["node_modules", "lib"]
+    "include": [
+        "src/**/*"
+    ],
+    "exclude": [
+        "node_modules",
+        "lib"
+    ]
 }

Разница между файлами не показана из-за своего большого размера
+ 170 - 1009
yarn.lock


Некоторые файлы не были показаны из-за большого количества измененных файлов