From 3051dc56a59c6d4774696acc05e42bd04de34305 Mon Sep 17 00:00:00 2001 From: 3y3 <3y3@ya.ru> Date: Tue, 5 Sep 2023 19:52:18 +0300 Subject: [PATCH 01/52] chore: Remove outdated infrastructure --- .storybook/addons.js | 3 - .storybook/config.js | 42 - .storybook/webpack-levels/index.js | 6 - .../webpack-levels/lib/ConfigBuilder.js | 108 -- .storybook/webpack-levels/lib/DevServer.js | 17 - .storybook/webpack-levels/lib/Entry.js | 49 - .storybook/webpack-levels/lib/Externals.js | 18 - .storybook/webpack-levels/lib/Module.js | 28 - .storybook/webpack-levels/lib/Node.js | 17 - .storybook/webpack-levels/lib/Optimization.js | 24 - .storybook/webpack-levels/lib/Output.js | 50 - .storybook/webpack-levels/lib/Performance.js | 17 - .storybook/webpack-levels/lib/Plugins.js | 18 - .storybook/webpack-levels/lib/Resolve.js | 52 - .storybook/webpack-levels/lib/Stats.js | 17 - .../webpack-levels/lib/levels/analyze.js | 20 - .../webpack-levels/lib/levels/assets.js | 49 - .../webpack-levels/lib/levels/javascript.js | 123 -- .../webpack-levels/lib/levels/optimize.js | 80 - .../webpack-levels/lib/levels/styles.js | 92 - .storybook/webpack-levels/lib/utils.js | 18 - .storybook/webpack.config.js | 90 - babel.config.js | 82 - jest.config.js | 20 - jest/fileTransform.js | 30 - jest/setup.js | 5 - package.json | 56 +- src/demo/Components/DocLeadingPage/index.tsx | 40 - src/demo/Components/DocLeadingPage/page.json | 1486 ----------------- src/demo/Components/DocPage/data.ts | 19 - src/demo/Components/DocPage/index.tsx | 195 --- src/demo/Components/DocPage/page-en.json | 260 --- src/demo/Components/DocPage/page-ru.json | 271 --- .../Components/DocPage/single-page-en.json | 30 - .../Components/DocPage/single-page-ru.json | 30 - src/demo/Components/ErrorPage/index.tsx | 34 - src/demo/Components/Header/Header.scss | 5 - src/demo/Components/Header/Header.tsx | 58 - src/demo/Components/Paginator/index.tsx | 21 - src/demo/Components/SearchItem/index.tsx | 10 - src/demo/Components/SearchItem/page.json | 5 - src/demo/Components/SearchPage/data.ts | 26 - src/demo/Components/SearchPage/index.tsx | 40 - src/demo/Components/index.js | 15 - src/demo/controls/lang.tsx | 13 - src/demo/controls/settings.tsx | 12 - src/demo/controls/vcs.tsx | 13 - src/demo/decorators/bookmark.ts | 12 - src/demo/decorators/subscribe.tsx | 12 - src/demo/decorators/withTheme.tsx | 23 - src/demo/reset-storybook.scss | 49 - src/stories/Components.stories.js | 34 - svgo.config.js | 21 - 53 files changed, 1 insertion(+), 3864 deletions(-) delete mode 100644 .storybook/addons.js delete mode 100644 .storybook/config.js delete mode 100644 .storybook/webpack-levels/index.js delete mode 100644 .storybook/webpack-levels/lib/ConfigBuilder.js delete mode 100644 .storybook/webpack-levels/lib/DevServer.js delete mode 100644 .storybook/webpack-levels/lib/Entry.js delete mode 100644 .storybook/webpack-levels/lib/Externals.js delete mode 100644 .storybook/webpack-levels/lib/Module.js delete mode 100644 .storybook/webpack-levels/lib/Node.js delete mode 100644 .storybook/webpack-levels/lib/Optimization.js delete mode 100644 .storybook/webpack-levels/lib/Output.js delete mode 100644 .storybook/webpack-levels/lib/Performance.js delete mode 100644 .storybook/webpack-levels/lib/Plugins.js delete mode 100644 .storybook/webpack-levels/lib/Resolve.js delete mode 100644 .storybook/webpack-levels/lib/Stats.js delete mode 100644 .storybook/webpack-levels/lib/levels/analyze.js delete mode 100644 .storybook/webpack-levels/lib/levels/assets.js delete mode 100644 .storybook/webpack-levels/lib/levels/javascript.js delete mode 100644 .storybook/webpack-levels/lib/levels/optimize.js delete mode 100644 .storybook/webpack-levels/lib/levels/styles.js delete mode 100644 .storybook/webpack-levels/lib/utils.js delete mode 100644 .storybook/webpack.config.js delete mode 100644 babel.config.js delete mode 100644 jest.config.js delete mode 100644 jest/fileTransform.js delete mode 100644 jest/setup.js delete mode 100644 src/demo/Components/DocLeadingPage/index.tsx delete mode 100644 src/demo/Components/DocLeadingPage/page.json delete mode 100644 src/demo/Components/DocPage/data.ts delete mode 100644 src/demo/Components/DocPage/index.tsx delete mode 100644 src/demo/Components/DocPage/page-en.json delete mode 100644 src/demo/Components/DocPage/page-ru.json delete mode 100644 src/demo/Components/DocPage/single-page-en.json delete mode 100644 src/demo/Components/DocPage/single-page-ru.json delete mode 100644 src/demo/Components/ErrorPage/index.tsx delete mode 100644 src/demo/Components/Header/Header.scss delete mode 100644 src/demo/Components/Header/Header.tsx delete mode 100644 src/demo/Components/Paginator/index.tsx delete mode 100644 src/demo/Components/SearchItem/index.tsx delete mode 100644 src/demo/Components/SearchItem/page.json delete mode 100644 src/demo/Components/SearchPage/data.ts delete mode 100644 src/demo/Components/SearchPage/index.tsx delete mode 100644 src/demo/Components/index.js delete mode 100644 src/demo/controls/lang.tsx delete mode 100644 src/demo/controls/settings.tsx delete mode 100644 src/demo/controls/vcs.tsx delete mode 100644 src/demo/decorators/bookmark.ts delete mode 100644 src/demo/decorators/subscribe.tsx delete mode 100644 src/demo/decorators/withTheme.tsx delete mode 100644 src/demo/reset-storybook.scss delete mode 100644 src/stories/Components.stories.js delete mode 100644 svgo.config.js diff --git a/.storybook/addons.js b/.storybook/addons.js deleted file mode 100644 index c77411ea..00000000 --- a/.storybook/addons.js +++ /dev/null @@ -1,3 +0,0 @@ -import '@storybook/addon-knobs/register'; -import '@storybook/addon-actions/register'; -import 'storybook-readme/register'; diff --git a/.storybook/config.js b/.storybook/config.js deleted file mode 100644 index e50bbc29..00000000 --- a/.storybook/config.js +++ /dev/null @@ -1,42 +0,0 @@ -import '../styles/themes.scss'; -import '../src/demo/reset-storybook.scss'; -import '../styles/default.scss'; -import '../styles/typography.scss'; - -import React from 'react'; -import {configure, addDecorator, addParameters} from '@storybook/react'; -import {withKnobs} from '@storybook/addon-knobs'; -import {addReadme} from 'storybook-readme'; -import {configure as configureUikit, Lang} from '@gravity-ui/uikit/build/esm/components/utils/configure'; - -import withTheme from '../src/demo/decorators/withTheme'; - -configureUikit({ - lang: Lang.En, -}); - -addParameters({ - options: { - panelPosition: 'right', - theme: { - brandTitle: 'Docs Components', - brandUrl: 'https://github.yandex-team.ru/data-ui/docs-components', - }, - showPanel: true, - }, -}); -addDecorator(addReadme); -addDecorator(withKnobs); -addDecorator(withTheme); - -addDecorator((Story, context) => ( -
- -
-)); - -function loadStories() { - require('../src/stories/Components.stories'); -} - -configure(loadStories, module); diff --git a/.storybook/webpack-levels/index.js b/.storybook/webpack-levels/index.js deleted file mode 100644 index 385160f1..00000000 --- a/.storybook/webpack-levels/index.js +++ /dev/null @@ -1,6 +0,0 @@ -exports.ConfigBuilder = require('./lib/ConfigBuilder'); -exports.javascript = require('./lib/levels/javascript'); -exports.styles = require('./lib/levels/styles'); -exports.assets = require('./lib/levels/assets'); -exports.analyze = require('./lib/levels/analyze'); -exports.optimize = require('./lib/levels/optimize'); diff --git a/.storybook/webpack-levels/lib/ConfigBuilder.js b/.storybook/webpack-levels/lib/ConfigBuilder.js deleted file mode 100644 index 2648e373..00000000 --- a/.storybook/webpack-levels/lib/ConfigBuilder.js +++ /dev/null @@ -1,108 +0,0 @@ -const webpack = require('webpack'); -const WebpackManifestPlugin = require('webpack-manifest-plugin'); - -const {getOption} = require('./utils'); -const Entry = require('./Entry'); -const Output = require('./Output'); -const Module = require('./Module'); -const Resolve = require('./Resolve'); -const Plugins = require('./Plugins'); -const Optimization = require('./Optimization'); -const Externals = require('./Externals'); -const DevServer = require('./DevServer'); -const Stats = require('./Stats'); -const Performance = require('./Performance'); -const Node = require('./Node'); - -class ConfigBuilder { - constructor(options = {}) { - this.name = getOption(options.name); - this.target = getOption(options.target); - this.context = getOption(options.context); - this.mode = getOption(options.mode, process.env.NODE_ENV || 'production'); - this.sourceMap = getOption(options.sourceMap, true); - this.cache = getOption(options.cache); - this.isProduction = this.mode === 'production'; - this.isDevelopment = this.mode === 'development'; - - this.entry = new Entry(this); - this.output = new Output(this); - this.module = new Module(this); - this.resolve = new Resolve(this); - this.plugins = new Plugins(this); - this.optimization = new Optimization(this); - this.externals = new Externals(this); - this.devServer = new DevServer(this); - this.stats = new Stats(this); - this.performance = new Performance(this); - this.node = new Node(this); - - // Defaults - if (this.sourceMap) { - this.setDevtool(this.isProduction ? 'source-map' : 'cheap-module-source-map'); - } - - this.plugins.addPlugin(new WebpackManifestPlugin()); - - if (this.isDevelopment) { - this.plugins.addPlugin(new webpack.HotModuleReplacementPlugin()); - } - - this.stats.set({ - children: false, - excludeAssets: (assetName) => { - const regex = /\.map$/; - - return regex.test(assetName); - }, - }); - } - - setMode(mode) { - this.mode = mode; - - return this; - } - - setContext(context) { - this.context = context; - - return this; - } - - setDevtool(devtool) { - this.devtool = devtool; - - return this; - } - - apply(definition) { - definition(this); - - return this; - } - - build() { - return { - name: this.name, - target: this.target, - context: this.context, - mode: this.mode, - cache: this.cache, - devtool: this.devtool, - entry: this.entry.build(), - output: this.output.build(), - module: this.module.build(), - resolve: this.resolve.build(), - plugins: this.plugins.build(), - optimization: this.optimization.build(), - externals: this.externals.build(), - devServer: this.devServer.build(), - stats: this.stats.build(), - performance: this.performance.build(), - node: this.node.build(), - }; - } -} - -module.exports = ConfigBuilder; diff --git a/.storybook/webpack-levels/lib/DevServer.js b/.storybook/webpack-levels/lib/DevServer.js deleted file mode 100644 index edf13acd..00000000 --- a/.storybook/webpack-levels/lib/DevServer.js +++ /dev/null @@ -1,17 +0,0 @@ -class DevServer { - constructor(builder) { - this._builder = builder; - } - - set(devServer) { - this._devServer = devServer; - - return this._builder; - } - - build() { - return this._devServer; - } -} - -module.exports = DevServer; diff --git a/.storybook/webpack-levels/lib/Entry.js b/.storybook/webpack-levels/lib/Entry.js deleted file mode 100644 index 3ffd3b90..00000000 --- a/.storybook/webpack-levels/lib/Entry.js +++ /dev/null @@ -1,49 +0,0 @@ -const {ensureArray} = require('./utils'); - -class Entry { - constructor(builder) { - this._builder = builder; - this._entry = {}; - this._preEntry = []; - this._postEntry = []; - } - - addEntry(name, value) { - let entries; - - if (typeof name === 'string') { - entries = {[name]: value}; - } else { - entries = name; - } - - Object.keys(entries).forEach((entryName) => { - const entryValue = entries[entryName]; - this._entry[entryName] = ensureArray(entryValue); - }); - - return this._builder; - } - - addPreEntry(value) { - this._preEntry = this._preEntry.concat(value); - - return this._builder; - } - - addPostEntry(value) { - this._postEntry = this._postEntry.concat(value); - - return this._builder; - } - - build() { - return Object.keys(this._entry).reduce((res, name) => { - res[name] = this._preEntry.concat(this._entry[name], this._postEntry); - - return res; - }, {}); - } -} - -module.exports = Entry; diff --git a/.storybook/webpack-levels/lib/Externals.js b/.storybook/webpack-levels/lib/Externals.js deleted file mode 100644 index c87b723f..00000000 --- a/.storybook/webpack-levels/lib/Externals.js +++ /dev/null @@ -1,18 +0,0 @@ -class Externals { - constructor(builder) { - this._builder = builder; - } - - extend(data) { - this._externals = this._externals || {}; - Object.assign(this._externals, data); - - return this._builder; - } - - build() { - return this._externals; - } -} - -module.exports = Externals; diff --git a/.storybook/webpack-levels/lib/Module.js b/.storybook/webpack-levels/lib/Module.js deleted file mode 100644 index 94efcd31..00000000 --- a/.storybook/webpack-levels/lib/Module.js +++ /dev/null @@ -1,28 +0,0 @@ -class Module { - constructor(builder) { - this._builder = builder; - this._rules = []; - } - - addNoParse(value) { - this._noParse = this._noParse || []; - this._noParse = this._noParse.concat(value); - - return this._builder; - } - - addRule(rule) { - this._rules.push(rule); - - return this._builder; - } - - build() { - return { - noParse: this._noParse, - rules: this._rules, - }; - } -} - -module.exports = Module; diff --git a/.storybook/webpack-levels/lib/Node.js b/.storybook/webpack-levels/lib/Node.js deleted file mode 100644 index f54b08b3..00000000 --- a/.storybook/webpack-levels/lib/Node.js +++ /dev/null @@ -1,17 +0,0 @@ -class Node { - constructor(builder) { - this._builder = builder; - } - - set(node) { - this._node = node; - - return this._builder; - } - - build() { - return this._node; - } -} - -module.exports = Node; diff --git a/.storybook/webpack-levels/lib/Optimization.js b/.storybook/webpack-levels/lib/Optimization.js deleted file mode 100644 index 08d2eb09..00000000 --- a/.storybook/webpack-levels/lib/Optimization.js +++ /dev/null @@ -1,24 +0,0 @@ -class Optimization { - constructor(builder) { - this._builder = builder; - } - - set(optimization) { - this._optimization = optimization; - - return this._builder; - } - - extend(data) { - this._optimization = this._optimization || {}; - Object.assign(this._optimization, data); - - return this._builder; - } - - build() { - return this._optimization; - } -} - -module.exports = Optimization; diff --git a/.storybook/webpack-levels/lib/Output.js b/.storybook/webpack-levels/lib/Output.js deleted file mode 100644 index 30ff387a..00000000 --- a/.storybook/webpack-levels/lib/Output.js +++ /dev/null @@ -1,50 +0,0 @@ -class Output { - constructor(builder) { - this._builder = builder; - } - - setPath(path) { - this._path = path; - - return this._builder; - } - - setPublicPath(publicPath) { - this._publicPath = publicPath; - - return this._builder; - } - - setFilename(filename) { - this._filename = filename; - - return this._builder; - } - - setChunkFilename(chunkFilename) { - this._chunkFilename = chunkFilename; - - return this._builder; - } - - extend(data) { - this._extend = this._extend || {}; - Object.assign(this._extend, data); - - return this._builder; - } - - build() { - return Object.assign( - { - path: this._path, - publicPath: this._publicPath, - filename: this._filename, - chunkFilename: this._chunkFilename, - }, - this._extend, - ); - } -} - -module.exports = Output; diff --git a/.storybook/webpack-levels/lib/Performance.js b/.storybook/webpack-levels/lib/Performance.js deleted file mode 100644 index fb445e46..00000000 --- a/.storybook/webpack-levels/lib/Performance.js +++ /dev/null @@ -1,17 +0,0 @@ -class Performance { - constructor(builder) { - this._builder = builder; - } - - set(performance) { - this._performance = performance; - - return this._builder; - } - - build() { - return this._performance; - } -} - -module.exports = Performance; diff --git a/.storybook/webpack-levels/lib/Plugins.js b/.storybook/webpack-levels/lib/Plugins.js deleted file mode 100644 index e83047da..00000000 --- a/.storybook/webpack-levels/lib/Plugins.js +++ /dev/null @@ -1,18 +0,0 @@ -class Plugins { - constructor(builder) { - this._builder = builder; - this._plugins = []; - } - - addPlugin(plugin) { - this._plugins.push(plugin); - - return this._builder; - } - - build() { - return this._plugins; - } -} - -module.exports = Plugins; diff --git a/.storybook/webpack-levels/lib/Resolve.js b/.storybook/webpack-levels/lib/Resolve.js deleted file mode 100644 index dc12d21c..00000000 --- a/.storybook/webpack-levels/lib/Resolve.js +++ /dev/null @@ -1,52 +0,0 @@ -const {ensureArray} = require('./utils'); - -class Resolve { - constructor(builder) { - this._builder = builder; - this._defaultModules = ['node_modules']; - this._modules = []; - this._defaultExtensions = ['.js', '.jsx', '.json']; - this._extensions = []; - } - - addModules(paths) { - paths = ensureArray(paths); - this._modules = this._modules.concat(paths); - - return this._builder; - } - - addAliases(aliases) { - this._aliases = this._aliases || {}; - Object.assign(this._aliases, aliases); - - return this._builder; - } - - addExtensions(extensions) { - extensions = ensureArray(extensions); - this._extensions = this._extensions.concat(extensions); - - return this._builder; - } - - extend(data) { - this._extend = this._extend || {}; - Object.assign(this._extend, data); - - return this._builder; - } - - build() { - return Object.assign( - { - modules: this._modules.concat(this._defaultModules), - alias: this._aliases, - extensions: this._extensions.concat(this._defaultExtensions), - }, - this._extend, - ); - } -} - -module.exports = Resolve; diff --git a/.storybook/webpack-levels/lib/Stats.js b/.storybook/webpack-levels/lib/Stats.js deleted file mode 100644 index 7262e0f2..00000000 --- a/.storybook/webpack-levels/lib/Stats.js +++ /dev/null @@ -1,17 +0,0 @@ -class Stats { - constructor(builder) { - this._builder = builder; - } - - set(stats) { - this._stats = stats; - - return this._builder; - } - - build() { - return this._stats; - } -} - -module.exports = Stats; diff --git a/.storybook/webpack-levels/lib/levels/analyze.js b/.storybook/webpack-levels/lib/levels/analyze.js deleted file mode 100644 index 17a3532c..00000000 --- a/.storybook/webpack-levels/lib/levels/analyze.js +++ /dev/null @@ -1,20 +0,0 @@ -const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer'); - -module.exports = function createAnalyzeLevel(options = {}) { - const defaultOptions = { - openAnalyzer: false, - analyzerMode: 'static', - reportFilename: 'stats.html', - }; - - return function analyzeLevel(config) { - if (options) { - config.plugins.addPlugin( - new BundleAnalyzerPlugin({ - ...defaultOptions, - ...options, - }), - ); - } - }; -}; diff --git a/.storybook/webpack-levels/lib/levels/assets.js b/.storybook/webpack-levels/lib/levels/assets.js deleted file mode 100644 index 7c3a6efe..00000000 --- a/.storybook/webpack-levels/lib/levels/assets.js +++ /dev/null @@ -1,49 +0,0 @@ -const {getOption} = require('../utils'); - -function addRule(config, {imgFilename, fontFilename, limit, ruleIncludes, ruleExcludes}) { - const imagesRule = { - test: /\.(ico|bmp|gif|jpe?g|png|svg)$/, - include: ruleIncludes, - exclude: ruleExcludes, - use: [ - { - loader: require.resolve('url-loader'), - options: { - limit: limit, - name: imgFilename, - fallback: 'file-loader', - }, - }, - ], - }; - const fontsRule = { - test: /\.(ttf|eot|woff2?)$/, - include: ruleIncludes, - exclude: ruleExcludes, - use: [ - { - loader: require.resolve('url-loader'), - options: { - limit: limit, - name: fontFilename, - fallback: 'file-loader', - }, - }, - ], - }; - - config.module.addRule(imagesRule); - config.module.addRule(fontsRule); -} - -module.exports = function createAssetsLevel(options = {}) { - const imgFilename = getOption(options.imgFilename, 'assets/img/[name].[hash:8].[ext]'); - const fontFilename = getOption(options.fontFilename, 'assets/fonts/[name].[hash:8].[ext]'); - const limit = getOption(options.limit, 8192); // 8kB - const ruleIncludes = getOption(options.ruleIncludes); - const ruleExcludes = getOption(options.ruleExcludes); - - return function assetsLevel(config) { - addRule(config, {imgFilename, fontFilename, limit, ruleIncludes, ruleExcludes}); - }; -}; diff --git a/.storybook/webpack-levels/lib/levels/javascript.js b/.storybook/webpack-levels/lib/levels/javascript.js deleted file mode 100644 index a389b0d8..00000000 --- a/.storybook/webpack-levels/lib/levels/javascript.js +++ /dev/null @@ -1,123 +0,0 @@ -const webpack = require('webpack'); -const FortTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); -const {getOption, setOutput} = require('../utils'); - -function addRule( - config, - { - browsers, - langs, - bemLevels, - ruleIncludes, - ruleExcludes, - reactHotLoader, - cacheLoader, - threadLoader, - typescript, - }, -) { - const envConfig = { - targets: browsers, - modules: false, - }; - - const jsRule = { - test: typescript ? /\.[jt]sx?$/ : /\.jsx?$/, - include: ruleIncludes, - exclude: ruleExcludes, - use: [ - { - loader: require.resolve('babel-loader'), - options: { - plugins: [reactHotLoader && require.resolve('react-hot-loader/babel')].filter( - Boolean, - ), - sourceType: 'unambiguous', - cacheDirectory: config.isDevelopment, - compact: config.isProduction, - }, - }, - ], - }; - - if (threadLoader) { - jsRule.use.unshift({ - loader: require.resolve('thread-loader'), - options: { - workers: require('os').cpus().length - 1, - }, - }); - } - - if (cacheLoader) { - jsRule.use.unshift({ - loader: require.resolve('cache-loader'), - }); - } - - config.module.addRule(jsRule); -} - -function addPlugins(config, {langs, buildLang, typescript, forkTsChecker}) { - config.plugins.addPlugin( - new webpack.ContextReplacementPlugin( - /moment[\\/]locale$/, - new RegExp(`^\\./(${langs.join('|')})$`), - ), - ); - - if (typescript) { - config.plugins.addPlugin( - new FortTsCheckerWebpackPlugin({ - checkSyntacticErrors: true, - useTypescriptIncrementalApi: true, - ...forkTsChecker, - }), - ); - } -} - -function addResolve(config, {reactHotLoader, typescript}) { - if (reactHotLoader && config.isDevelopment) { - config.resolve.addAliases({ - 'react-dom': require.resolve('@hot-loader/react-dom'), - }); - } - - if (typescript) { - config.resolve.addExtensions(['.ts', '.tsx']); - } -} - -module.exports = function createJavascriptLevel(options = {}) { - const filename = getOption(options.filename, 'js/[name].[chunkhash:8].js'); - const chunkFilename = getOption(options.chunkFilename, filename); - const browsers = getOption(options.browsers); - const langs = getOption(options.langs, ['ru']); - const bemLevels = getOption(options.bemLevels, []); - const typescript = getOption(options.typescript, false); - const buildLang = getOption(options.buildLang, 'ru'); - const ruleIncludes = getOption(options.ruleIncludes); - const ruleExcludes = getOption(options.ruleExcludes); - const reactHotLoader = getOption(options.reactHotLoader, true); - const cacheLoader = getOption(options.cacheLoader, true); - const threadLoader = getOption(options.threadLoader, true); - const forkTsChecker = getOption(options.forkTsChecker, {}); - - return function javascriptLevel(config) { - setOutput(config, {filename, chunkFilename}); - addRule(config, { - browsers, - langs, - bemLevels, - ruleIncludes, - ruleExcludes, - reactHotLoader, - cacheLoader, - threadLoader, - typescript, - }); - addPlugins(config, {langs, buildLang, typescript, forkTsChecker}); - addResolve(config, {typescript, reactHotLoader}); - }; -}; diff --git a/.storybook/webpack-levels/lib/levels/optimize.js b/.storybook/webpack-levels/lib/levels/optimize.js deleted file mode 100644 index 366ba7a8..00000000 --- a/.storybook/webpack-levels/lib/levels/optimize.js +++ /dev/null @@ -1,80 +0,0 @@ -const TerserWebpackPlugin = require('terser-webpack-plugin'); -const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); -const {getOption} = require('../utils'); - -const defaultVendorsList = [ - 'react', - 'react-dom', - 'react-is', - 'redux', - 'react-redux', - 'lodash', - 'lodash-es', - 'moment', - 'scheduler', - '@babel/runtime', -]; - -module.exports = function createOptimizeLevel(options = {}) { - const vendors = getOption(options.vendors, true); - const vendorsList = getOption(options.vendorsList, defaultVendorsList); - const commons = getOption(options.commons, true); - const minCommonsChunks = getOption(options.minCommonsChunks, 2); - const runtimeChunk = getOption(options.runtimeChunk, true); - const customCacheGroups = getOption(options.cacheGroups, {}); - const terser = getOption(options.terser); - - const cacheGroups = { - default: false, - }; - - if (vendors) { - cacheGroups.vendors = { - name: 'vendors', - test: new RegExp(`([\\\\/])node_modules\\1(${vendorsList.join('|')})\\1`), - priority: Infinity, - }; - } else { - // Webpack by default has its own vendors cache group. - // We don't want to use it. - cacheGroups.vendors = false; - } - - if (commons) { - cacheGroups.commons = { - name: 'commons', - minChunks: minCommonsChunks, - }; - } - - return function optimizeLevel(config) { - if (config.isProduction) { - config.optimization.extend({ - minimizer: [ - new TerserWebpackPlugin({ - sourceMap: config.sourceMap, - terserOptions: terser, - }), - new OptimizeCSSAssetsPlugin({ - cssProcessorPluginOptions: { - preset: [ - 'default', - { - svgo: false, - }, - ], - }, - }), - ], - splitChunks: { - chunks: 'all', - cacheGroups: { - ...cacheGroups, - ...customCacheGroups, - }, - }, - runtimeChunk: runtimeChunk ? 'single' : false, - }); - } - }; -}; diff --git a/.storybook/webpack-levels/lib/levels/styles.js b/.storybook/webpack-levels/lib/levels/styles.js deleted file mode 100644 index 42fbb507..00000000 --- a/.storybook/webpack-levels/lib/levels/styles.js +++ /dev/null @@ -1,92 +0,0 @@ -const MiniCSSExtractPlugin = require('mini-css-extract-plugin'); -const autoprefixer = require('autoprefixer'); -const {getOption} = require('../utils'); - -function addRules(config, {browsers, extract, ruleIncludes, ruleExcludes, paths}) { - const styleLoader = { - loader: require.resolve('style-loader'), - }; - const cssLoader = { - loader: require.resolve('css-loader'), - options: { - sourceMap: config.sourceMap, - importLoaders: 2, - }, - }; - const postcssLoader = { - loader: require.resolve('postcss-loader'), - options: { - sourceMap: config.sourceMap, - plugins: [autoprefixer({browsers})], - }, - }; - const sassLoader = { - loader: require.resolve('sass-loader'), - options: { - sourceMap: config.sourceMap, - sassOptions: { - includePaths: paths, - }, - }, - }; - const stylusLoader = { - loader: require.resolve('stylus-loader'), - options: { - sourceMap: config.sourceMap, - paths: paths, - }, - }; - const resolveUrlLoader = { - loader: require.resolve('resolve-url-loader'), - options: { - sourceMap: config.sourceMap, - }, - }; - - const sassRule = { - test: /\.s?css$/, - include: ruleIncludes, - exclude: ruleExcludes, - use: [cssLoader, postcssLoader, resolveUrlLoader, sassLoader], - }; - const stylusRule = { - test: /\.styl$/, - include: ruleIncludes, - exclude: ruleExcludes, - use: [cssLoader, postcssLoader, stylusLoader], - }; - - if (config.isProduction && extract) { - sassRule.use.unshift(MiniCSSExtractPlugin.loader); - stylusRule.use.unshift(MiniCSSExtractPlugin.loader); - } else { - sassRule.use.unshift(styleLoader); - stylusRule.use.unshift(styleLoader); - } - - config.module.addRule(sassRule); - config.module.addRule(stylusRule); -} - -function addPlugins(config, {filename}) { - config.plugins.addPlugin( - new MiniCSSExtractPlugin({ - filename: config.isProduction ? filename : '[name].css', - chunkFilename: config.isProduction ? filename : '[name].css', - }), - ); -} - -module.exports = function createStylesLevel(options = {}) { - const filename = getOption(options.filename, 'css/[name].[contenthash:8].css'); - const extract = getOption(options.extract, true); - const browsers = getOption(options.browsers); - const ruleIncludes = getOption(options.ruleIncludes); - const ruleExcludes = getOption(options.ruleExcludes); - const paths = getOption(options.paths); - - return function stylesLevel(config) { - addRules(config, {filename, extract, browsers, ruleIncludes, ruleExcludes, paths}); - addPlugins(config, {filename}); - }; -}; diff --git a/.storybook/webpack-levels/lib/utils.js b/.storybook/webpack-levels/lib/utils.js deleted file mode 100644 index 455cd6cd..00000000 --- a/.storybook/webpack-levels/lib/utils.js +++ /dev/null @@ -1,18 +0,0 @@ -exports.getOption = function getOption(value, defaultValue) { - if (typeof value === 'undefined') { - return defaultValue; - } - - return value; -}; - -exports.ensureArray = function ensureArray(value) { - return Array.isArray(value) ? value : [value]; -}; - -exports.setOutput = function setOutput(config, {filename, chunkFilename}) { - const devName = '[name].js'; - - config.output.setFilename(config.isProduction ? filename : devName); - config.output.setChunkFilename(config.isProduction ? chunkFilename : devName); -}; diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js deleted file mode 100644 index 26573149..00000000 --- a/.storybook/webpack.config.js +++ /dev/null @@ -1,90 +0,0 @@ -'use strict'; -const path = require('path'); -const SpriteLoaderPlugin = require('svg-sprite-loader/plugin'); -const {ConfigBuilder, javascript, styles, assets, optimize} = require('./webpack-levels'); - -const srcRoot = path.resolve(__dirname, '..', 'src'); -const stylesRoot = path.resolve(__dirname, '..', 'styles'); -const assetsRoot = path.resolve(__dirname, '..', 'assets'); -const storybookRoot = path.resolve(__dirname, '..', '.storybook'); -const storybookHost = path.resolve(__dirname, '..', 'node_modules/storybook-host'); -const yfmTransformDir = path.resolve(srcRoot, '../node_modules/@doc-tools/transform'); -const uiKitDir = path.resolve(srcRoot, '../node_modules/@gravity-ui/uikit'); - -const ruleIncludes = [ - srcRoot, - stylesRoot, - assetsRoot, - storybookRoot, - storybookHost, - yfmTransformDir, - uiKitDir, -]; - -const config = new ConfigBuilder(); - -config - .apply( - javascript({ - bem: false, - typescript: true, - reactHotLoader: false, - ruleIncludes, - }), - ) - .apply( - styles({ - ruleIncludes, - }), - ) - .apply( - assets({ - ruleIncludes, - ruleExcludes: [path.resolve(assetsRoot, 'icons')], - }), - ) - .apply(optimize()) - .resolve.addModules(srcRoot) - .module.addRule({ - test: /\.svg$/, - include: [path.resolve(assetsRoot, 'icons')], - loader: 'svg-sprite-loader', - options: { - extract: true, - spriteFilename: 'sprite-[hash:6].svg', - }, - exclude: [path.resolve(assetsRoot, 'icons')], - }) - .module.addRule({ - test: /\.svg$/, - loader: 'react-svg-loader', - include: [path.resolve(assetsRoot, 'icons')], - options: { - svgo: { - plugins: [ - { - removeViewBox: false, - }, - ], - }, - }, - }) - .plugins.addPlugin(new SpriteLoaderPlugin({plainSprite: true})); - -const projectConfig = config.build(); - -module.exports = ({config: storybookBaseConfig}) => { - storybookBaseConfig.module = projectConfig.module; - storybookBaseConfig.resolve = projectConfig.resolve; - - // storybook-readme - storybookBaseConfig.module.rules.push({ - test: /\.md$/, - include: [path.resolve(__dirname, '..')], - use: [{loader: 'html-loader'}, {loader: 'markdown-loader'}], - }); - - storybookBaseConfig.plugins.push(...projectConfig.plugins); - - return storybookBaseConfig; -}; diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index 36865f90..00000000 --- a/babel.config.js +++ /dev/null @@ -1,82 +0,0 @@ -/* eslint-env node */ -const env = process.env.BABEL_ENV || process.env.NODE_ENV || 'development'; -const isEnvDevelopment = env === 'development'; -const isEnvProduction = env === 'production'; -const isEnvTest = env === 'test'; - -module.exports = { - presets: [ - // Latest stable ECMAScript features - (isEnvDevelopment || isEnvProduction) && [require.resolve('@babel/preset-env'), {}], - // ES features necessary for current Node version - isEnvTest && [ - require.resolve('@babel/preset-env'), - Object.assign( - {}, - {}, - { - targets: { - node: 'current', - }, - }, - ), - ], - // JSX - [ - require.resolve('@babel/preset-react'), - { - development: isEnvDevelopment || isEnvTest, - useBuiltIns: true, - }, - ], - [require.resolve('@babel/preset-typescript')], - ].filter(Boolean), - plugins: [ - // class { handleClick = () => { } } - [ - require.resolve('@babel/plugin-proposal-class-properties'), - { - loose: true, - }, - ], - // const { a, ...z } = { a: 1, b: 2, c: 3 } - [ - require.resolve('@babel/plugin-proposal-object-rest-spread'), - { - useBuiltIns: true, - }, - ], - [ - require.resolve('@babel/plugin-proposal-decorators'), - { - // @decorator - // export class Foo {} - decoratorsBeforeExport: true, - }, - ], - // Polyfills the runtime needed for async/await and generators - [require.resolve('@babel/plugin-transform-runtime'), {}], - isEnvProduction && [ - require.resolve('babel-plugin-transform-react-remove-prop-types'), - { - removeImport: true, - }, - ], - // function* () { yield 42; yield 43; } - (isEnvDevelopment || isEnvProduction) && [ - require.resolve('@babel/plugin-transform-regenerator'), - { - // Async functions are converted to generators by @babel/preset-env - async: false, - }, - ], - // a?.b?.c - require.resolve('@babel/plugin-proposal-optional-chaining'), - // a ?? b; - require.resolve('@babel/plugin-proposal-nullish-coalescing-operator'), - // Adds syntax support for import() - require.resolve('@babel/plugin-syntax-dynamic-import'), - // Compiles import() to a deferred require() - isEnvTest && require.resolve('babel-plugin-dynamic-import-node'), - ].filter(Boolean), -}; diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index c9d719be..00000000 --- a/jest.config.js +++ /dev/null @@ -1,20 +0,0 @@ -/* eslint-env node */ - -module.exports = { - globals: { - window: true, - 'ts-jest': { - tsConfig: '/tsconfig.json', - }, - }, - setupFilesAfterEnv: ['/jest/setup.js'], - roots: ['/jest', '/src'], - moduleDirectories: ['node_modules', '/jest'], - transform: { - '^.+\\.(js|jsx)$': 'babel-jest', - '^.+\\.(ts|tsx)$': 'ts-jest', - '^(?!.*\\.(js|jsx|ts|tsx|json)$)': '/jest/fileTransform.js', - }, - transformIgnorePatterns: ['node_modules/'], - snapshotSerializers: ['enzyme-to-json/serializer'], -}; diff --git a/jest/fileTransform.js b/jest/fileTransform.js deleted file mode 100644 index 4f753ce4..00000000 --- a/jest/fileTransform.js +++ /dev/null @@ -1,30 +0,0 @@ -/* eslint-env node */ -const path = require('path'); - -module.exports = { - process: function (src, filename) { - const assetFilename = JSON.stringify(path.basename(filename)); - - if (filename.match(/\.svg$/)) { - return `module.exports = { - __esModule: true, - default: { - id: ${assetFilename}, - url: ${assetFilename}, - viewBox: '0 0 0 0' - }, - ReactComponent: (props) => ({ - $$typeof: Symbol.for('react.element'), - type: 'svg', - ref: null, - key: null, - props: Object.assign({}, props, { - children: ${assetFilename} - }) - }), - };`; - } - - return `module.exports = ${assetFilename};`; - }, -}; diff --git a/jest/setup.js b/jest/setup.js deleted file mode 100644 index 26c78a72..00000000 --- a/jest/setup.js +++ /dev/null @@ -1,5 +0,0 @@ -/* eslint-env node */ -const Enzyme = require('enzyme'); -const Adapter = require('enzyme-adapter-react-16'); - -Enzyme.configure({adapter: new Adapter()}); diff --git a/package.json b/package.json index 414648da..8f1ed2e5 100644 --- a/package.json +++ b/package.json @@ -34,17 +34,11 @@ "lint:prettier": "prettier --check 'src/**/*.{js,jsx,ts,tsx,css,scss,json,yaml,yml,md}'", "lint:prettier:fix": "prettier --write 'src/**/*.{js,jsx,ts,tsx,css,scss,json,yaml,yml,md}'", "typecheck": "tsc --noEmit", - "svgo": "svgo --config svgo.config.js", - "svgo:all": "svgo --config svgo.config.js -r -f .", - "dev": "npm run storybook:start", - "test": "jest --passWithNoTests", - "test:update": "jest -u", + "test": "exit 0", "build:clean": "rimraf build", "build:copy": "copyfiles -u 1 \"src/components/**/*.scss\" \"src/components/**/*.svg\" build", "build:compile": "tsc -p tsconfig.publish.json", "build": "npm run build:clean && npm run build:compile && npm run build:copy", - "storybook:start": "start-storybook -p 7008", - "storybook:build": "build-storybook -c .storybook -o storybook-static", "prepublishOnly": "npm run lint && npm run test && npm run build", "prepare": "husky install", "pre-commit": "lint-staged" @@ -68,26 +62,7 @@ "@doc-tools/transform": "^3.10.2" }, "devDependencies": { - "@babel/core": "7.17.5", - "@babel/parser": "7.17.3", - "@babel/plugin-proposal-class-properties": "7.16.7", - "@babel/plugin-proposal-decorators": "7.17.2", - "@babel/plugin-proposal-nullish-coalescing-operator": "7.16.7", - "@babel/plugin-proposal-object-rest-spread": "7.17.3", - "@babel/plugin-proposal-optional-chaining": "7.16.7", - "@babel/plugin-syntax-dynamic-import": "7.8.3", - "@babel/plugin-transform-regenerator": "7.16.7", - "@babel/plugin-transform-runtime": "7.17.0", - "@babel/preset-env": "7.16.11", - "@babel/preset-react": "7.16.7", - "@babel/preset-typescript": "7.16.7", - "@babel/runtime": "7.17.2", "@doc-tools/transform": "^3.10.2", - "@storybook/addon-actions": "5.3.21", - "@storybook/addon-knobs": "5.3.21", - "@storybook/react": "5.3.21", - "@types/enzyme": "3.10.11", - "@types/jest": "25.2.3", "@types/langs": "^2.0.1", "@types/lodash": "4.14.179", "@types/react": "16.14.23", @@ -97,46 +72,17 @@ "@yandex-cloud/stylelint-config": "1.1.0", "@yandex-cloud/tsconfig": "1.0.0", "autoprefixer": "9.8.8", - "babel-jest": "25.5.1", - "babel-loader": "8.2.3", - "babel-plugin-dynamic-import-node": "2.3.3", - "babel-plugin-transform-react-remove-prop-types": "0.4.24", - "cache-loader": "4.1.0", "copyfiles": "2.4.1", - "css-loader": "3.6.0", - "enzyme": "3.11.0", - "enzyme-adapter-react-16": "1.15.6", - "enzyme-to-json": "3.6.2", "eslint": "8.10.0", - "file-loader": "6.2.0", - "fork-ts-checker-webpack-plugin": "4.1.6", "husky": "7.0.4", - "jest": "25.5.4", "lint-staged": "12.3.5", - "mini-css-extract-plugin": "0.9.0", "node-sass": "4.14.1", "npm-run-all": "4.1.5", - "optimize-css-assets-webpack-plugin": "5.0.8", "postcss": "8.4.8", - "postcss-loader": "3.0.0", "prettier": "2.5.1", - "react-hot-loader": "4.13.0", - "react-svg-loader": "3.0.3", - "resolve-url-loader": "3.1.4", - "sass-loader": "8.0.2", - "storybook-host": "5.2.0", - "storybook-readme": "5.0.9", - "style-loader": "1.3.0", "stylelint": "14.5.3", - "stylus-loader": "3.0.2", - "svg-sprite-loader": "4.3.0", "svgo": "2.8.0", - "terser-webpack-plugin": "2.3.8", - "thread-loader": "2.1.3", - "ts-jest": "25.5.1", "typescript": "4.6.2", - "url-loader": "4.1.1", - "webpack-bundle-analyzer": "3.9.0", "webpack-manifest-plugin": "2.2.0" }, "lint-staged": { diff --git a/src/demo/Components/DocLeadingPage/index.tsx b/src/demo/Components/DocLeadingPage/index.tsx deleted file mode 100644 index 16d5672b..00000000 --- a/src/demo/Components/DocLeadingPage/index.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import React, {useState} from 'react'; -import cn from 'bem-cn-lite'; -import {DocLeadingPage} from '../../../index'; -import Header from '../Header/Header'; -import {DEFAULT_SETTINGS} from '../../../constants'; -import {getIsMobile} from '../../controls/settings'; -import getLangControl from '../../controls/lang'; -import pageContent from './page.json'; -import {DocLeadingPageData} from '../../../models'; - -const layoutBlock = cn('Layout'); - -const DocLeadingPageDemo = () => { - const langValue = getLangControl(); - const isMobile = getIsMobile(); - const router = {pathname: '/docs/compute'}; - - const [fullScreen, onChangeFullScreen] = useState(DEFAULT_SETTINGS.fullScreen); - const [lang, onChangeLang] = useState(langValue); - - return ( -
-
-
- -
-
- ); -}; - -export default DocLeadingPageDemo; diff --git a/src/demo/Components/DocLeadingPage/page.json b/src/demo/Components/DocLeadingPage/page.json deleted file mode 100644 index ba66e0c5..00000000 --- a/src/demo/Components/DocLeadingPage/page.json +++ /dev/null @@ -1,1486 +0,0 @@ -{ - "toc": { - "title": "Yandex Compute Cloud", - "href": "/docs/compute/", - "items": [ - { - "name": "Начало работы", - "items": [ - { - "name": "Обзор", - "href": "/docs/compute/quickstart/", - "id": "2b1fc8b8c3fb717e87c9bf403f56d997" - }, - { - "name": "Создание виртуальной машины Linux", - "href": "/docs/compute/quickstart/quick-create-linux", - "id": "525156d7631981ed1fd0025eb2d38ac6" - }, - { - "name": "Создание виртуальной машины Windows", - "href": "/docs/compute/quickstart/quick-create-windows", - "id": "33f2b39cbddb23929b4fa20ba78367b3" - }, - { - "name": "Создание группы виртуальных машин", - "href": "/docs/compute/quickstart/ig", - "id": "d4b7d652f22d3e137f6fcc4e33669046" - } - ], - "id": "e7bade82ebbf6557c6fe306cfe69b62a" - }, - { - "name": "Пошаговые инструкции", - "items": [ - { - "name": "Все инструкции", - "href": "/docs/compute/operations/", - "id": "0d611622724e5dff64175797fa90000b" - }, - { - "name": "Создание виртуальной машины", - "items": [ - { - "name": "Создать ВМ Linux", - "href": "/docs/compute/operations/vm-create/create-linux-vm", - "id": "097b0c0dae78bb8f7c5b4bf995531f8b" - }, - { - "name": "Создать ВМ Windows", - "href": "/docs/compute/operations/vm-create/create-windows-vm", - "id": "96c6d6fb217fe80f746bb14dc0d58891" - }, - { - "name": "Создать ВМ из набора дисков", - "href": "/docs/compute/operations/vm-create/create-from-disks", - "id": "744fe79d8c1a08b1221b171ccf161cc8" - }, - { - "name": "Создать ВМ с дисками из снимков", - "href": "/docs/compute/operations/vm-create/create-from-snapshots", - "id": "1c15840f0022321f21b9451e050816ab" - }, - { - "name": "Создать ВМ из пользовательского образа", - "href": "/docs/compute/operations/vm-create/create-from-user-image", - "id": "f9d73a7be5ad533ee12c7490c0c56ac0" - }, - { - "name": "Создать прерываемую ВМ", - "href": "/docs/compute/operations/vm-create/create-preemptible-vm", - "id": "448dbdd7f0bd62eda18a8e78e8948364" - }, - { - "name": "Создать ВМ c GPU", - "href": "/docs/compute/operations/vm-create/create-vm-with-gpu", - "id": "4ce394283f98084a41183f22732ba4c0" - } - ], - "id": "202d23c522cc4abcc765fe0c8d314cd6" - }, - { - "name": "DSVM", - "items": [ - { - "name": "Обзор", - "href": "/docs/compute/operations/dsvm/", - "id": "06e6e1f19b30eb9e0ae287bd39aa5fbb" - }, - { - "name": "Создать ВМ из публичного образа DSVM", - "href": "/docs/compute/operations/dsvm/quickstart", - "id": "5838b5b6bfad03e5a6a039d24e4f06a8" - } - ], - "id": "893a628d7a9e1540ab14394b1b2be78d" - }, - { - "name": "Группы размещения", - "items": [ - { - "name": "Создать группу размещения", - "href": "/docs/compute/operations/placement-groups/create", - "id": "742efaa6e0058c84979823eee6d26c48" - }, - { - "name": "Удалить группу размещения", - "href": "/docs/compute/operations/placement-groups/delete", - "id": "dd141f3f258b5f18b57be7817eea42dc" - }, - { - "name": "Создать ВМ в группе размещения", - "href": "/docs/compute/operations/placement-groups/create-vm-in-pg", - "id": "8cc9dfba72f9ac7c34ed89ae81a4a064" - }, - { - "name": "Добавить ВМ в группу размещения", - "href": "/docs/compute/operations/placement-groups/add-vm", - "id": "0eccdbf70eb5012f7cb19bc1cae42fea" - }, - { - "name": "Исключить ВМ из группы размещения", - "href": "/docs/compute/operations/placement-groups/delete-vm", - "id": "29c3cda26e600165be543bda66ff2141" - } - ], - "id": "649653e7b44375c7530d29ac2d713ffc" - }, - { - "name": "Образы с предустановленным ПО", - "items": [ - { - "name": "Создать ВМ из публичного образа", - "href": "/docs/compute/operations/images-with-pre-installed-software/create", - "id": "53d082e0dd61e3fb71d1b58050ba3d48" - }, - { - "name": "Настроить ПО", - "href": "/docs/compute/operations/images-with-pre-installed-software/setup", - "id": "b725ab490b39c14796dc3b5e701b0340" - }, - { - "name": "Работа с ВМ на базе публичного образа", - "href": "/docs/compute/operations/images-with-pre-installed-software/operate", - "id": "2aa01248658d9bca73f8883faa62618b" - }, - { - "name": "Получить список публичных образов", - "href": "/docs/compute/operations/images-with-pre-installed-software/get-list", - "id": "c5f78060588b30093f078c2ad2499649" - } - ], - "id": "eaae70b245e84cae69729647e56141b3" - }, - { - "name": "Получение информации о виртуальной машине", - "items": [ - { - "name": "Получить информацию о ВМ", - "href": "/docs/compute/operations/vm-info/get-info", - "id": "d3f6dd43a99d56e80e092f01b8318dc1" - }, - { - "name": "Получить вывод последовательного порта", - "href": "/docs/compute/operations/vm-info/get-serial-port-output", - "id": "8cb88af2ed1ef49294dd4cf03cdc0b98" - } - ], - "id": "9657fcaf9ecc34ebd98ed890a8d479b9" - }, - { - "name": "Управление виртуальной машиной", - "items": [ - { - "name": "Остановить и запустить ВМ", - "href": "/docs/compute/operations/vm-control/vm-stop-and-start", - "id": "c9285f14c796379c07a4d93ba04013b7" - }, - { - "name": "Подключить диск к ВМ", - "href": "/docs/compute/operations/vm-control/vm-attach-disk", - "id": "b25d5f41f89b869d23eb1e6435950945" - }, - { - "name": "Отключить диск от ВМ", - "href": "/docs/compute/operations/vm-control/vm-detach-disk", - "id": "2e23067c1366b0d315c2f9d580eeb950" - }, - { - "name": "Перенести ВМ в другую зону доступности", - "href": "/docs/compute/operations/vm-control/vm-change-zone", - "id": "f4db404dbb16896066e1f2ae17b9394c" - }, - { - "name": "Сделать публичный IP-адрес ВМ статическим", - "href": "/docs/compute/operations/vm-control/vm-set-static-ip", - "id": "7df529e93e61f30a3c9cefbcd2d271b3" - }, - { - "name": "Изменить ВМ", - "href": "/docs/compute/operations/vm-control/vm-update", - "id": "a024103c9a61c19a5c3b938a5ee98668" - }, - { - "name": "Изменить вычислительные ресурсы ВМ", - "href": "/docs/compute/operations/vm-control/vm-update-resources", - "id": "8b28bdcdf089ec44140802dd69a86048" - }, - { - "name": "Удалить ВМ", - "href": "/docs/compute/operations/vm-control/vm-delete", - "id": "edae79bd12dd5c7d2d2337a63cff1170" - } - ], - "id": "41b5d169b5a879a9f26f53b638af3c80" - }, - { - "name": "Работа на виртуальной машине", - "items": [ - { - "name": "Подключиться к ВМ по SSH", - "href": "/docs/compute/operations/vm-connect/ssh", - "id": "4937b718ce8d5dba56f42dfca2229ae9" - }, - { - "name": "Подключиться к ВМ по RDP", - "href": "/docs/compute/operations/vm-connect/rdp", - "id": "8009e4f53b5126a98b29b1939042236b" - }, - { - "name": "Подключиться к ВМ через PowerShell", - "href": "/docs/compute/operations/vm-connect/powershell", - "id": "f1b9415a33ad3d2c3e18b4fdf4d46a66" - }, - { - "name": "Работа с Yandex.Cloud изнутри ВМ", - "href": "/docs/compute/operations/vm-connect/auth-inside-vm", - "id": "70d4e6376271a28d85f84cfa71740d10" - }, - { - "name": "Установить NVIDIA-драйверы", - "href": "/docs/compute/operations/vm-operate/install-nvidia-drivers", - "id": "0a5b26c3353cb05eb4914658e13ee8b6" - }, - { - "name": "Восстановить доступ к ВМ", - "href": "/docs/compute/operations/vm-connect/recovery-access", - "id": "5fa057a38e18bb78203114ca09fef3f4" - } - ], - "id": "d334bd798474237e73ac29f1591cb43d" - }, - { - "name": "Создание нового диска", - "items": [ - { - "name": "Создать пустой диcк", - "href": "/docs/compute/operations/disk-create/empty", - "id": "2125a206cb1625ab58042778415fba12" - }, - { - "name": "Создать пустой диск с блоком большого размера", - "href": "/docs/compute/operations/disk-create/empty-disk-blocksize", - "id": "846c476e44dcc470d99a13eb821ba99a" - }, - { - "name": "Создать нереплицируемый диск", - "href": "/docs/compute/operations/disk-create/nonreplicated", - "id": "83dda0e7796b8eeb920633ff1761af3b" - } - ], - "id": "7e21fff691b7e021939e0f64977b0a32" - }, - { - "name": "Управление диском", - "items": [ - { - "name": "Создать снимок диска", - "href": "/docs/compute/operations/disk-control/create-snapshot", - "id": "0cc9f2e81dc6fdc2e6aa63870dffba47" - }, - { - "name": "Изменить диск", - "href": "/docs/compute/operations/disk-control/update", - "id": "3e9beba4dafd9f7e3e9d477086a838d9" - }, - { - "name": "Удалить диcк", - "href": "/docs/compute/operations/disk-control/delete", - "id": "32c2adb207b86021e04d396b8e2f490f" - }, - { - "name": "Удалить снимок диска", - "href": "/docs/compute/operations/snapshot-control/delete", - "id": "8d4126c099cbecd7a0fa7892684d4a4e" - } - ], - "id": "09bf4f27793a28dd8486b631182bb756" - }, - { - "name": "Группы размещения дисков", - "items": [ - { - "name": "Создать группу размещения дисков", - "href": "/docs/compute/operations/disk-placement-groups/create", - "id": "e24064c1c66e9b8d130932fbb73ec91d" - }, - { - "name": "Удалить диск из группы размещения", - "href": "/docs/compute/operations/disk-placement-groups/remove-disk", - "id": "603617df3ee117be0180b5d25272b8f9" - } - ], - "id": "2e1a239532bb45c6010ca9c13a59acf4" - }, - { - "name": "Создание нового образа", - "items": [ - { - "name": "Подготовить образ диска", - "href": "/docs/compute/operations/image-create/custom-image", - "id": "c62b6f69dd4210d8621629962882fb3c" - }, - { - "name": "Загрузить свой образ", - "href": "/docs/compute/operations/image-create/upload", - "id": "9c9f49866e0e27f9d82c53c5c867494e" - } - ], - "id": "13e495f58a5a6827f0cdef10f18fd750" - }, - { - "name": "Управление образом", - "items": [ - { - "name": "Удалить образ", - "href": "/docs/compute/operations/image-control/delete", - "id": "9e68880ee44a373113ae28dd97c01880" - } - ], - "id": "afd841339862d4f32b8e3acb3bb20c49" - }, - { - "name": "Управление серийной консолью", - "items": [ - { - "name": "Начало работы", - "href": "/docs/compute/operations/serial-console/", - "id": "e9d715705edc19799d721119678c9ba9" - }, - { - "name": "Подключиться к серийной консоли по SSH", - "href": "/docs/compute/operations/serial-console/connect-ssh", - "id": "d6febb580c9522013585bdd67669e8c7" - }, - { - "name": "Подключиться к серийной консоли с помощью CLI", - "href": "/docs/compute/operations/serial-console/connect-cli", - "id": "e030afc9cc525dd989aba4885b382e09" - }, - { - "name": "Запустить командную оболочку в Windows SAC", - "href": "/docs/compute/operations/serial-console/windows-sac", - "id": "150209a9224a84f4486d55e062d00f13" - }, - { - "name": "Отключить доступ к серийной консоли", - "href": "/docs/compute/operations/serial-console/disable", - "id": "d7f703bee09caa5415e1d2e2b735d5d4" - } - ], - "id": "ffc8951f2fa849ea203d7e24ab6e7186" - }, - { - "name": "Создание группы виртуальных машин", - "items": [ - { - "name": "Создать группу ВМ фиксированного размера", - "href": "/docs/compute/operations/instance-groups/create-fixed-group", - "id": "dbfd6456f8e16d76b0a3a1964e1f60b8" - }, - { - "name": "Создать группу ВМ фиксированного размера с сетевым балансировщиком", - "href": "/docs/compute/operations/instance-groups/create-with-balancer", - "id": "38b1095f5e2b6149bd3ac710bcac5fe2" - }, - { - "name": "Создать автоматически масштабируемую группу ВМ", - "href": "/docs/compute/operations/instance-groups/create-autoscaled-group", - "id": "e21f55f1b5c7254008c2e37e39db4ab5" - }, - { - "name": "Создать группу ВМ с Container Optimized Image", - "href": "/docs/compute/operations/instance-groups/create-with-coi", - "id": "e922f477fedcf68783003128e6836fec" - } - ], - "id": "29ce44223e454ceb013860c3f5e7b2b2" - }, - { - "name": "Получение информации о группе виртуальных машин", - "items": [ - { - "name": "Получить список групп ВМ", - "href": "/docs/compute/operations/instance-groups/get-list", - "id": "6e849183d448865595463c5d388d3d9a" - }, - { - "name": "Получить информацию о группе ВМ", - "href": "/docs/compute/operations/instance-groups/get-info", - "id": "856fcfc3d82e23d1f26a526e7324376b" - }, - { - "name": "Получить список ВМ в группе", - "href": "/docs/compute/operations/instance-groups/get-list-instances", - "id": "c7f0bfa8c3a9a752ca4e4e2812f7418a" - } - ], - "id": "9a1f185f2447a61c048f9d5a05204a89" - }, - { - "name": "Управление группой виртуальных машин", - "items": [ - { - "name": "Изменить группу ВМ", - "href": "/docs/compute/operations/instance-groups/update", - "id": "dbbd57f0cd5b113b99bcea2322513ca1" - }, - { - "name": "Настроить проверку состояния приложения на ВМ", - "href": "/docs/compute/operations/instance-groups/enable-autohealing", - "id": "e3a003e332b4de7ce0c2e4c49804833f" - }, - { - "name": "Обновить группу", - "items": [ - { - "name": "Постепенное обновление", - "href": "/docs/compute/operations/instance-groups/deploy/rolling-update", - "id": "f2538229a48e285028645250e3b27fff" - }, - { - "name": "Обновление без простоя", - "href": "/docs/compute/operations/instance-groups/deploy/zero-downtime", - "id": "8a267fa54d9b5d8c564a451fbdd97018" - } - ], - "id": "cc0a67aa8973db0dc7ff8d56b9e6d863" - }, - { - "name": "Остановить группу ВМ", - "href": "/docs/compute/operations/instance-groups/stop", - "id": "6e7e81f59e17b5091f7c4287fb214773" - }, - { - "name": "Запустить группу ВМ", - "href": "/docs/compute/operations/instance-groups/start", - "id": "57585d922551d33e5ca925732579ab5a" - }, - { - "name": "Удалить группу ВМ", - "href": "/docs/compute/operations/instance-groups/delete", - "id": "a2b5a96ee776d990a8b1b5e1dc5ec5b3" - } - ], - "id": "e223df49ee0b73df2dd3d473c5e160ed" - }, - { - "name": "Выделенные хосты", - "items": [ - { - "name": "Создать ВМ в группе выделенных хостов", - "href": "/docs/compute/operations/dedicated-host/running-host-group-vms", - "id": "edf35741be1188f978c526a39b3fa618" - }, - { - "name": "Создать ВМ на выделенном хосте", - "href": "/docs/compute/operations/dedicated-host/running-host-vms", - "id": "a7371db28a644d2cd8126488f212d2c4" - } - ], - "id": "9b6d2dc4f06cb2b63dd03933a843010e" - } - ], - "id": "06211b0dd4947721dc2b15cbc35cd783" - }, - { - "name": "Yandex Container Solution", - "href": "/docs/cos", - "id": "4f713cceebd273aec0ce8d54e3bfd61c" - }, - { - "name": "Сценарии использования", - "items": [ - { - "name": "Настройка синхронизации времени NTP", - "href": "/docs/compute/solutions/ntp", - "id": "699440aa8e2a2ca7bbb347e239c9425d" - }, - { - "name": "Работа с группой ВМ с автоматическим масштабированием", - "href": "/docs/compute/solutions/vm-autoscale", - "id": "c62f66e69535d261ad97ad9b8c87fb84" - }, - { - "name": "Развертывание Remote Desktop Gateway", - "href": "/docs/compute/solutions/rds-gw", - "id": "02f5baeed5dfc97441e48500770ef4cb" - } - ], - "id": "62111ba2dea597fa4b7e1aed31fc64e7" - }, - { - "name": "Концепции", - "items": [ - { - "name": "Взаимосвязь ресурсов", - "href": "/docs/compute/concepts/", - "id": "2d00d0fab5f8529b918b661e1920f18e" - }, - { - "name": "Виртуальные машины", - "items": [ - { - "name": "Обзор", - "href": "/docs/compute/concepts/vm", - "id": "6ce77159075b0937bf43463b19400ac4" - }, - { - "name": "Платформы", - "href": "/docs/compute/concepts/vm-platforms", - "id": "c075341cb0ab99ce4f3e01f7971efd60" - }, - { - "name": "Уровни производительности vCPU", - "href": "/docs/compute/concepts/performance-levels", - "id": "d4e4d102765748f7ca142e1e2053ff91" - }, - { - "name": "Прерываемые виртуальные машины", - "href": "/docs/compute/concepts/preemptible-vm", - "id": "32ba37896370ce863d61279ac041cff3" - }, - { - "name": "Сеть на виртуальной машине", - "href": "/docs/compute/concepts/network", - "id": "2558324b1932419f89cfc9dabfb171d2" - }, - { - "name": "Программно-ускоренная сеть", - "href": "/docs/compute/concepts/software-accelerated-network", - "id": "4793a03826725c8fdeeb913058beb5f2" - }, - { - "name": "Динамическая миграция", - "href": "/docs/compute/concepts/live-migration", - "id": "8d595bf47cbd0d2e019445199a7dfe6e" - }, - { - "name": "Группы размещения ВМ", - "href": "/docs/compute/concepts/placement-groups", - "id": "d0f1db64f7ba8d0e59de5fd0fda2e59a" - }, - { - "name": "Статусы", - "href": "/docs/compute/concepts/vm-statuses", - "id": "43b3b28bc7b0ebe379f7c8c35e5e5b34" - }, - { - "name": "Метаданные", - "href": "/docs/compute/concepts/vm-metadata", - "id": "e1c95b834834045659eff415f7025d8a" - } - ], - "id": "1d6c11690e2bf720c57adce97f7dc5a7" - }, - { - "name": "Графические ускорители GPU и vGPU", - "href": "/docs/compute/concepts/gpus", - "id": "8667ac4e98c101e4bf99a29ac2b3940a" - }, - { - "name": "Диски", - "items": [ - { - "name": "Обзор", - "href": "/docs/compute/concepts/disk", - "id": "abb707d81ac66d8487efff92266d797f" - }, - { - "name": "Снимки дисков", - "href": "/docs/compute/concepts/snapshot", - "id": "c55c22b709a7854abb9d78028022992b" - }, - { - "name": "Группы размещения нереплицируемых дисков", - "href": "/docs/compute/concepts/disk-placement-group", - "id": "9f4c67900412fb1f107a4bb4c2938603" - } - ], - "id": "342192a76b117ba7a1790f1af41452be" - }, - { - "name": "Образы", - "href": "/docs/compute/concepts/image", - "id": "933a6662d39cd2dbac5f0d716a6b3b06" - }, - { - "name": "Группы виртуальных машин", - "items": [ - { - "name": "Обзор", - "href": "/docs/compute/concepts/instance-groups/", - "id": "7868565e9b66bae20549b5a2bc29447e" - }, - { - "name": "Доступ", - "href": "/docs/compute/concepts/instance-groups/access", - "id": "2d44df48a11dd67359b8b9d5f5a19f44" - }, - { - "name": "Шаблон виртуальной машины", - "href": "/docs/compute/concepts/instance-groups/instance-template", - "id": "57cb89522dfe46be0adb7e60dfe7b489" - }, - { - "name": "Переменные в шаблоне виртуальной машины", - "href": "/docs/compute/concepts/instance-groups/variables-in-the-template", - "id": "91fdb579790f8fa9f094dd0b9f31585d" - }, - { - "name": "Политики", - "items": [ - { - "name": "Обзор", - "href": "/docs/compute/concepts/instance-groups/policies/", - "id": "f3466c602803fc8e81092476f1b983b5" - }, - { - "name": "Политика распределения", - "href": "/docs/compute/concepts/instance-groups/policies/allocation-policy", - "id": "5e905361837425dae46e9c1262be7245" - }, - { - "name": "Политика развертывания", - "href": "/docs/compute/concepts/instance-groups/policies/deploy-policy", - "id": "b40369d01a92f89c002d92865e51a7a7" - }, - { - "name": "Политика масштабирования", - "href": "/docs/compute/concepts/instance-groups/policies/scale-policy", - "id": "3bc7477a5b720f275645c9fdf7e668d9" - } - ], - "id": "476b12b1ce9b4697c490bb1683c17f95" - }, - { - "name": "Типы масштабирования", - "href": "/docs/compute/concepts/instance-groups/scale", - "id": "9a9478592e46cd197131be9045fd67bf" - }, - { - "name": "Автоматическое восстановление", - "href": "/docs/compute/concepts/instance-groups/autohealing", - "id": "1cc2dd77752e0865498cdaf85cbb210e" - }, - { - "name": "Обновление", - "items": [ - { - "name": "Обзор", - "href": "/docs/compute/concepts/instance-groups/deploy/", - "id": "a368e738b64acd3c2e1d22609fbbaeb5" - }, - { - "name": "Распределение виртуальных машин по зонам", - "href": "/docs/compute/concepts/instance-groups/deploy/zones", - "id": "cdbc90b2e1196525fbb8407846c6940e" - }, - { - "name": "Алгоритм развертывания", - "href": "/docs/compute/concepts/instance-groups/deploy/deploy", - "id": "1ccf766d02dc0eb838f5db7354130323" - }, - { - "name": "Правила обновления виртуальных машин", - "href": "/docs/compute/concepts/instance-groups/deploy/instance", - "id": "418a33538fe126aa74fefe1f03b63df9" - }, - { - "name": "Изменение дополнительных дисков в шаблоне виртуальной машины", - "href": "/docs/compute/concepts/instance-groups/deploy/secondary-disk", - "id": "522dce017feed3b00b1f10d7bdd0149b" - } - ], - "id": "6d2d48de86f064426741d71455085e25" - }, - { - "name": "Статусы", - "href": "/docs/compute/concepts/instance-groups/statuses", - "id": "31db35561f58a4494b24f3145559b3f4" - } - ], - "id": "fbe7550f4fe70a41146457532da3d6f2" - }, - { - "name": "Выделенный хост", - "href": "/docs/compute/concepts/dedicated-host", - "id": "760f698a1e11b8f15102348be64658db" - }, - { - "name": "Резервное копирование", - "href": "/docs/compute/concepts/backups", - "id": "c60dedf0e50bab29bc0c924d9c1a609a" - }, - { - "name": "Квоты и лимиты", - "href": "/docs/compute/concepts/limits", - "id": "f921689102dc26f2c275626950a932bc" - } - ], - "id": "fb4285a2292fce2292d1cc009322b02e" - }, - { - "name": "Управление доступом", - "href": "/docs/compute/security/", - "id": "46e626b38a31451a1dc4dc759d1d4de3" - }, - { - "name": "Правила тарификации", - "items": [ - { - "name": "Действующие правила", - "href": "/docs/compute/pricing", - "id": "1739ac88cfe4424b5a4f22a90e8d780b" - }, - { - "name": "Архив", - "items": [ - { - "name": "До 1 января 2019 года", - "href": "/docs/compute/pricing-archive/pricing-01012019", - "id": "15f10124ab26784a2f0f8c72baa8d40a" - }, - { - "name": "С 1 января до 1 марта 2019 года", - "href": "/docs/compute/pricing-archive/pricing-01032019", - "id": "9b350bc153ed60ca00be3fe7ad4059a0" - }, - { - "name": "С 1 марта до 1 мая 2019 года", - "href": "/docs/compute/pricing-archive/pricing-30042019", - "id": "86db546d079e3bd7445d6d1f69757243" - } - ], - "id": "222722de5e9ba1ce9165b1b99524aa55" - } - ], - "id": "3bc947ea79e55d6ff2b6d9e5eb74ddca" - }, - { - "name": "Справочник API (англ.)", - "items": [ - { - "name": "Authentication", - "href": "/docs/compute/api-ref/authentication", - "id": "4065c2b4fcec9060399cab5e8636bda8" - }, - { - "name": "gRPC", - "items": [ - { - "name": "Overview", - "href": "/docs/compute/api-ref/grpc/", - "id": "7d33758f3b8effb2a5d0e9d6cdbdaf6a" - }, - { - "name": "DiskPlacementGroupService", - "href": "/docs/compute/api-ref/grpc/disk_placement_group_service", - "id": "fc9b237bce3986734dbc7133e81db823" - }, - { - "name": "DiskService", - "href": "/docs/compute/api-ref/grpc/disk_service", - "id": "183568887f8002b1d805ef31000ee8ab" - }, - { - "name": "DiskTypeService", - "href": "/docs/compute/api-ref/grpc/disk_type_service", - "id": "43880210bbcd80a05f2397386d753a50" - }, - { - "name": "HostGroupService", - "href": "/docs/compute/api-ref/grpc/host_group_service", - "id": "14b2b64ff13ae0d432308f15c9633b40" - }, - { - "name": "HostTypeService", - "href": "/docs/compute/api-ref/grpc/host_type_service", - "id": "15cd45977a9b8a946e47cbea1808a8c9" - }, - { - "name": "ImageService", - "href": "/docs/compute/api-ref/grpc/image_service", - "id": "83dfefde5b5e75865193adc192ccb451" - }, - { - "name": "InstanceService", - "href": "/docs/compute/api-ref/grpc/instance_service", - "id": "c766edb445cb0951fbce81ec973129cf" - }, - { - "name": "PlacementGroupService", - "href": "/docs/compute/api-ref/grpc/placement_group_service", - "id": "39758d87c7686a9c4461cf751f827ef8" - }, - { - "name": "SnapshotService", - "href": "/docs/compute/api-ref/grpc/snapshot_service", - "id": "a5458c54835ff07d7146829edac1ecc3" - }, - { - "name": "ZoneService", - "href": "/docs/compute/api-ref/grpc/zone_service", - "id": "4f71b442d3512c41e1bf1ee5fafba42c" - }, - { - "name": "InstanceGroupService", - "href": "/docs/compute/api-ref/grpc/instance_group_service", - "id": "eb8b6d6d7f0b6f2403cdf953368fe79d" - }, - { - "name": "OperationService", - "href": "/docs/compute/api-ref/grpc/operation_service", - "id": "d66c6d778116d6d68fe5feb94e50d916" - } - ], - "id": "1303e29f9c971322530f90473943f153" - }, - { - "name": "REST", - "items": [ - { - "name": "Overview", - "href": "/docs/compute/api-ref/", - "id": "206dafd3cbc05521d69e3e17a7e027fd" - }, - { - "name": "Disk", - "items": [ - { - "name": "Overview", - "href": "/docs/compute/api-ref/Disk/", - "id": "10c2dc04ad65390102cb067d3c63591d" - }, - { - "name": "create", - "href": "/docs/compute/api-ref/Disk/create", - "id": "57fd42d9dbf2a79c725246d6027dec60" - }, - { - "name": "delete", - "href": "/docs/compute/api-ref/Disk/delete", - "id": "27fcdea4b5a38c4778acf381b7f323e6" - }, - { - "name": "get", - "href": "/docs/compute/api-ref/Disk/get", - "id": "387479b42e1e5e5fabb3f74f1db71606" - }, - { - "name": "list", - "href": "/docs/compute/api-ref/Disk/list", - "id": "3507f355b267bc78f2c752d4f72061ad" - }, - { - "name": "listOperations", - "href": "/docs/compute/api-ref/Disk/listOperations", - "id": "a9b86fee5b49d955310beab3c9e8094f" - }, - { - "name": "update", - "href": "/docs/compute/api-ref/Disk/update", - "id": "a156926cd7645b84651382131f937ba3" - } - ], - "id": "d3fe77edd4d0f4dd9fc65d4f397ac71e" - }, - { - "name": "DiskPlacementGroup", - "items": [ - { - "name": "Overview", - "href": "/docs/compute/api-ref/DiskPlacementGroup/", - "id": "7cb17d1db5e91657508864de04489db7" - }, - { - "name": "create", - "href": "/docs/compute/api-ref/DiskPlacementGroup/create", - "id": "18d27168befb2e9ccc082f6d148b080b" - }, - { - "name": "delete", - "href": "/docs/compute/api-ref/DiskPlacementGroup/delete", - "id": "2ff143ecd3c6d5ac416ef4976ffd016b" - }, - { - "name": "get", - "href": "/docs/compute/api-ref/DiskPlacementGroup/get", - "id": "aa2d8f50e64933aba7523e507c8c81d3" - }, - { - "name": "list", - "href": "/docs/compute/api-ref/DiskPlacementGroup/list", - "id": "33656e7a90b6e9c8fe4f42c217a33a83" - }, - { - "name": "listDisks", - "href": "/docs/compute/api-ref/DiskPlacementGroup/listDisks", - "id": "83b5a71eaa9987c18ff7feab53417adf" - }, - { - "name": "listOperations", - "href": "/docs/compute/api-ref/DiskPlacementGroup/listOperations", - "id": "045e47f69b810ec6b79fff741ce0752a" - }, - { - "name": "update", - "href": "/docs/compute/api-ref/DiskPlacementGroup/update", - "id": "549ef65faf30eea0b0a3ac44658ed21a" - } - ], - "id": "91ec069554da6e246478cb3d782bd637" - }, - { - "name": "DiskType", - "items": [ - { - "name": "Overview", - "href": "/docs/compute/api-ref/DiskType/", - "id": "e80ac2765bd3b557fe1dfcf2b918129f" - }, - { - "name": "get", - "href": "/docs/compute/api-ref/DiskType/get", - "id": "5b6fe17b678326f1774a000e3349bf9b" - }, - { - "name": "list", - "href": "/docs/compute/api-ref/DiskType/list", - "id": "a99237dfcf1c7261cc7118f8104ed04c" - } - ], - "id": "0777ffeb4aa87404830de3d98d24806c" - }, - { - "name": "HostGroup", - "items": [ - { - "name": "Overview", - "href": "/docs/compute/api-ref/HostGroup/", - "id": "fd4788d84c5fe50da6387191a371cf99" - }, - { - "name": "create", - "href": "/docs/compute/api-ref/HostGroup/create", - "id": "1d7fead29ab1e5f657db327579290b9d" - }, - { - "name": "delete", - "href": "/docs/compute/api-ref/HostGroup/delete", - "id": "a41f33ed5c62843d7e6cf22cccdd677b" - }, - { - "name": "get", - "href": "/docs/compute/api-ref/HostGroup/get", - "id": "c1fed3a1da8b1d44b31bcf5e95d61f83" - }, - { - "name": "list", - "href": "/docs/compute/api-ref/HostGroup/list", - "id": "f472b2cc137d83848c2719ce8654da88" - }, - { - "name": "listHosts", - "href": "/docs/compute/api-ref/HostGroup/listHosts", - "id": "4db59034471bd3eb4797f96824e70654" - }, - { - "name": "listInstances", - "href": "/docs/compute/api-ref/HostGroup/listInstances", - "id": "7ce7be6bf220ae58e510ccd32016187d" - }, - { - "name": "listOperations", - "href": "/docs/compute/api-ref/HostGroup/listOperations", - "id": "bd1fb46734dc731fc4382fc9a47abec6" - }, - { - "name": "update", - "href": "/docs/compute/api-ref/HostGroup/update", - "id": "fa1f4d1cdf2572e1429e503f4fa8ae1a" - } - ], - "id": "b1705d18eaccc471487091dfc65838e6" - }, - { - "name": "HostType", - "items": [ - { - "name": "Overview", - "href": "/docs/compute/api-ref/HostType/", - "id": "da1073582ae7dc920a070c35e78e57fa" - }, - { - "name": "get", - "href": "/docs/compute/api-ref/HostType/get", - "id": "3ad68c7cb512d76d533b09137430f526" - }, - { - "name": "list", - "href": "/docs/compute/api-ref/HostType/list", - "id": "23673587930ef8826c09b29de3f43d98" - } - ], - "id": "d62dedb95fa88bf5d213408e619f6291" - }, - { - "name": "Image", - "items": [ - { - "name": "Overview", - "href": "/docs/compute/api-ref/Image/", - "id": "12f38909e9e20dd8a17ae95ce54d8596" - }, - { - "name": "create", - "href": "/docs/compute/api-ref/Image/create", - "id": "9e1a4a6496addad0b97be9b17dc11bcb" - }, - { - "name": "delete", - "href": "/docs/compute/api-ref/Image/delete", - "id": "cb0dac57aa204abd6104ce028ece8639" - }, - { - "name": "get", - "href": "/docs/compute/api-ref/Image/get", - "id": "49d88ad14c50de30f2bc6d55db88f0d2" - }, - { - "name": "getLatestByFamily", - "href": "/docs/compute/api-ref/Image/getLatestByFamily", - "id": "de72019255b5c136dba632a365348847" - }, - { - "name": "list", - "href": "/docs/compute/api-ref/Image/list", - "id": "de5ded9c8f876bc16fb093e71b5bfe7c" - }, - { - "name": "listOperations", - "href": "/docs/compute/api-ref/Image/listOperations", - "id": "d6d4979c0534148c56a56305361b2257" - }, - { - "name": "update", - "href": "/docs/compute/api-ref/Image/update", - "id": "dd33eb8bfb4ad1308b133604ba0e6626" - } - ], - "id": "8f62f52989f1eeaa0b9a43faf1c6d2c2" - }, - { - "name": "Instance", - "items": [ - { - "name": "Overview", - "href": "/docs/compute/api-ref/Instance/", - "id": "59c61b8c709f1a65662cc9c353e2000c" - }, - { - "name": "addOneToOneNat", - "href": "/docs/compute/api-ref/Instance/addOneToOneNat", - "id": "16c0de22c1badc8ccd180782bcd642ad" - }, - { - "name": "attachDisk", - "href": "/docs/compute/api-ref/Instance/attachDisk", - "id": "b6c923e6fc373a2efba43726f53a3ecb" - }, - { - "name": "create", - "href": "/docs/compute/api-ref/Instance/create", - "id": "c66ce715d6cb184d064cf7cc9694a14b" - }, - { - "name": "delete", - "href": "/docs/compute/api-ref/Instance/delete", - "id": "20d98520f4495fe6fd3d6b1aa2d6df0f" - }, - { - "name": "detachDisk", - "href": "/docs/compute/api-ref/Instance/detachDisk", - "id": "c0efb15408b03219f2ae8d0c0515c7c2" - }, - { - "name": "get", - "href": "/docs/compute/api-ref/Instance/get", - "id": "3ab1bf4644f6c25270ea6ef7245000f8" - }, - { - "name": "getSerialPortOutput", - "href": "/docs/compute/api-ref/Instance/getSerialPortOutput", - "id": "5c90204a67e5c2b65315f1e4418c5aba" - }, - { - "name": "list", - "href": "/docs/compute/api-ref/Instance/list", - "id": "3bc78b14cbac79c4fbcd9f897bbe52ea" - }, - { - "name": "listOperations", - "href": "/docs/compute/api-ref/Instance/listOperations", - "id": "0838d206cb281f0ee5f383b572d1b180" - }, - { - "name": "removeOneToOneNat", - "href": "/docs/compute/api-ref/Instance/removeOneToOneNat", - "id": "982c875a3d410a13db74c36cb4a1f1b4" - }, - { - "name": "restart", - "href": "/docs/compute/api-ref/Instance/restart", - "id": "2e15cb3d8ad4d1d05e87addd2ae85f71" - }, - { - "name": "start", - "href": "/docs/compute/api-ref/Instance/start", - "id": "1d6e14276ae4343534a750d3c617f58e" - }, - { - "name": "stop", - "href": "/docs/compute/api-ref/Instance/stop", - "id": "3fded9541f39283ee3e24cd67858b50b" - }, - { - "name": "update", - "href": "/docs/compute/api-ref/Instance/update", - "id": "11fc6acf5cab9ab1b1238f5390ed5903" - }, - { - "name": "updateMetadata", - "href": "/docs/compute/api-ref/Instance/updateMetadata", - "id": "9befc40838dd8e56183ab82c6eae2ecb" - }, - { - "name": "updateNetworkInterface", - "href": "/docs/compute/api-ref/Instance/updateNetworkInterface", - "id": "b087a67c55a0ddbc13c1e108cb5729b0" - } - ], - "id": "75621caf15b2ac16d9793e595d75758c" - }, - { - "name": "PlacementGroup", - "items": [ - { - "name": "Overview", - "href": "/docs/compute/api-ref/PlacementGroup/", - "id": "6e99bc9c10355a62957300d95b8e129f" - }, - { - "name": "create", - "href": "/docs/compute/api-ref/PlacementGroup/create", - "id": "45d68e07a4f69e223bfa96c5c54fed0b" - }, - { - "name": "delete", - "href": "/docs/compute/api-ref/PlacementGroup/delete", - "id": "578831ec3cfceccc19fa464fbdacdb3c" - }, - { - "name": "get", - "href": "/docs/compute/api-ref/PlacementGroup/get", - "id": "386083f739b14e9b696aa6d63e27cca7" - }, - { - "name": "list", - "href": "/docs/compute/api-ref/PlacementGroup/list", - "id": "2be8f3cd703838f80351e4a7c93e5694" - }, - { - "name": "listInstances", - "href": "/docs/compute/api-ref/PlacementGroup/listInstances", - "id": "eefe4737ebdf94e38c15c4c37fb7ea8f" - }, - { - "name": "listOperations", - "href": "/docs/compute/api-ref/PlacementGroup/listOperations", - "id": "8e89dc9276515e4b64f98f3b0e22f72a" - }, - { - "name": "update", - "href": "/docs/compute/api-ref/PlacementGroup/update", - "id": "ce58a7d9e59acf1993debd6207fef553" - } - ], - "id": "fa71bc1d3b6e7d2946c63add21c445d0" - }, - { - "name": "Snapshot", - "items": [ - { - "name": "Overview", - "href": "/docs/compute/api-ref/Snapshot/", - "id": "e3fa043c876b999d6ce86ce04c789706" - }, - { - "name": "create", - "href": "/docs/compute/api-ref/Snapshot/create", - "id": "675fa7429c69e7c860b92496ff6eebaf" - }, - { - "name": "delete", - "href": "/docs/compute/api-ref/Snapshot/delete", - "id": "0c405401a906732839dcaae0979e06b4" - }, - { - "name": "get", - "href": "/docs/compute/api-ref/Snapshot/get", - "id": "58a9e37a93fe61f9ea09989e26aabddd" - }, - { - "name": "list", - "href": "/docs/compute/api-ref/Snapshot/list", - "id": "f0d7cc1c7dc6b422efbbf804a50886b9" - }, - { - "name": "listOperations", - "href": "/docs/compute/api-ref/Snapshot/listOperations", - "id": "965270fffcfdaaa95151496ac1b3313f" - }, - { - "name": "update", - "href": "/docs/compute/api-ref/Snapshot/update", - "id": "9f895c5e30f4bcd3b9c667c4757aeb99" - } - ], - "id": "5ee42b468ddb414e2a8c7f29f8004018" - }, - { - "name": "Zone", - "items": [ - { - "name": "Overview", - "href": "/docs/compute/api-ref/Zone/", - "id": "d98945085a2a15d5534f216061bc2830" - }, - { - "name": "get", - "href": "/docs/compute/api-ref/Zone/get", - "id": "856253b846a25e8df3cc9ee6e2aefdd5" - }, - { - "name": "list", - "href": "/docs/compute/api-ref/Zone/list", - "id": "4589276c0069a5da6d180a2469b95150" - } - ], - "id": "87637f8990558328af315e0be8cc5645" - }, - { - "name": "Operation", - "items": [ - { - "name": "Overview", - "href": "/docs/compute/api-ref/Operation/", - "id": "af31006d769dd452e3db35fc50aeb40f" - }, - { - "name": "get", - "href": "/docs/compute/api-ref/Operation/get", - "id": "6a3541298c2be2b9efb0159646ff4006" - } - ], - "id": "6659a4de82e1b552285059d510a31b02" - }, - { - "name": "InstanceGroup", - "items": [ - { - "name": "Overview", - "href": "/docs/compute/api-ref/InstanceGroup/", - "id": "422ad4733fc1f431c6251bcaad1aef5c" - }, - { - "name": "list", - "href": "/docs/compute/api-ref/InstanceGroup/list", - "id": "aa28579ea49b026f5ff102f43caad4dc" - }, - { - "name": "get", - "href": "/docs/compute/api-ref/InstanceGroup/get", - "id": "444644e0a8093f0bcd80b1f686d0c128" - }, - { - "name": "listLogRecords", - "href": "/docs/compute/api-ref/InstanceGroup/listLogRecords", - "id": "1efb47decccf2cc2b9af872bb9d157eb" - }, - { - "name": "updateFromYaml", - "href": "/docs/compute/api-ref/InstanceGroup/updateFromYaml", - "id": "9585f5a13966e6aa6b0dabc0909d2a9d" - }, - { - "name": "updateAccessBindings", - "href": "/docs/compute/api-ref/InstanceGroup/updateAccessBindings", - "id": "344d58cc7834cc665e2d56b291b76e2f" - }, - { - "name": "stop", - "href": "/docs/compute/api-ref/InstanceGroup/stop", - "id": "3517e5448f34444c781ce803389411ca" - }, - { - "name": "start", - "href": "/docs/compute/api-ref/InstanceGroup/start", - "id": "240d9ffc106d620af16b733ea025a3a3" - }, - { - "name": "delete", - "href": "/docs/compute/api-ref/InstanceGroup/delete", - "id": "61525a20eb261219f092afd049dd203e" - }, - { - "name": "listInstances", - "href": "/docs/compute/api-ref/InstanceGroup/listInstances", - "id": "8ae489d16fbb65a7b6014cd180006dcd" - }, - { - "name": "createFromYaml", - "href": "/docs/compute/api-ref/InstanceGroup/createFromYaml", - "id": "c6ac1c0c0a86478f2f1abf6a9cb05cdc" - }, - { - "name": "update", - "href": "/docs/compute/api-ref/InstanceGroup/update", - "id": "db66fae49886770f8e1a085c3d768e66" - }, - { - "name": "setAccessBindings", - "href": "/docs/compute/api-ref/InstanceGroup/setAccessBindings", - "id": "13f8eb2b63c7957da2f83ec3e55bc046" - }, - { - "name": "listOperations", - "href": "/docs/compute/api-ref/InstanceGroup/listOperations", - "id": "4b933c0b6f02c32d0d79f5142fa6156e" - }, - { - "name": "create", - "href": "/docs/compute/api-ref/InstanceGroup/create", - "id": "cd15bb21eb1d72c71dc12e9707781275" - }, - { - "name": "listAccessBindings", - "href": "/docs/compute/api-ref/InstanceGroup/listAccessBindings", - "id": "e4f8edc4d48ecc2ce465ec3b55f6d1ec" - } - ], - "id": "b7a4e142bc80f9a5150c1cdaaa210892" - } - ], - "id": "bdf1444c41a074065430400fa317354a" - } - ], - "id": "84782b84f9addcd45b1809bee30342ed" - }, - { - "name": "Вопросы и ответы", - "items": [ - { - "name": "Общие вопросы", - "href": "/docs/compute/qa/general", - "id": "20cbea75319a2f7c9ff3a2b579238327" - }, - { - "name": "Виртуальные машины", - "href": "/docs/compute/qa/vm", - "id": "387c884cf5075a3837b5f3efd7df9ad4" - }, - { - "name": "Диски и снимки", - "href": "/docs/compute/qa/disks", - "id": "0f3cf57b87ab585e6ba472ff5a2e850c" - }, - { - "name": "Аварийное восстановление", - "href": "/docs/compute/qa/disaster-recovery", - "id": "26ef1411439e23e7c68a9d26c3cf923c" - }, - { - "name": "Лицензирование", - "href": "/docs/compute/qa/licensing", - "id": "7192931858e7517337a2535ddc4ae630" - }, - { - "name": "Все вопросы на одной странице", - "href": "/docs/compute/qa/all", - "id": "9c694a04c9e955a9c8ec10010c6f179f" - } - ], - "id": "1c4e87c19c3c976038a9de66e1a50663" - } - ], - "base": "ru/compute" - }, - "leading": true, - "data": { - "title": "Yandex Compute Cloud", - "description": [ - "Сервис Yandex Compute Cloud предоставляет масштабируемые вычислительные мощности для создания виртуальных машин и управления ими. Сервис поддерживает прерываемые виртуальные машины, а также отказоустойчивые группы виртуальных машин.", - "Вы можете подключать к виртуальным машинам диски с образами на базе OC Linux и Windows, доступные в Marketplace. Каждый диск автоматически реплицируется внутри своей зоны доступности, что обеспечивает надежное хранение данных. Также, для удобного переноса данных с одного диска на другой, Compute Cloud поддерживает снимки дисков.", - "Инфраструктура Yandex.Cloud защищена в соответствии с Федеральным законом Российской Федерации «О персональных данных» № 152-ФЗ.", - "Для сервиса действует соглашение об уровне обслуживания. Уровень обслуживания сервиса определен в документе Уровень обслуживания Yandex Compute Cloud." - ], - "meta": { - "title": "Yandex Compute Cloud" - }, - "links": [ - { - "title": "Начало работы", - "description": "Создайте первую виртуальную машину или группу ВМ", - "imgSrc": "http://s3.mds.yandex.net/catboost-opensource/icon__loss-functions.png", - "links": [ - { - "title": "Ссылка 1", - "imgSrc": "http://s3.mds.yandex.net/catboost-opensource/icon__loss-functions.png", - "href": "#" - } - ] - }, - { - "title": "Пошаговые инструкции", - "description": "Инструкции по выполнению рутинных операций", - "href": "operations/" - }, - { - "title": "Концепции", - "description": "Узнайте про взаимосвязь виртуальных машин, дисков, снимков и образов", - "href": "concepts/" - }, - { - "title": "Справочник API (англ.)", - "description": "Описание методов HTTP API", - "href": "api-ref/" - }, - { - "title": "Квоты и лимиты", - "description": "Технические и организационные ограничения сервиса", - "href": "concepts/limits" - }, - { - "title": "Управление доступом", - "description": "Настройте права доступа для работы с сервисом", - "href": "security/" - }, - { - "title": "Правила тарификации", - "description": "Цены и правила расчета стоимости услуг сервиса", - "href": "pricing" - } - ] - }, - "meta": { - "description": "", - "author": { - "avatar": "https://storage.yandexcloud.net/cloud-www-assets/constructor/content-program/icons/yandexcloud.svg", - "email": "", - "login": "", - "name": "Yandex.Cloud", - "url": "https://cloud.yandex.ru/" - } - } -} diff --git a/src/demo/Components/DocPage/data.ts b/src/demo/Components/DocPage/data.ts deleted file mode 100644 index c0ab6428..00000000 --- a/src/demo/Components/DocPage/data.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {Lang} from '../../../index'; -import pageContentRu from './page-ru.json'; -import pageContentEn from './page-en.json'; -import singlePageContentRu from './single-page-ru.json'; -import singlePageContentEn from './single-page-en.json'; - -export const getContent = (lang: Lang, singlePage: boolean) => { - if (singlePage && lang === 'ru') { - return singlePageContentRu; - } else if (singlePage) { - return singlePageContentEn; - } - - if (lang === 'ru') { - return pageContentRu; - } - - return pageContentEn; -}; diff --git a/src/demo/Components/DocPage/index.tsx b/src/demo/Components/DocPage/index.tsx deleted file mode 100644 index f28b2ab4..00000000 --- a/src/demo/Components/DocPage/index.tsx +++ /dev/null @@ -1,195 +0,0 @@ -import React, {useCallback, useEffect, useState} from 'react'; -import cn from 'bem-cn-lite'; -import {configure as configureUikit} from '@gravity-ui/uikit'; - -import {DocPage, FeedbackSendData, FeedbackType, Theme} from '../../../index'; -import Header from '../Header/Header'; -import {DEFAULT_SETTINGS, DISLIKE_VARIANTS} from '../../../constants'; -import {getIsMobile} from '../../controls/settings'; -import getLangControl from '../../controls/lang'; -import getVcsControl from '../../controls/vcs'; -import {getHasBookmark} from '../../decorators/bookmark'; -import {getHasSubscribe} from '../../decorators/subscribe'; -import {getContent} from './data'; - -import {join} from 'path'; - -const layoutBlock = cn('Layout'); - -function updateBodyClassName(theme: Theme) { - const bodyEl = document.body; - - if (!bodyEl.classList.contains('yc-root')) { - bodyEl.classList.add('yc-root'); - } - - bodyEl.classList.toggle('yc-root_theme_light', theme === 'light'); - bodyEl.classList.toggle('yc-root_theme_dark', theme === 'dark'); -} - -const DocPageDemo = () => { - const langValue = getLangControl(); - const vcsType = getVcsControl(); - const isMobile = getIsMobile(); - const hasBookmark = getHasBookmark(); - const hasSubscribe = getHasSubscribe(); - const router = {pathname: '/docs/overview/concepts/quotas-limits'}; - const langs = ['ru', 'en', 'cs']; - const pdfLink = 'https://doc.yandex-team.ru/help/diy/diy-guide.pdf'; - - const [fullScreen, onChangeFullScreen] = useState(DEFAULT_SETTINGS.fullScreen); - const [wideFormat, onChangeWideFormat] = useState(DEFAULT_SETTINGS.wideFormat); - const [showMiniToc, onChangeShowMiniToc] = useState(DEFAULT_SETTINGS.showMiniToc); - const [theme, onChangeTheme] = useState(DEFAULT_SETTINGS.theme); - const [textSize, onChangeTextSize] = useState(DEFAULT_SETTINGS.textSize); - const [singlePage, onChangeSinglePage] = useState(DEFAULT_SETTINGS.singlePage); - const [isLiked, setIsLiked] = useState(DEFAULT_SETTINGS.isLiked); - const [isDisliked, setIsDisliked] = useState(DEFAULT_SETTINGS.isDisliked); - const [isPinned, setIsPinned] = useState(DEFAULT_SETTINGS.isPinned as boolean); - const [lang, onChangeLang] = useState(langValue); - const [showSearchBar, setShowSearchBar] = useState(true); - const onCloseSearchBar = () => setShowSearchBar(false); - const [searchQuery, setSearchQuery] = useState(''); - const [searchWords, setSearchWords] = useState([]); - - const onSendFeedback = useCallback((data: FeedbackSendData) => { - const {type} = data; - - if (type === FeedbackType.like) { - setIsLiked(true); - setIsDisliked(false); - } else if (type === FeedbackType.dislike) { - setIsLiked(false); - setIsDisliked(true); - } else { - setIsLiked(false); - setIsDisliked(false); - } - - console.log('Feedback:', data); - }, []); - - const onChangeBookmarkPage = useCallback((data: boolean) => { - setIsPinned(data); - console.log(`This page pinned: ${data}`); - }, []); - - updateBodyClassName(theme); - - useEffect(() => { - const newSearchWords = searchQuery.split(' ').filter((word) => { - if (!word) { - return false; - } - - if (searchQuery.length > 10) { - return word.length > 3; - } - - return true; - }); - - setSearchWords(newSearchWords); - }, [searchQuery]); - - const onNotFoundWords = useCallback(() => { - console.log(`Not found words for the query: ${searchQuery}`); - }, [searchQuery]); - const onContentMutation = useCallback(() => { - console.log('onContentMutation'); - }, []); - const onContentLoaded = useCallback(() => { - console.log('onContentLoaded'); - }, []); - - const onChangeLangWrapper = useCallback((value) => { - onChangeLang(value); - configureUikit({lang: value}); - }, []); - - const props = { - ...getContent(lang, singlePage), - vcsType, - lang, - onChangeLang: onChangeLangWrapper, - langs, - router, - headerHeight: fullScreen ? 0 : 64, - fullScreen, - onChangeFullScreen, - wideFormat, - onChangeWideFormat, - showMiniToc, - onChangeShowMiniToc, - onSubscribe: hasSubscribe === 'true' ? () => {} : undefined, - theme, - onNotFoundWords, - onChangeTheme: (themeValue: Theme) => { - updateBodyClassName(themeValue); - onChangeTheme(themeValue); - }, - textSize, - onChangeTextSize, - singlePage, - onChangeSinglePage, - isLiked, - isDisliked, - onSendFeedback, - showSearchBar, - searchWords, - searchQuery, - onCloseSearchBar, - useSearchBar: true, - bookmarkedPage: hasBookmark === 'true' && isPinned, - onChangeBookmarkPage: hasBookmark === 'true' ? onChangeBookmarkPage : undefined, - pdfLink, - }; - - const tocTitleIcon = ( - - {/* eslint-disable-next-line max-len */} - - - ); - const convertPathToOriginalArticle = (path: string) => join('prefix', path); - const generatePathToVcs = (path: string) => - join(`https://github.com/yandex-cloud/docs/blob/master/${props.lang}`, path); - const renderLoader = () => 'Loading...'; - const onChangeSearch = (value: string) => { - setShowSearchBar(true); - setSearchQuery(value); - }; - - return ( -
- {props.fullScreen ? null : ( -
- )} -
- -
-
- ); -}; - -export default DocPageDemo; diff --git a/src/demo/Components/DocPage/page-en.json b/src/demo/Components/DocPage/page-en.json deleted file mode 100644 index 8119c91a..00000000 --- a/src/demo/Components/DocPage/page-en.json +++ /dev/null @@ -1,260 +0,0 @@ -{ - "html": "

Yandex.Cloud services can be subject to quotas and limits:

\n
    \n
  • Quotas are organizational restrictions that can be changed by technical support on request.
  • \n
  • Limits are technical restrictions due to the Yandex.Cloud architecture specifics. The limits cannot be changed.
  • \n
\n

You can view your current account quotas in the management console.

\n

When designing your infrastructure in Yandex.Cloud, consider the limits as the boundary of opportunities that Yandex Cloud can provide to you. Quotas are changeable restrictions that can potentially be increased to the values of limits.

\n

Why quotas are needed

\n

Quotas serve as a soft restriction for requesting resources, and allow Yandex Cloud to guarantee the stability of the service: new users cannot take up too much resources for testing purposes. If you are ready to use more resources, contact the technical support and tell us exactly which quotas you need to increase, and how.

\n

Technical support decides whether or not to increase quotas on an individual basis.

\n

Quotas and limits defaults for Yandex.Cloud services

\n

Quotas are listed with default values that match the quotas of the trial period.

\n

Yandex Compute Cloud

\n

Quotas

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Number of virtual machines per cloud12
Total number of vCPUs for all VMs per cloud32
Total virtual memory for all VMs per cloud128 GB
Total number of disks per cloud32
Total SSD storage capacity per cloud200 GB
Total HDD storage capacity per cloud500 GB
Total number of disk snapshots per cloud32
Total storage capacity of all disk snapshots per cloud400 GB
Number of images per cloud8
Number of instance groups per cloud10
Total number of GPUs for all VMs per cloud*0
Number of concurrent operations in the cloud15
Maximum number of VM instances in a placement group5
Maximum number of placement groups per cloud2
\n

* To create a VM with a GPU, contact technical support.

\n

VM limits

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Maximum number of vCPUs per VM32 and 64 for Intel Broadwell and Intel Cascade Lake platforms, respectively
Maximum virtual memory per VM256 GB and 512 GB for Intel Broadwell and Intel Cascade Lake platforms, respectively
Maximum number of disks connected to a single VM7
Maximum number of GPUs connected to a single VM4
Maximum number of vCPUs for VMs with GPUs32
Maximum RAM for VMs with GPUs384
\n

Disk limits

\n
\n
\n
Network SSD
\n
Network HDD
\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Maximum disk size4 TB
Maximum disk snapshot size4 TB
Allocation unit size32 GB
Maximum* IOPS for writes, per disk40,000
Maximum* IOPS for writes, per allocation unit1000
Maximum** bandwidth for writes, per disk450 MB/s
Maximum** bandwidth for writes, per allocation unit15 MB/s
Maximum* IOPS for reads, per disk12,000
Maximum* IOPS for reads, per allocation unit400
Maximum** bandwidth for reads, per disk450 MB/s
Maximum** bandwidth for reads, per allocation unit15 MB/s
\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Maximum disk size4 TB
Maximum disk snapshot size4 TB
Allocation unit size256 GB
Maximum* IOPS for writes, per disk11,000
Maximum* IOPS for writes, per allocation unit300
Maximum** bandwidth for writes, per disk240 MB/s
Maximum** bandwidth for writes, per allocation unit30 MB/s
Maximum* IOPS for reads, per disk300
Maximum* IOPS for reads, per allocation unit100
Maximum** bandwidth for reads, per disk240 MB/s
Maximum** bandwidth for reads, per allocation unit30 MB/s
\n
\n
\n
*
\n

To achieve maximum IOPS, we recommend performing read and write operations that are 4 KB and less.

\n
**
\n

To achieve the maximum possible bandwidth, we recommend performing 4 MB reads and writes.

\n

Yandex Object Storage

\n

Quotas

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Storage volume in a cloud5 TB
Number of buckets per cloud25
\n

Limits

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Maximum object size5 TB
Total header size per request to the HTTP API8 KB
Size of user-defined metadata in an object2 KB
Maximum size of data to be uploaded per request5 GB
Minimum size of data parts for multipart uploading, except the last one5 MB
Maximum number of parts in multi-part uploading10,000
\n

Yandex Virtual Private Cloud

\n

Quotas

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Number of cloud networks per cloud2
Number of subnets per cloud6
Number of all public IP addresses per cloud8
Number of static public IP addresses per cloud2
Number of route tables per cloud8
Number of static routes per cloud256
\n

Limits

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Minimum CIDR size for a subnet/28
Maximum CIDR size for a subnet/16
Maximum number of simultaneous TCP/UDP connections per VM50000
\n

Filtering outgoing traffic

\n

Yandex.Cloud always blocks outgoing traffic to TCP port 25:

\n
    \n
  • To any servers on the internet other than Yandex.Mail servers.
  • \n
  • To Yandex Compute Cloud VMs when accessed via a public IP address.
  • \n
\n

Yandex.Cloud may open TCP port 25 by request via technical support if you comply with Acceptable Use Policy. Yandex.Cloud is entitled to block outgoing traffic on TCP port 25 if you violate the Use Policy.

\n

Yandex Resource Manager

\n

There are no quotas or limits for Yandex Resource Manager.

\n

Yandex Load Balancer

\n

Quotas

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Number of load balancers per cloud2
Number of target groups per cloud2
\n

Limits

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Number of resources per target group254
Number of listening ports10
Number of health checks per attached target group1
Status check protocolTCP, HTTP
\n

Other restrictions

\n

A particular target group can only contain target resources from a single cloud network.

\n

A target group can include resources that are connected to the same subnet within a single availability zone.

\n

You can create a load balancer without a listener.

\n

Health checks are transmitted from the IP address range 198.18.235.0/24.

\n

When connecting resources to the load balancer, keep in mind the limit on the maximum number of simultaneous TCP/UDP connections per VM.

\n

Yandex Managed Service for ClickHouse

\n

Quotas

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Number of clusters per cloud16
Total number of processor cores for all DB hosts per cloud64
Total virtual memory for all DB hosts per cloud512 GB
Total storage capacity for all clusters per cloud4096 GB
\n

Limits

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Lowest host classb1.nano (5% × 2 vCPU Intel Broadwell, 2 GB RAM)
Highest host classm2.8xlarge (64 vCPU Intel Cascade Lake, 512 GB RAM)
Maximum number of hosts in the cluster10
Maximum network storage capacity4096 GB
Maximum local storage capacity1400 GB
\n

Yandex Managed Service for MongoDB

\n

Quotas

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Number of clusters per cloud16
Total number of processor cores for all DB hosts per cloud64
Total virtual memory for all DB hosts per cloud512 GB
Total storage capacity for all clusters per cloud4096 GB
\n

Limits

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Lowest host classb1.nano (5% × 2 vCPU Intel Broadwell, 2 GB RAM)
Highest host classm2.8xlarge (64 vCPU Intel Cascade Lake, 512 GB RAM)
Maximum number of hosts per cluster MongoDB5
Maximum storage capacity for a cluster MongoDB512 GB
\n

Yandex Managed Service for MySQL

\n

Quotas

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Number of clusters per cloud16
Total number of processor cores for all DB hosts per cloud64
Total virtual memory for all DB hosts per cloud512 GB
Total storage capacity for all clusters per cloud4096 GB
\n

Limits

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Lowest host classb1.nano (5% × 2 vCPU Intel Broadwell, 2 GB RAM)
Highest host classm2.8xlarge (64 vCPU Intel Cascade Lake, 512 GB RAM)
Maximum number of hosts per cluster7
Maximum network storage capacity2048 GB
Maximum local storage capacity1400 GB
\n

Yandex Managed Service for PostgreSQL

\n

Quotas

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Number of clusters per cloud16
Total number of processor cores for all DB hosts per cloud64
Total virtual memory for all DB hosts per cloud512 GB
Total storage capacity for all clusters per cloud4096 GB
\n

Limits

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Lowest host classb1.nano (5% × 2 vCPU Intel Broadwell, 2 GB RAM)
Highest host classm2.8xlarge (64 vCPU Intel Cascade Lake, 512 GB RAM)
Maximum number of hosts per cluster10
Maximum network storage capacity2048 GB
Maximum local storage capacity1400 GB
\n

Yandex Managed Service for Redis

\n

Quotas

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Number of clusters per cloud16
Total number of processor cores for all DB hosts per cloud64
Total virtual memory for all hosts per cloud512 GB
Total disk storage capacity for all clusters per cloud4096 GB
\n

Limits

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Lowest host classb1.nano (burstable with 2 GB RAM)
Highest host classhm1.9xlarge (high-memory with 256 GB RAM)
Maximum number of hosts per cluster7
Minimum disk size per cluster2 times more than the amount of RAM selected
Maximum disk size per cluster8 times more than the amount of RAM selected
\n

Yandex Message Queue

\n

Quotas

\n
Messages
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Number of SendMessage and SendMessageBatch calls per queue300 calls per second for standard queues

30 calls per second for FIFO queues
Number of ReceiveMessage calls per queue300 calls per second for standard queues

30 calls per second for FIFO queues
Number of DeleteMessage and DeleteMessageBatch calls per queue300 calls per second for standard queues

30 calls per second for FIFO queues
Number of ChangeMessageVisibility and ChangeMessageVisibilityBatch calls per queue300 calls per second for standard queues

30 calls per second for FIFO queues
Number of CreateQueue calls per cloud2 calls per second
Number of DeleteQueue calls per cloud5 calls per second
Number of other request calls per cloud100 calls per second
Number of queues per cloud10
\n

Limits

\n
Queues
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Minimum message enqueue delay (DelaySeconds parameter)0 seconds
Maximum message enqueue delay (DelaySeconds parameter)900 seconds (15 minutes)
Number of messages being processed per standard queue120,000
Number of messages being processed per FIFO queue20,000
Queue nameMaximum of 80 characters, including numbers, small and capital Latin letters, hyphens, and underscores. The name of a FIFO queue must end with the .fifo suffix.
\n
Messages
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Batch entry IDMaximum of 80 characters, including numbers, small and capital Latin letters, hyphens, and underscores.
Maximum number of message attributes10
Maximum number of entries per batch10
Message contentXML, JSON, and unformatted text. The following Unicode characters are supported:
  • #x9
  • #xA
  • #xD
  • from #x20 to #xD7FF
  • from #xE000 to #xFFFD
  • from #x10000 to #x10FFFF
Maximum period for retaining messages in a queue1209600 seconds (14 days)
Minimum period for retaining messages in a queue60 seconds (1 minute)
Maximum message enqueue delay (DelaySeconds parameter)900 seconds (15 minutes)
Minimum message enqueue delay (DelaySeconds parameter)0 seconds
Maximum message size262144 bytes (256 KB)
Minimum message size1 byte
Maximum message visibility timeout12 hours
Minimum message visibility timeout0 seconds
Maximum clients waiting time to wait for the message from an empty queue (WaitTimeSeconds parameter)20 seconds
\n

Yandex SpeechKit

\n

Quotas

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Short audio recognition
Requests per second20
Streaming mode of short audio recognition
Requests per second40
Long audio recognition
Recognition requests per hour500
Operation status check requests per hour2500
Billable hours of audio per day10,000
Speech synthesis
Requests per second40
\n

Limits

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Short audio recognition
Maximum file size1 MB
Maximum duration of audio30 seconds
Maximum number of audio channels1
Streaming mode of short audio recognition
Maximum duration of transmitted audio for the entire session5 minutes
Maximum size of transmitted audio data10 MB
Maximum number of audio channels1
Long audio recognition
Maximum file size1 GB
Maximum duration of audio4 hours
Period for storing recognition results on the server3 days
Speech synthesis
Maximum request size5000 characters
\n

Yandex Translate

\n

Quotas

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Calls of a single API method per second20
Characters sent for translation per hour1 million
Characters sent for language detection per hour1 million
\n

Limits

\n

There are no limits in the service. For limitations on the field values in the request body, see the API reference.

\n

Yandex Vision

\n

Quotas

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Number of requests per second10
Text recognition attempts per second5
Face detection attempts per second5
Image classification attempts per second5
\n

Limits

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Type of limitValue
Maximum file size1 MB
Maximum image size20 MP (length × width)
Maximum number of pages in a PDF file8
\n", - "title": "Quotas and limits", - "headings": [ - { - "title": "Why quotas are needed", - "href": "#quotas", - "level": 2 - }, - { - "title": "Quotas and limits defaults for Yandex.Cloud services", - "href": "#quotas-and-limits-defaults-for-yandex.cloud-services", - "level": 2, - "items": [ - { - "title": "Yandex Compute Cloud", - "href": "#compute", - "level": 3, - "items": [] - }, - { - "title": "Yandex Object Storage", - "href": "#storage", - "level": 3, - "items": [] - }, - { - "title": "Yandex Virtual Private Cloud", - "href": "#vpc", - "level": 3, - "items": [] - }, - { - "title": "Yandex Resource Manager", - "href": "#resource-manager", - "level": 3 - }, - { - "title": "Yandex Load Balancer", - "href": "#load-balancer", - "level": 3, - "items": [] - }, - { - "title": "Yandex Managed Service for ClickHouse", - "href": "#mch", - "level": 3, - "items": [] - }, - { - "title": "Yandex Managed Service for MongoDB", - "href": "#mmg", - "level": 3, - "items": [] - }, - { - "title": "Yandex Managed Service for MySQL", - "href": "#mmy", - "level": 3, - "items": [] - }, - { - "title": "Yandex Managed Service for PostgreSQL", - "href": "#mpg", - "level": 3, - "items": [] - }, - { - "title": "Yandex Managed Service for Redis", - "href": "#mrd", - "level": 3, - "items": [] - }, - { - "title": "Yandex Message Queue", - "href": "#mq", - "level": 3, - "items": [] - }, - { - "title": "Yandex SpeechKit", - "href": "#speechkit", - "level": 3, - "items": [] - }, - { - "title": "Yandex Translate", - "href": "#translate", - "level": 3, - "items": [] - }, - { - "title": "Yandex Vision", - "href": "#vision", - "level": 3, - "items": [] - } - ] - } - ], - "meta": { - "author": { - "avatar": "", - "email": "robot-dataui-vcs@yandex-team.ru", - "login": "", - "name": "", - "url": "http://yandex.ru/" - }, - "contributors": [ - { - "avatar": "https://avatars.githubusercontent.com/u/2485945?v=6", - "email": "robot-dataui-vcs@yandex-team.ru", - "login": "", - "name": "DataUI VCS Robot", - "url": "" - }, - { - "avatar": "", - "email": "skanunnikov@yandex-team.ru", - "login": "", - "name": "Sergey Kanunnikov", - "url": "" - }, - { - "avatar": "https://avatars.githubusercontent.com/u/2485935?v=4", - "email": "", - "login": "zamulla", - "name": "Aleksey Zamulla", - "url": "http://yandex.ru/" - }, - { - "avatar": "https://avatars.githubusercontent.com/u/2485945?v=6", - "email": "migelle@yandex-team.ru", - "login": "migelle", - "name": "", - "url": "http://yandex.ru/" - }, - { - "avatar": "https://avatars.githubusercontent.com/u/2485945?v=6", - "email": "leeuw@yandex-team.ru", - "login": "", - "name": "", - "url": "http://yandex.ru/" - }, - { - "avatar": "", - "email": "dottir@yandex-team.ru", - "login": "", - "name": "Anastasia Karavaeva", - "url": "" - } - ] - }, - "toc": { - "title": "Yandex.Cloud overview", - "href": "/docs/overview/", - "items": [ - { - "name": "Yandex.Cloud services", - "href": "/docs/overview/concepts/services", - "id": "86fb5a9101b917e55d57955e41a4a773" - }, - { - "name": "Equivalent services on other platforms", - "expanded": true, - "items": [ - { - "name": "Overview", - "href": "/docs/overview/platform-comparison/", - "id": "db26ca551a9e94ece774f19a472cc500" - }, - { - "name": "Equivalents for Amazon Web Services", - "href": "/docs/overview/platform-comparison/aws", - "id": "c735da730d10e0f31b4b9a722251c0f0" - }, - { - "name": "Equivalents for Google Cloud Platform", - "href": "/docs/overview/platform-comparison/gcp", - "id": "f7aff73e87f0aca60595896b050a002d" - }, - { - "name": "Equivalents for Microsoft Azure", - "href": "/docs/overview/platform-comparison/azure", - "id": "ca1deb97788e2d54631f4908ac1b5ca6" - } - ], - "id": "c5625beecf9017c1290bd748fd9dc854" - }, - { - "name": "Availability zones", - "href": "/docs/overview/concepts/geo-scope", - "id": "685e6c38153524c07dcd17f415e1f516" - }, - { - "name": "Getting started", - "href": "/docs/overview/quickstart", - "id": "ab338ee86e023121249934c955ed9810" - }, - { - "name": "Release stages", - "href": "/docs/overview/concepts/launch-stages", - "id": "de2baab2bd603f5630a1a398d25fa370" - }, - { - "name": "Quotas and limits", - "href": "/docs/overview/concepts/quotas-limits", - "id": "a3bb97002572fbdca0f056d75d42acd2" - }, - { - "name": "API", - "href": "/docs/overview/api", - "id": "136bdc27253376a450eee99e4539c186" - }, - { - "name": "Security and compliance", - "items": [ - { - "name": "Security bulletin", - "href": "/docs/overview/security-bulletins/", - "id": "51e1474da0ee2ab04126285a0e3ca7df" - }, - { - "name": "Rules for performing external security scans", - "href": "/docs/overview/compliance/pentest", - "id": "50ced679abe4df4b724e5b2dc462846e" - } - ], - "id": "a2541cbb86e50d2f6ed9c2eba245611d" - }, - { - "name": "Deleting user data", - "href": "/docs/overview/concepts/data-deletion", - "id": "bba0c3f3dc2cd1b71c1aeb83d67d77a8" - }, - { - "name": "SLA", - "href": "/docs/overview/sla", - "id": "1636c6203504367fc47f73077e95ebad" - }, - { - "name": "Questions and answers", - "href": "/docs/overview/qa", - "id": "6293190c6aa223973af0014e104c70ef" - } - ] - }, - "breadcrumbs": [ - { - "name": "Documentation" - }, - { - "name": "Quotas and limits" - } - ], - "filePath": "/cache/c12878f1-cc2e-445f-8f96-7374fe76b074/en/overview/concepts/quotas-limits.md", - "githubUrl": "https://github.com/yandex-cloud/docs/tree/master/en/overview/concepts/quotas-limits.md", - "vcsType": "github", - "vcsUrl": "https://github.com/yandex-cloud/docs/tree/master/en/overview/concepts/quotas-limits.md" -} diff --git a/src/demo/Components/DocPage/page-ru.json b/src/demo/Components/DocPage/page-ru.json deleted file mode 100644 index fa452b63..00000000 --- a/src/demo/Components/DocPage/page-ru.json +++ /dev/null @@ -1,271 +0,0 @@ -{ - "html": "

В сервисах Яндекс.Облака могут действовать квоты и лимиты:

\n
    \n
  • Квоты — организационные ограничения, которые можно изменить по запросу в техническую поддержку.
  • \n
  • Лимиты — технические ограничения, обусловленные особенностями архитектуры Яндекс.Облака. Изменение лимитов невозможно.
  • \n
\n

Квоты, установленные для вашего аккаунта, можно посмотреть в консоли управления.

\n

Проектируя инфраструктуру в Облаке, учитывайте лимиты как предел возможностей, которые Облако может вам предоставить. Квоты — изменяемые ограничения, которые потенциально могут быть увеличены до значения лимитов.

\n

Зачем нужны квоты

\n

Квоты служат мягким ограничением для запроса ресурсов, и позволяют Облаку гарантировать стабильность работы сервиса: новые пользователи не могут занять слишком много ресурсов в тестовых целях. Если вы готовы использовать большее количество ресурсов, обратитесь в техническую поддержку и расскажите, какие именно квоты нужно увеличить, и каким образом.

\n

Техническая поддержка принимает решение увеличивать или не увеличивать квоты в индивидуальном порядке.

\n

Квоты и лимиты по умолчанию для сервисов Облака

\n

Квоты приведены со значениями по умолчанию, которые совпадают с квотами на время пробного периода.

\n

Yandex Compute Cloud

\n

Квоты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Количество виртуальных машин в одном облаке12
Суммарное количество vCPU для всех виртуальных машин в одном облаке32
Суммарный объем виртуальной памяти для всех виртуальных машин в одном облаке128 ГБ
Суммарное количество дисков в одном облаке32
Суммарный объем SSD-дисков в одном облаке200 ГБ
Суммарный объем HDD-дисков в одном облаке500 ГБ
Суммарное количество снимков дисков в одном облаке32
Суммарный объем всех снимков дисков в одном облаке400 ГБ
Количество образов в одном облаке8
Количество групп виртуальных машин в одном облаке10
Суммарное количество GPU для всех виртуальных машин в одном облаке*0
Количество одновременно выполняемых операций в облаке15
Максимальное количество ВМ в одной группе размещения5
Максимальное количество групп размещения в одном облаке2
\n

* Чтобы создать виртуальную машину с GPU, обратитесь в техническую поддержку.

\n

Лимиты виртуальных машин

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Максимальное количество vCPU для одной виртуальной машины32 и 64 для платформ Intel Broadwell и Intel Cascade Lake соответственно
Максимальный объем виртуальной памяти для одной виртуальной машины256 ГБ и 512 ГБ для платформ Intel Broadwell и Intel Cascade Lake соответственно
Максимальное количество дисков, подключенных к одной виртуальной машине7
Максимальное количество GPU, подключенных к одной виртуальной машине4
Максимальное количество vCPU для виртуальных машин с GPU32
Максимальное количество RAM для виртуальных машин с GPU384
\n

Лимиты дисков

\n
\n
\n
Сетевой SSD-диск
\n
Cетевой HDD-диск
\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Максимальный размер диска4 ТБ
Максимальный размер снимка диска4 ТБ
Размер блока размещения32 ГБ
Максимальный* IOPS на запись, на 1 диск40000
Максимальный* IOPS на запись, на блок размещения1000
Максимальная** пропускная способность на запись, на 1 диск450 МБ/с
Максимальная** пропускная способность на запись, на блок размещения15 МБ/с
Максимальный* IOPS на чтение, на 1 диск12000
Максимальный* IOPS на чтение, на блок размещения400
Максимальная** пропускная способность на чтение, на 1 диск450 МБ/с
Максимальная** пропускная способность на чтение, на блок размещения15 МБ/с
\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Максимальный размер диска4 ТБ
Максимальный размер снимка диска4 ТБ
Размер блока размещения256 ГБ
Максимальный* IOPS на запись, на 1 диск11000
Максимальный* IOPS на запись, на блок размещения300
Максимальная** пропускная способность на запись, на 1 диск240 МБ/с
Максимальная** пропускная способность на запись, на блок размещения30 МБ/с
Максимальный* IOPS на чтение, на 1 диск300
Максимальный* IOPS на чтение, на блок размещения100
Максимальная** пропускная способность на чтение, на 1 диск240 МБ/с
Максимальная** пропускная способность на чтение, на блок размещения30 МБ/с
\n
\n
\n

Операции чтения и записи потребляют один и тот же дисковый ресурс — чем больше производится операций чтения, тем меньше операций записи, и наоборот. Подробнее читайте в разделе Диски.

\n
*
\n

Для получения максимального значения IOPS рекомендуется делать чтения и записи, не превышающие 4 КБ.

\n
**
\n

Для получения максимального значения пропускной способности рекомендуется делать чтения и записи размером 4 МБ.

\n

Yandex Object Storage

\n

Квоты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Объем хранилища в одном облаке5 ТБ
Количество бакетов в одном облаке25
\n

Лимиты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Максимальный размер объекта5 TБ
Общий размер заголовков для 1 запроса к HTTP API8 KБ
Размер пользовательских метаданных объекта2 KБ
Максимальный размер данных для загрузки за 1 запрос5 ГБ
Минимальный размер части данных для составной загрузки, кроме последнего5 МБ
Максимальное количество частей в составной загрузке10000
\n

Yandex Virtual Private Cloud

\n

Квоты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Количество облачных сетей в одном облаке2
Количество подсетей в одном облаке6
Количество всех публичных IP-адресов в одном облаке8
Количество статических публичных IP-адресов в одном облаке2
Количество таблиц маршрутизации в одном облаке8
Количество статических маршрутов в одном облаке256
\n

Лимиты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Минимальный размер CIDR для подсети/28
Максимальный размер CIDR для подсети/16
Максимальное количество одновременно установленных TCP/UDP-соединений для одной виртуальной машины50000
\n

Фильтрация исходящего трафика

\n

В Яндекс.Облаке всегда блокируется исходящий трафик на TCP-порт 25:

\n
    \n
  • на любые серверы в интернете, кроме почтовых серверов Яндекс.Почты;
  • \n
  • на виртуальные машины Yandex Compute Cloud, при обращении через публичный IP-адрес.
  • \n
\n

Яндекс.Облако может открыть TCP-порт 25 по запросу в поддержку, если вы соблюдаете Правила допустимого использования. При этом Яндекс.Облако всегда может снова заблокировать исходящий трафик на TCP-порте 25, если вы нарушите Правила.

\n

Yandex Resource Manager

\n

Квоты и лимиты для сервиса Yandex Resource Manager не определены.

\n

Yandex Load Balancer

\n

Квоты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Количество балансировщиков в одном облаке2
Количество целевых групп в одном облаке2
\n

Лимиты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Количество ресурсов в целевой группе254
Количество портов обработчика10
Количество проверок состояния на подключенную целевую группу1
Протокол проверок состоянияTCP, HTTP
\n

Прочие ограничения

\n

В одной целевой группе могут находиться целевые ресурсы только из одной облачной сети.

\n

В пределах одной зоны доступности в целевую группу могут входить ресурсы, подключенные к одной подсети.

\n

Можно создать балансировщик без обработчика.

\n

Проверки состояния передаются из диапазона адресов 198.18.235.0/24.

\n

При подключении ресурсов к балансировщику учитывайте лимит на максимальное количество одновременно установленных TCP/UDP-соединений для одной виртуальной машины.

\n

Yandex Managed Service for ClickHouse

\n

Квоты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Количество кластеров в одном облаке16
Суммарное количество ядер процессора для всех хостов баз данных в одном облаке64
Суммарный объем виртуальной памяти для всех хостов баз данных в одном облаке512 ГБ
Суммарный объем хранилищ для всех кластеров в одном облаке4096 ГБ
\n

Лимиты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Минимальный класс хостаb1.nano (5% × 2 vCPU Intel Broadwell, 2 ГБ RAM)
Максимальный класс хостаm2.8xlarge (64 vCPU Intel Cascade Lake, 512 ГБ RAM)
Максимальное количество шардов в одном кластере10
Максимальное количество хостов в одном шарде7
Максимальное количество хостов в одном кластере73 (10 шардов × 7 хостов + 3 хоста ZooKeeper)
Максимальный объем данных при использовании сетевого хранилища4096 ГБ
Максимальный объем данных при использовании локального хранилища1400 ГБ
\n

Yandex Managed Service for MongoDB

\n

Квоты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Количество кластеров в одном облаке16
Суммарное количество ядер процессора для всех хостов баз данных в одном облаке64
Суммарный объем виртуальной памяти для всех хостов баз данных в одном облаке512 ГБ
Суммарный объем хранилищ для всех кластеров в одном облаке4096 ГБ
\n

Лимиты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Минимальный класс хостаb1.nano (5% × 2 vCPU Intel Broadwell, 2 ГБ RAM)
Максимальный класс хостаm2.8xlarge (64 vCPU Intel Cascade Lake, 512 ГБ RAM)
Максимальное количество шардов в одном кластере MongoDB10
Максимальное количество хостов в одном шарде7
Максимальное количество хостов в одном кластере70 (10 шардов × 7 хостов)
Максимальный объем хранилища для кластера512 ГБ
\n

Yandex Managed Service for MySQL

\n

Квоты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Количество кластеров в одном облаке16
Суммарное количество ядер процессора для всех хостов баз данных в одном облаке64
Суммарный объем виртуальной памяти для всех хостов баз данных в одном облаке512 ГБ
Суммарный объем хранилищ для всех кластеров в одном облаке4096 ГБ
\n

Лимиты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Минимальный класс хостаb1.nano (5% × 2 vCPU Intel Broadwell, 2 ГБ RAM)
Максимальный класс хостаm2.8xlarge (64 vCPU Intel Cascade Lake, 512 ГБ RAM)
Максимальное количество хостов в одном кластере7
Максимальный объем данных при использовании сетевого хранилища2048 ГБ
Максимальный объем данных при использовании локального хранилища1400 ГБ
\n

Yandex Managed Service for PostgreSQL

\n

Квоты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Количество кластеров в одном облаке16
Суммарное количество ядер процессора для всех хостов баз данных в одном облаке64
Суммарный объем виртуальной памяти для всех хостов баз данных в одном облаке512 ГБ
Суммарный объем хранилищ для всех кластеров в одном облаке4096 ГБ
\n

Лимиты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Минимальный класс хостаb1.nano (5% × 2 vCPU Intel Broadwell, 2 ГБ RAM)
Максимальный класс хостаm2.8xlarge (64 vCPU Intel Cascade Lake, 512 ГБ RAM)
Максимальное количество хостов в одном кластере7
Максимальный объем данных при использовании сетевого хранилища2048 ГБ
Максимальный объем данных при использовании локального хранилища1400 ГБ
\n

Yandex Managed Service for Redis

\n

Квоты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Количество кластеров в одном облаке16
Суммарное количество ядер процессора для всех хостов баз данных в одном облаке64
Суммарный объем виртуальной памяти для всех хостов в одном облаке512 ГБ
Суммарный объем дисков для всех кластеров в одном облаке4096 ГБ
\n

Лимиты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Минимальный класс хостаb1.nano (burstable с 2 ГБ RAM)
Максимальный класс хостаhm1.9xlarge (high-memory с 256 ГБ RAM)
Максимальное количество хостов в одном кластере7
Минимальный размер диска для кластераВ 2 раза больше выбранного объема RAM
Максимальный размер диска для кластераВ 8 раз больше выбранного объема RAM
\n

Yandex Message Queue

\n

Квоты

\n
Сообщения
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Количество вызовов SendMessage и SendMessageBatch на одну очередь300 вызовов/с для стандартной очереди

30 вызовов/с для очереди FIFO
Количество вызовов ReceiveMessage на одну очередь300 вызовов/с для стандартной очереди

30 вызовов/с для очереди FIFO
Количество вызовов DeleteMessage и DeleteMessageBatch на одну очередь300 вызовов/с для стандартной очереди

30 вызовов/с для очереди FIFO
Количество вызовов ChangeMessageVisibility и ChangeMessageVisibilityBatch на одну очередь300 вызовов/с для стандартной очереди

30 вызовов/с для очереди FIFO
Количество вызовов CreateQueue на одно облако2 вызова/с
Количество вызовов DeleteQueue на одно облако5 вызовов/с
Количество вызовов других запросов на одно облако100 вызовов/с
Количество очередей в одном облаке10
\n

Лимиты

\n
Очереди
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Минимальное время задержки доставки сообщения в очередь (параметр DelaySeconds)0 секунд
Максимальное время задержки доставки сообщения в очередь (параметр DelaySeconds)900 секунд (15 минут)
Количество сообщений в обработке на одну стандартную очередь120000
Количество сообщений в обработке на одну очередь FIFO20000
Имя очередиНе более 80 символов: цифр, маленьких и заглавных латинских букв, дефисов и подчеркиваний. Имя очереди FIFO должно заканчиваться суффиксом .fifo.
\n
Сообщения
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Идентификатор сообщения в группеНе более 80 символов: цифр, маленьких и заглавных латинских букв, дефисов и подчеркиваний.
Максимальное количество атрибутов сообщения10
Максимальное количество сообщений в группе10
Содержимое сообщенийXML, JSON и неформатированный текст. Поддерживаются следующие символы Unicode:
  • #x9
  • #xA
  • #xD
  • от #x20 до #xD7FF
  • от #xE000 до #xFFFD
  • от #x10000 до #x10FFFF
Максимальный срок хранения сообщений в очереди1209600 секунд (14 дней)
Минимальный срок хранения сообщений в очереди60 секунд (1 минута)
Максимальное время задержки доставки сообщения в очередь (параметр DelaySeconds)900 секунд (15 минут)
Минимальное время задержки доставки сообщения в очередь (параметр DelaySeconds)0 секунд
Максимальный размер сообщения262144 байт (256 КБ)
Минимальный размер сообщения1 байт
Максимальный таймаут видимости сообщения12 часов
Минимальный таймаут видимости сообщения0 секунд
Максимальное время ожидания клиентом сообщения в пустой очереди. (параметр WaitTimeSeconds)20 секунд
\n

Yandex SpeechKit

\n

Квоты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Распознавание коротких аудио
Запросов в секунду20
Потоковый режим распознавания коротких аудио
Запросов в секунду40
Распознавание длинных аудио
Запросов на распознавание в час500
Запросов на проверку статуса операции в час2500
Тарифицированных часов аудио в день10000
Cинтез речи
Запросов в секунду40
\n

Лимиты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Распознавание коротких аудио
Максимальный размер файла1 МБ
Максимальная длительность аудио30 секунд
Максимальное количество аудиоканалов1
Потоковый режим распознавания коротких аудио
Максимальная длительность переданного аудио за всю сессию5 минут
Максимальный размер переданных аудиоданных10 МБ
Максимальное количество аудиоканалов1
Распознавание длинных аудио
Максимальный размер файла1 ГБ
Максимальная длительность аудио4 часа
Срок хранения результатов распознавания на сервере3 суток
Синтез речи
Максимальный размер запроса5000 символов
\n

Yandex Translate

\n

Квоты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Вызовов одного метода API в секунду20
Символов, отправленных на перевод, в час1 млн
Символов, отправленных на определение языка, в час1 млн
\n

Лимиты

\n

Лимиты в сервисе отсутствуют. Ограничения на значения полей в теле запроса см. в справочнике API.

\n

Yandex Vision

\n

Квоты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Количество запросов в секунду10
Распознаваний текста в секунду5
Обнаружений лиц в секунду5
Классификаций изображений в секунду5
\n

Лимиты

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Вид ограниченияЗначение
Максимальный размер файла1 МБ
Максимальный размер изображения20 мегапикселей (длина × ширина)
Максимальное количество страниц в PDF-файле8
\n", - "title": "Квоты и лимиты", - "headings": [ - { - "title": "Зачем нужны квоты", - "href": "#quotas", - "level": 2 - }, - { - "title": "Квоты и лимиты по умолчанию для сервисов Облака", - "href": "#kvoty-i-limity-po-umolchaniyu-dlya-servisov-oblaka", - "level": 2, - "items": [ - { - "title": "Yandex Compute Cloud", - "href": "#compute", - "level": 3, - "items": [] - }, - { - "title": "Yandex Object Storage", - "href": "#storage", - "level": 3, - "items": [] - }, - { - "title": "Yandex Virtual Private Cloud", - "href": "#vpc", - "level": 3, - "items": [] - }, - { - "title": "Yandex Resource Manager", - "href": "#resource-manager", - "level": 3 - }, - { - "title": "Yandex Load Balancer", - "href": "#load-balancer", - "level": 3, - "items": [] - }, - { - "title": "Yandex Managed Service for ClickHouse", - "href": "#mch", - "level": 3, - "items": [] - }, - { - "title": "Yandex Managed Service for MongoDB", - "href": "#mmg", - "level": 3, - "items": [] - }, - { - "title": "Yandex Managed Service for MySQL", - "href": "#mmy", - "level": 3, - "items": [] - }, - { - "title": "Yandex Managed Service for PostgreSQL", - "href": "#mpg", - "level": 3, - "items": [] - }, - { - "title": "Yandex Managed Service for Redis", - "href": "#mrd", - "level": 3, - "items": [] - }, - { - "title": "Yandex Message Queue", - "href": "#mq", - "level": 3, - "items": [] - }, - { - "title": "Yandex SpeechKit", - "href": "#speechkit", - "level": 3, - "items": [] - }, - { - "title": "Yandex Translate", - "href": "#translate", - "level": 3, - "items": [] - }, - { - "title": "Yandex Vision", - "href": "#vision", - "level": 3, - "items": [] - } - ] - } - ], - "meta": { - "author": { - "avatar": "https://avatars.githubusercontent.com/u/2485935?v=4", - "email": "", - "login": "robot-dataui-vcs@yandex-team.ru", - "name": "DataUI VCS Robot", - "url": "http://yandex.ru/" - }, - "contributors": [ - { - "avatar": "https://avatars.githubusercontent.com/u/2485945?v=6", - "email": "robot-dataui-vcs@yandex-team.ru", - "login": "", - "name": "DataUI VCS Robot", - "url": "" - } - ] - }, - "toc": { - "title": "Обзор платформы", - "href": "/docs/overview/", - "items": [ - { - "name": "Сервисы Яндекс.Облака", - "href": "/docs/overview/concepts/services", - "id": "e619422ec28a87ea25c41d4ad7e45563" - }, - { - "name": "Сопоставление с другими платформами", - "expanded": true, - "items": [ - { - "name": "Обзор", - "href": "/docs/overview/platform-comparison/", - "id": "6cb52f285fd5d6ec04cbd96854dffab6" - }, - { - "name": "Сопоставление с Amazon Web Services", - "href": "/docs/overview/platform-comparison/aws", - "id": "1d84cf0e31f3f0968bdc6e8c2e11ba2f" - }, - { - "name": "Сопоставление с Google Cloud Platform", - "href": "/docs/overview/platform-comparison/gcp", - "id": "931f7302b4162c44fbe9bb07cea05b1e" - }, - { - "name": "Сопоставление с Microsoft Azure", - "href": "/docs/overview/platform-comparison/azure", - "id": "c14b119f6944d57afd8d4b3e61c37e21" - } - ], - "id": "fa5cef5c0b0cf236fdaa1562cbc48eb8" - }, - { - "name": "Зоны доступности", - "href": "/docs/overview/concepts/geo-scope", - "id": "054328058d7c7b60ff85e3c95be1a264" - }, - { - "name": "Начало работы", - "href": "/docs/overview/quickstart", - "id": "c984893c04c28bc3fe91f5ff87b79c29" - }, - { - "name": "Стадии готовности сервисов", - "href": "/docs/overview/concepts/launch-stages", - "id": "c53a86addb3cf17f0847acde5282ad84" - }, - { - "name": "Квоты и лимиты", - "href": "/docs/overview/concepts/quotas-limits", - "id": "56baa37ef41a57f75c4fce09722ab06c" - }, - { - "name": "API", - "href": "/docs/overview/api", - "id": "136bdc27253376a450eee99e4539c186" - }, - { - "name": "Безопасность и соответствие стандартам", - "items": [ - { - "name": "Рекомендации по безопасности", - "href": "/docs/overview/security-bulletins/", - "id": "c87ac8d995548646dec5661b1b28e45c" - }, - { - "name": "Правила проведения внешних сканирований безопасности", - "href": "/docs/overview/compliance/pentest", - "id": "544f4d243ace1fc32591d87d2460742d" - }, - { - "name": "Безопасность платформы Яндекс.Облако", - "items": [ - { - "name": "Обзор", - "href": "/docs/overview/security/", - "id": "022cc6311c43fdd3c2848a238c92de1e" - }, - { - "name": "Ключевые принципы безопасности", - "href": "/docs/overview/security/principles", - "id": "fba46ca0333fec72f16d0e29fb78e239" - }, - { - "name": "Разделение ответственности за обеспечение безопасности", - "href": "/docs/overview/security/respons", - "id": "177e184118e1e45bc890a53bf76971c7" - }, - { - "name": "Следование лучшим практикам и стандартам", - "href": "/docs/overview/security/standarts", - "id": "778f32125645fc81b0f680d2d9d09d23" - }, - { - "name": "Соответствие требованиям", - "href": "/docs/overview/security/conform", - "id": "4d7329262a479edc4b28a7607a31c2b4" - }, - { - "name": "Технические меры защиты на стороне провайдера", - "href": "/docs/overview/security/tech-measures", - "id": "98cfa650bb690aa5b9a9aa66f3e7e52b" - }, - { - "name": "Средства защиты, доступные пользователям облачных сервисов", - "href": "/docs/overview/security/user-side", - "id": "aaffdd260266d59cd003e6fcab17a1ec" - }, - { - "name": "Полезные ресурсы", - "href": "/docs/overview/security/resources", - "id": "72071e3e599234cb4df3fe56ac378dc1" - } - ], - "id": "f8cca556a8df3df325a1dce2ffec52dc" - } - ], - "id": "7f196892c00a04c9d36a10bc2275ccb8" - }, - { - "name": "Удаление данных пользователей", - "href": "/docs/overview/concepts/data-deletion", - "id": "66fe49c2a324ffd44541fe6a33e14ed0" - }, - { - "name": "SLA", - "href": "/docs/overview/sla", - "id": "1636c6203504367fc47f73077e95ebad" - }, - { - "name": "Вопросы и ответы", - "href": "/docs/overview/qa", - "id": "ff7aacc415c714c83d0ea5715eabb016" - } - ] - }, - "breadcrumbs": [ - { - "name": "Документация" - }, - { - "name": "Квоты и лимиты" - } - ], - "filePath": "/cache/c12878f1-cc2e-445f-8f96-7374fe76b074/ru/overview/concepts/quotas-limits.md", - "githubUrl": "https://github.com/yandex-cloud/docs/tree/master/ru/overview/concepts/quotas-limits.md", - "vcsType": "github", - "vcsUrl": "https://github.com/yandex-cloud/docs/tree/master/ru/overview/concepts/quotas-limits.md" -} diff --git a/src/demo/Components/DocPage/single-page-en.json b/src/demo/Components/DocPage/single-page-en.json deleted file mode 100644 index d24f3073..00000000 --- a/src/demo/Components/DocPage/single-page-en.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "html": "

Get started

\n

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

\n
\n

Second page

\n

Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?

\n", - "title": "", - "headings": [ - {"title": "Get started", "href": "#_en_index_get-started", "level": 2}, - {"title": "Second page", "href": "#_en_second-page_second-page", "level": 2} - ], - "meta": {}, - "toc": { - "title": "Test langs", - "href": "/test-lang/", - "items": [ - { - "name": "Get started", - "href": "/test-lang/#_en_index", - "id": "5b421eb9a84e5f57b317fa003da0b25c" - }, - { - "name": "Second page", - "href": "/test-lang/#_en_second-page", - "id": "291ab0582d4b7c0831afdf82a80a8c92" - } - ], - "base": "en", - "singlePage": true - }, - "breadcrumbs": [], - "vcsUrl": "https://github.com/yandex-cloud/docs/tree/test-lang/1/en/_single_page/index.md", - "vcsType": "github" -} diff --git a/src/demo/Components/DocPage/single-page-ru.json b/src/demo/Components/DocPage/single-page-ru.json deleted file mode 100644 index 40206f88..00000000 --- a/src/demo/Components/DocPage/single-page-ru.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "html": "

Ознакомление

\n

Lorem Ipsum - это текст-"рыба", часто используемый в печати и вэб-дизайне. Lorem Ipsum является стандартной "рыбой" для текстов на латинице с начала XVI века. В то время некий безымянный печатник создал большую коллекцию размеров и форм шрифтов, используя Lorem Ipsum для распечатки образцов. Lorem Ipsum не только успешно пережил без заметных изменений пять веков, но и перешагнул в электронный дизайн. Его популяризации в новое время послужили публикация листов Letraset с образцами Lorem Ipsum в 60-х годах и, в более недавнее время, программы электронной вёрстки типа Aldus PageMaker, в шаблонах которых используется Lorem Ipsum.

\n
\n

Вторая страница

\n

Давно выяснено, что при оценке дизайна и композиции читаемый текст мешает сосредоточиться. Lorem Ipsum используют потому, что тот обеспечивает более или менее стандартное заполнение шаблона, а также реальное распределение букв и пробелов в абзацах, которое не получается при простой дубликации "Здесь ваш текст.. Здесь ваш текст.. Здесь ваш текст.." Многие программы электронной вёрстки и редакторы HTML используют Lorem Ipsum в качестве текста по умолчанию, так что поиск по ключевым словам "lorem ipsum" сразу показывает, как много веб-страниц всё ещё дожидаются своего настоящего рождения. За прошедшие годы текст Lorem Ipsum получил много версий. Некоторые версии появились по ошибке, некоторые - намеренно (например, юмористические варианты).

\n", - "title": "", - "headings": [ - {"title": "Ознакомление", "href": "#_ru_index_oznakomlenie", "level": 2}, - {"title": "Вторая страница", "href": "#_ru_second-page_vtoraya-stranica", "level": 2} - ], - "meta": {}, - "toc": { - "title": "Тест языков", - "href": "/test-lang/", - "items": [ - { - "name": "Ознакомление", - "href": "/test-lang/#_ru_index", - "id": "7c9fe5f5f46c91e3c70bc359bef65b22" - }, - { - "name": "Вторая страница", - "href": "/test-lang/#_ru_second-page", - "id": "dc946863157ca4b7430495cf64689a64" - } - ], - "base": "ru", - "singlePage": true - }, - "breadcrumbs": [], - "vcsUrl": "https://github.com/yandex-cloud/docs/tree/test-lang/1/ru/_single_page/index.md", - "vcsType": "github" -} diff --git a/src/demo/Components/ErrorPage/index.tsx b/src/demo/Components/ErrorPage/index.tsx deleted file mode 100644 index e4c3353d..00000000 --- a/src/demo/Components/ErrorPage/index.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; - -import {ErrorPage} from '../../../index'; -import {getIsMobile} from '../../controls/settings'; -import getLangControl from '../../controls/lang'; -import {radios, text} from '@storybook/addon-knobs'; - -enum ErrorOptions { - ACCESS_DENIED = '403', - NOT_FOUND = '404', - SERVER_ERROR = '500', -} - -const ErrorPageDemo = () => { - const langValue = getLangControl(); - const isMobile = getIsMobile(); - const errorCode = getErrorCode(); - - return ( -
- -
- ); -}; - -function getErrorCode() { - return radios('Errors', ErrorOptions, ErrorOptions.SERVER_ERROR); -} - -export default ErrorPageDemo; diff --git a/src/demo/Components/Header/Header.scss b/src/demo/Components/Header/Header.scss deleted file mode 100644 index 6fdf87dc..00000000 --- a/src/demo/Components/Header/Header.scss +++ /dev/null @@ -1,5 +0,0 @@ -.Header { - &__control-input { - max-width: 170px; - } -} diff --git a/src/demo/Components/Header/Header.tsx b/src/demo/Components/Header/Header.tsx deleted file mode 100644 index f528c7fe..00000000 --- a/src/demo/Components/Header/Header.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import React from 'react'; -import cn from 'bem-cn-lite'; -import {TextInput} from '@gravity-ui/uikit'; -import {ControlSizes, LangControl, FullScreenControl, DividerControl} from '../../../index'; -import {Lang} from '../../../models'; -import './Header.scss'; - -const headBlock = cn('Header'); -const layoutBlock = cn('Layout'); - -export interface HeaderProps { - lang: Lang; - fullScreen: boolean; - searchText?: string; - onChangeLang?: (lang: Lang) => void; - onChangeFullScreen?: (value: boolean) => void; - onChangeSearch?: (value: string) => void; -} - -const Header: React.FC = ({ - lang, - fullScreen, - searchText, - onChangeFullScreen, - onChangeLang, - onChangeSearch, -}) => { - return ( -
-
- - - - - {onChangeFullScreen ? ( - - ) : null} -
-
- ); -}; - -export default Header; diff --git a/src/demo/Components/Paginator/index.tsx b/src/demo/Components/Paginator/index.tsx deleted file mode 100644 index 77d89bda..00000000 --- a/src/demo/Components/Paginator/index.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React, {useState} from 'react'; - -import {Paginator} from '../../../index'; - -const PaginatorDemo = () => { - const [page, setPage] = useState(1); - - return ( - - ); -}; - -export default PaginatorDemo; diff --git a/src/demo/Components/SearchItem/index.tsx b/src/demo/Components/SearchItem/index.tsx deleted file mode 100644 index a3c8a172..00000000 --- a/src/demo/Components/SearchItem/index.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; - -import {SearchItem} from '../../../index'; -import data from './page.json'; - -const SearchItemDemo = () => { - return ; -}; - -export default SearchItemDemo; diff --git a/src/demo/Components/SearchItem/page.json b/src/demo/Components/SearchItem/page.json deleted file mode 100644 index 3e79f05f..00000000 --- a/src/demo/Components/SearchItem/page.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "title": "Add a chart", - "description": "Follow these instruction to add a chart to your dashboard.", - "url": "/en/data-visualization/step-by-step/charts/add-chart" -} diff --git a/src/demo/Components/SearchPage/data.ts b/src/demo/Components/SearchPage/data.ts deleted file mode 100644 index a5d2cf75..00000000 --- a/src/demo/Components/SearchPage/data.ts +++ /dev/null @@ -1,26 +0,0 @@ -export default [ - { - title: 'Lorem ipsum dolor sit amet', - url: '/en/data-visualization/step-by-step/charts/add-chart', - description: - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean nec nisi eget tellus pharetra pharetra sit amet in metus. Fusce eu consequat nisl. Sed dictum porta scelerisque. Fusce auctor enim ligula, at rhoncus ligula placerat id. Aliquam venenatis cursus ante, quis feugiat tortor mattis et. Donec sed justo eu est egestas malesuada.', - }, - { - title: 'Vivamus scelerisque dictum blandit', - url: '/en/data-visualization/step-by-step/charts/add-chart', - description: - 'Vivamus scelerisque dictum blandit. Curabitur metus odio, lobortis eu est id, iaculis accumsan massa. Ut et euismod nisl. Donec mollis odio at elementum sagittis. Quisque dapibus eros purus, eu vulputate nisl pellentesque et. Ut vehicula mattis euismod. Vestibulum ornare nulla vel nisi consectetur accumsan. Quisque id felis tempus, porttitor ex et, pellentesque ligula.', - }, - { - title: 'Integer tincidunt rhoncus purus', - url: '/en/data-visualization/step-by-step/charts/add-chart', - description: - 'Integer tincidunt rhoncus purus, nec laoreet arcu. Ut fermentum nulla sit amet arcu tristique vulputate. Vestibulum et tempor arcu. Cras laoreet ipsum ac mi rutrum, in sagittis metus pharetra. In mi nibh, lobortis sed tempor quis, fermentu', - }, - { - title: 'Cras aliquam et eros ut', - url: '/en/data-visualization/step-by-step/charts/add-chart', - description: - 'Cras aliquam et eros ut lobortis. Quisque malesuada, nunc vitae placerat varius, ante nulla pharetra libero, at fringilla magna sem non velit. Curabitur finibus mauris vitae est pellentesque, vitae molestie tortor condimentum. ', - }, -]; diff --git a/src/demo/Components/SearchPage/index.tsx b/src/demo/Components/SearchPage/index.tsx deleted file mode 100644 index e7ace993..00000000 --- a/src/demo/Components/SearchPage/index.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import React, {useState} from 'react'; - -import {ISearchItem, SearchPage} from '../../../index'; -import {getIsMobile} from '../../controls/settings'; -import getLangControl from '../../controls/lang'; -import mockData from './data'; - -const SearchPageDemo = () => { - const langValue = getLangControl(); - const isMobile = getIsMobile(); - const [page, setPage] = useState(1); - const [items, setItems] = useState(getItems(page, mockData)); - - function getItems(newPage: number, data: ISearchItem[]): ISearchItem[] { - return newPage === 1 ? data.slice(0, 2) : data.slice(2); - } - - return ( -
- { - setPage(newPage); - setItems(getItems(newPage, mockData)); - }} - onSubmit={() => setItems(getItems(page, mockData))} - itemOnClick={(item) => console.log('Click on search result', item)} - irrelevantOnClick={(item) => console.log('Click on dislike button', item)} - relevantOnClick={(item) => console.log('Click on like button', item)} - itemsPerPage={2} - totalItems={mockData.length} - lang={langValue} - /> -
- ); -}; - -export default SearchPageDemo; diff --git a/src/demo/Components/index.js b/src/demo/Components/index.js deleted file mode 100644 index 342f00a0..00000000 --- a/src/demo/Components/index.js +++ /dev/null @@ -1,15 +0,0 @@ -import DocPage from './DocPage'; -import DocLeadingPage from './DocLeadingPage'; -import ErrorPage from './ErrorPage'; -import Paginator from './Paginator'; -import SearchItem from './SearchItem'; -import SearchPage from './SearchPage'; - -export default class Components { - static DocPage = DocPage; - static DocLeadingPage = DocLeadingPage; - static ErrorPage = ErrorPage; - static Paginator = Paginator; - static SearchItem = SearchItem; - static SearchPage = SearchPage; -} diff --git a/src/demo/controls/lang.tsx b/src/demo/controls/lang.tsx deleted file mode 100644 index bcda89f0..00000000 --- a/src/demo/controls/lang.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import {radios} from '@storybook/addon-knobs'; -import {Lang} from '../../index'; - -export default function getLangControl() { - const label = 'Language'; - const options = { - ru: Lang.Ru, - en: Lang.En, - }; - const defaultValue = Lang.Ru; - - return radios(label, options, defaultValue); -} diff --git a/src/demo/controls/settings.tsx b/src/demo/controls/settings.tsx deleted file mode 100644 index 1361508b..00000000 --- a/src/demo/controls/settings.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import {radios} from '@storybook/addon-knobs'; - -export function getIsMobile() { - const label = 'Mobile version'; - const options = { - enabled: 'true', - disabled: 'false', - }; - const defaultValue = 'false'; - - return radios(label, options, defaultValue); -} diff --git a/src/demo/controls/vcs.tsx b/src/demo/controls/vcs.tsx deleted file mode 100644 index 7e1dbf59..00000000 --- a/src/demo/controls/vcs.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import {radios} from '@storybook/addon-knobs'; -import {Vcs} from '../../index'; - -export default function getVcsControl() { - const label = 'VCS'; - const options = { - github: Vcs.Github, - arcanum: Vcs.Arcanum, - }; - const defaultValue = Vcs.Github; - - return radios(label, options, defaultValue); -} diff --git a/src/demo/decorators/bookmark.ts b/src/demo/decorators/bookmark.ts deleted file mode 100644 index 25c10cee..00000000 --- a/src/demo/decorators/bookmark.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {radios} from '@storybook/addon-knobs'; - -export function getHasBookmark() { - const label = 'Bookmark page'; - const options = { - enabled: 'true', - disabled: 'false', - }; - const defaultValue = 'false'; - - return radios(label, options, defaultValue); -} diff --git a/src/demo/decorators/subscribe.tsx b/src/demo/decorators/subscribe.tsx deleted file mode 100644 index d8e7b260..00000000 --- a/src/demo/decorators/subscribe.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import {radios} from '@storybook/addon-knobs'; - -export function getHasSubscribe() { - const label = 'Subscribe button'; - const options = { - enabled: 'true', - disabled: 'false', - }; - const defaultValue = 'true'; - - return radios(label, options, defaultValue); -} diff --git a/src/demo/decorators/withTheme.tsx b/src/demo/decorators/withTheme.tsx deleted file mode 100644 index 5f1b16bb..00000000 --- a/src/demo/decorators/withTheme.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import {StoryFn} from '@storybook/addons'; -import {radios} from '@storybook/addon-knobs'; -import {Theme} from '../../models'; - -export function getThemeSelector() { - const label = 'Theme'; - const options = { - Light: Theme.Light, - Dark: Theme.Dark, - }; - const defaultValue = Theme.Dark; - - return radios(label, options, defaultValue); -} - -export default function withTheme(story: StoryFn) { - const theme = getThemeSelector(); - document.body.classList.add('yc-root'); - document.body.classList.toggle('yc-root_theme_light', theme === 'light'); - document.body.classList.toggle('yc-root_theme_dark', theme === 'dark'); - - return story(); -} diff --git a/src/demo/reset-storybook.scss b/src/demo/reset-storybook.scss deleted file mode 100644 index bdf68a77..00000000 --- a/src/demo/reset-storybook.scss +++ /dev/null @@ -1,49 +0,0 @@ -@import '../../styles/mixins'; - -.yc-root { - & code[class*='language-'], - pre[class*='language-'] { - background: initial; - padding: initial; - margin: 0 0 15px; - text-shadow: none; - } -} - -body.yc-root { - --dc-header-height: 64px; -} - -.yc-root .Header { - display: flex; - justify-content: flex-end; - align-items: center; - height: 64px; - padding: 0 24px; - background-color: var(--yc-color-base-background); - box-shadow: inset 0 -1px 0 var(--yc-color-line-generic); - - &__control { - color: var(--yc-color-text-primary); - } - - &__control-input { - height: 28px; - border-radius: 4px; - border: 1px solid var(--yc-color-line-generic); - margin-right: 6px; - display: flex; - align-items: center; - justify-content: center; - } - - &__divider { - margin: 0 4px; - } -} - -.Layout__header { - position: sticky; - top: 0; - z-index: 101; -} diff --git a/src/stories/Components.stories.js b/src/stories/Components.stories.js deleted file mode 100644 index 8e280e16..00000000 --- a/src/stories/Components.stories.js +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; -import {storiesOf} from '@storybook/react'; -import Components from '../demo/Components'; - -// eslint-disable-next-line no-undef -const stories = storiesOf('Components', module); - -stories.add('DocPage', () => { - return ; -}); - -stories.add('DocLeadingPage', () => { - return ; -}); - -stories.add('ErrorPage', () => { - return ; -}); - -stories.add('SearchPage', () => { - return ; -}); - -stories.add('Paginator', () => { - return ; -}); - -stories.add('SearchItem', () => { - return ; -}); - -stories.add('Paginator', () => { - return ; -}); diff --git a/svgo.config.js b/svgo.config.js deleted file mode 100644 index 2185d08a..00000000 --- a/svgo.config.js +++ /dev/null @@ -1,21 +0,0 @@ -/* eslint-env node */ - -module.exports = { - multipass: true, - js2svg: { - pretty: true, - }, - plugins: [ - { - name: 'preset-default', - params: { - overrides: { - removeViewBox: false, - cleanupIDs: { - minify: false, - }, - }, - }, - }, - ], -}; From 43534189e6984f72924ebb4ac14ae4b115a85df9 Mon Sep 17 00:00:00 2001 From: 3y3 <3y3@ya.ru> Date: Tue, 5 Sep 2023 20:01:03 +0300 Subject: [PATCH 02/52] chore: Update outdated infrastructure --- .eslintignore | 11 +++++++++-- .eslintrc | 2 +- .nvmrc | 2 +- .prettierrc | 1 + .prettierrc.js | 1 - .stylelinignore | 11 +++++++++++ .stylelintrc | 2 +- package.json | 49 ++++++++++++++++++++++++------------------------- src/.eslintrc | 2 +- tsconfig.json | 5 +++-- 10 files changed, 52 insertions(+), 34 deletions(-) create mode 100644 .prettierrc delete mode 100644 .prettierrc.js create mode 100644 .stylelinignore diff --git a/.eslintignore b/.eslintignore index 494039a2..85b33cc0 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,11 @@ +# System +.idea +.vscode +.history +.storybook + +# Libs node_modules -/build -/storybook-static +# Build +build diff --git a/.eslintrc b/.eslintrc index 582979e4..b4c98570 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,4 +1,4 @@ { - "extends": ["@yandex-cloud/eslint-config", "@yandex-cloud/eslint-config/prettier"], + "extends": ["@gravity-ui/eslint-config"], "root": true } diff --git a/.nvmrc b/.nvmrc index da2d3988..3c032078 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -14 \ No newline at end of file +18 diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..72931973 --- /dev/null +++ b/.prettierrc @@ -0,0 +1 @@ +"@gravity-ui/prettier-config" diff --git a/.prettierrc.js b/.prettierrc.js deleted file mode 100644 index 8a43dc10..00000000 --- a/.prettierrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('@yandex-cloud/prettier-config'); diff --git a/.stylelinignore b/.stylelinignore new file mode 100644 index 00000000..85b33cc0 --- /dev/null +++ b/.stylelinignore @@ -0,0 +1,11 @@ +# System +.idea +.vscode +.history +.storybook + +# Libs +node_modules + +# Build +build diff --git a/.stylelintrc b/.stylelintrc index 3384c742..5135c9ad 100644 --- a/.stylelintrc +++ b/.stylelintrc @@ -1,3 +1,3 @@ { - "extends": ["@yandex-cloud/stylelint-config", "@yandex-cloud/stylelint-config/prettier"] + "extends": ["@gravity-ui/stylelint-config", "@gravity-ui/stylelint-config/prettier"] } diff --git a/package.json b/package.json index 8f1ed2e5..264f6bb0 100644 --- a/package.json +++ b/package.json @@ -25,14 +25,14 @@ "scripts": { "deps:install": "npm ci", "deps:truncate": "npm prune --production", - "lint": "run-p lint:js lint:styles lint:prettier", - "lint:fix": "run-s lint:js:fix lint:styles:fix lint:prettier:fix", - "lint:js": "eslint 'src/**/*.{js,jsx,ts,tsx}'", - "lint:js:fix": "eslint 'src/**/*.{js,jsx,ts,tsx}' --quiet --fix", - "lint:styles": "stylelint src/**/*.scss", - "lint:styles:fix": "stylelint **/*.scss --fix", - "lint:prettier": "prettier --check 'src/**/*.{js,jsx,ts,tsx,css,scss,json,yaml,yml,md}'", - "lint:prettier:fix": "prettier --write 'src/**/*.{js,jsx,ts,tsx,css,scss,json,yaml,yml,md}'", + "lint": "run-p _lint:js _lint:styles _lint:prettier", + "lint:fix": "run-s _lint:js:fix _lint:styles:fix _lint:prettier:fix", + "_lint:js": "eslint 'src/**/*.{js,jsx,ts,tsx}'", + "_lint:js:fix": "npm run _lint:js -- --quiet --fix", + "_lint:prettier": "prettier --check 'src/**/*.{js,jsx,ts,tsx}'", + "_lint:prettier:fix": "prettier --write 'src/**/*.{js,jsx,ts,tsx}'", + "_lint:styles": "stylelint src/**/*.scss", + "_lint:styles:fix": "npm run _lint:styles -- --fix", "typecheck": "tsc --noEmit", "test": "exit 0", "build:clean": "rimraf build", @@ -63,38 +63,37 @@ }, "devDependencies": { "@doc-tools/transform": "^3.10.2", + "@gravity-ui/eslint-config": "^2.2.0", + "@gravity-ui/prettier-config": "^1.0.1", + "@gravity-ui/stylelint-config": "^3.0.0", + "@gravity-ui/tsconfig": "^1.0.0", "@types/langs": "^2.0.1", "@types/lodash": "4.14.179", "@types/react": "16.14.23", "@types/react-dom": "16.9.14", - "@yandex-cloud/eslint-config": "1.0.0", - "@yandex-cloud/prettier-config": "1.0.0", - "@yandex-cloud/stylelint-config": "1.1.0", - "@yandex-cloud/tsconfig": "1.0.0", - "autoprefixer": "9.8.8", "copyfiles": "2.4.1", - "eslint": "8.10.0", - "husky": "7.0.4", - "lint-staged": "12.3.5", "node-sass": "4.14.1", "npm-run-all": "4.1.5", - "postcss": "8.4.8", - "prettier": "2.5.1", - "stylelint": "14.5.3", + "autoprefixer": "^10.4.15", + "eslint": "^8.48.0", + "husky": "^8.0.3", + "lint-staged": "^14.0.1", + "postcss": "^8.4.28", + "prettier": "^2.8.8", + "stylelint": "^15.10.3", "svgo": "2.8.0", - "typescript": "4.6.2", - "webpack-manifest-plugin": "2.2.0" + "typescript": "^5.2.2" }, "lint-staged": { - "*.{js,jsx,ts,tsx}": [ - "eslint --max-warnings=0 --fix", + "src/**/*.{js,jsx,ts,tsx}": [ + "eslint --max-warnings=0 --fix -c eslint.publish.json", "prettier --write" ], - "*.{css,scss}": [ + "src/**/*.{css,scss}": [ "stylelint --fix", "prettier --write" ], - "*.{json,yaml,yml,md}": [ + "src/**/*.{json,yaml,yml,md}": [ "prettier --write" ], "*.svg": [ diff --git a/src/.eslintrc b/src/.eslintrc index 9e3fa058..9ecc6200 100644 --- a/src/.eslintrc +++ b/src/.eslintrc @@ -1,3 +1,3 @@ { - "extends": "@yandex-cloud/eslint-config/client" + "extends": "@gravity-ui/eslint-config/client" } diff --git a/tsconfig.json b/tsconfig.json index a837a784..5f0c9940 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,13 +1,14 @@ { - "extends": "@yandex-cloud/tsconfig/tsconfig", + "extends": "@gravity-ui/tsconfig/tsconfig", "compilerOptions": { - "module": "esNext", + "module": "ESNext", "allowJs": false, "importHelpers": true, "resolveJsonModule": true, "declaration": true, "jsx": "react", "outDir": "build", + "target": "ESNext", "baseUrl": "." }, "include": ["src/**/*.ts", "src/**/*.tsx"] From 4b1c8c9e7df2dd10a169b0f06ca1be2f6c3a4852 Mon Sep 17 00:00:00 2001 From: 3y3 <3y3@ya.ru> Date: Tue, 5 Sep 2023 20:51:46 +0300 Subject: [PATCH 03/52] chore: Basic type and lint fixes --- .../BookmarkButton/BookmarkButton.tsx | 6 +-- src/components/Breadcrumbs/Breadcrumbs.tsx | 1 + src/components/Button/Button.tsx | 0 .../ContributorAvatars/Avatars/Avatar.tsx | 5 ++- .../Avatars/AvatarWithDescription.tsx | 5 ++- .../ContributorAvatars/Avatars/Details.tsx | 10 +++-- .../Avatars/HiddenAvatars.tsx | 6 ++- .../ContributorAvatars/ContributorAvatars.tsx | 12 +++--- src/components/Contributors/Contributors.tsx | 2 +- src/components/DocLayout/DocLayout.tsx | 9 +++-- .../DocLeadingPage/DocLeadingPage.tsx | 6 +-- src/components/DocPage/DocPage.tsx | 38 +++++++++---------- src/components/DocPageTitle/DocPageTitle.tsx | 6 +-- .../ErrorBoundary/ErrorBoundary.tsx | 5 ++- src/components/ErrorPage/ErrorPage.tsx | 13 ++++--- src/components/Link.tsx | 4 +- src/components/Mark/Mark.tsx | 1 + src/components/OutsideClick/OutsideClick.tsx | 4 +- src/components/Scrollspy/Scrollspy.tsx | 5 ++- src/components/SearchBar/SearchBar.tsx | 18 ++++++--- src/components/SearchBar/hooks.ts | 9 +++-- .../SearchBar/withHighlightedSearchWords.tsx | 6 +-- src/components/SearchPage/SearchPage.tsx | 11 +++--- src/components/StageLabel/StageLabel.tsx | 1 + src/components/Subscribe/utils.ts | 3 +- src/components/Toc/Toc.tsx | 11 +++--- src/components/TocItem/TocItem.tsx | 3 +- 27 files changed, 113 insertions(+), 87 deletions(-) delete mode 100644 src/components/Button/Button.tsx diff --git a/src/components/BookmarkButton/BookmarkButton.tsx b/src/components/BookmarkButton/BookmarkButton.tsx index f5428e05..7e2e1389 100644 --- a/src/components/BookmarkButton/BookmarkButton.tsx +++ b/src/components/BookmarkButton/BookmarkButton.tsx @@ -1,7 +1,7 @@ import React from 'react'; -import block from 'bem-cn-lite'; -import {Button, Icon as IconComponent} from '@gravity-ui/uikit'; +import {Button, Icon} from '@gravity-ui/uikit'; +import block from 'bem-cn-lite'; import StarActive from '../../../assets/icons/star-active.svg'; import StarInactive from '../../../assets/icons/star-inactive.svg'; @@ -29,7 +29,7 @@ export const BookmarkButton: React.FC = ({ view="flat-secondary" > - + ); diff --git a/src/components/Breadcrumbs/Breadcrumbs.tsx b/src/components/Breadcrumbs/Breadcrumbs.tsx index 849bec23..c41bc5e7 100644 --- a/src/components/Breadcrumbs/Breadcrumbs.tsx +++ b/src/components/Breadcrumbs/Breadcrumbs.tsx @@ -1,4 +1,5 @@ import React from 'react'; + import block from 'bem-cn-lite'; import {BreadcrumbItem} from '../../models'; diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx deleted file mode 100644 index e69de29b..00000000 diff --git a/src/components/ContributorAvatars/Avatars/Avatar.tsx b/src/components/ContributorAvatars/Avatars/Avatar.tsx index 020576c5..1df6e976 100644 --- a/src/components/ContributorAvatars/Avatars/Avatar.tsx +++ b/src/components/ContributorAvatars/Avatars/Avatar.tsx @@ -1,8 +1,9 @@ -import block from 'bem-cn-lite'; import React, {BaseSyntheticEvent} from 'react'; -import {AvatarData, AvatarSizes, PopupData} from '../models'; +import block from 'bem-cn-lite'; + import {Contributor} from '../../../models'; +import {AvatarData, AvatarSizes, PopupData} from '../models'; import {getUserIdentificator} from '../utils'; import '../ContributorAvatars.scss'; diff --git a/src/components/ContributorAvatars/Avatars/AvatarWithDescription.tsx b/src/components/ContributorAvatars/Avatars/AvatarWithDescription.tsx index 9365c6d2..acb100b2 100644 --- a/src/components/ContributorAvatars/Avatars/AvatarWithDescription.tsx +++ b/src/components/ContributorAvatars/Avatars/AvatarWithDescription.tsx @@ -1,8 +1,9 @@ import React, {Fragment, useRef, useState} from 'react'; -import Avatar from './Avatar'; -import {AvatarData, AvatarSizes, PopupData} from '../models'; import {Contributor} from '../../../models'; +import {AvatarData, AvatarSizes, PopupData} from '../models'; + +import Avatar from './Avatar'; import Details from './Details'; interface AvatarWithDescriptionProps { diff --git a/src/components/ContributorAvatars/Avatars/Details.tsx b/src/components/ContributorAvatars/Avatars/Details.tsx index 15a7ed6c..6a341910 100644 --- a/src/components/ContributorAvatars/Avatars/Details.tsx +++ b/src/components/ContributorAvatars/Avatars/Details.tsx @@ -1,13 +1,15 @@ -import block from 'bem-cn-lite'; import React from 'react'; + import {Popup} from '@gravity-ui/uikit'; +import block from 'bem-cn-lite'; -import Avatar from './Avatar'; +import {PopperPosition} from '../../../hooks'; +import {Contributor} from '../../../models'; import Link from '../../Link'; import {AvatarData, AvatarSizes, PopupData} from '../models'; import {getName} from '../utils'; -import {Contributor} from '../../../models'; -import {PopperPosition} from '../../../hooks'; + +import Avatar from './Avatar'; import '../ContributorAvatars.scss'; diff --git a/src/components/ContributorAvatars/Avatars/HiddenAvatars.tsx b/src/components/ContributorAvatars/Avatars/HiddenAvatars.tsx index 97146a0d..5f00bc87 100644 --- a/src/components/ContributorAvatars/Avatars/HiddenAvatars.tsx +++ b/src/components/ContributorAvatars/Avatars/HiddenAvatars.tsx @@ -1,8 +1,10 @@ -import block from 'bem-cn-lite'; import React, {BaseSyntheticEvent, Fragment, useRef, useState} from 'react'; -import {AvatarSizes, PopupData} from '../models'; +import block from 'bem-cn-lite'; + import {Contributor} from '../../../models'; +import {AvatarSizes, PopupData} from '../models'; + import Details from './Details'; import '../ContributorAvatars.scss'; diff --git a/src/components/ContributorAvatars/ContributorAvatars.tsx b/src/components/ContributorAvatars/ContributorAvatars.tsx index 574b3f66..f4146ddd 100644 --- a/src/components/ContributorAvatars/ContributorAvatars.tsx +++ b/src/components/ContributorAvatars/ContributorAvatars.tsx @@ -1,13 +1,15 @@ -import block from 'bem-cn-lite'; import React, {Fragment, ReactElement} from 'react'; +import block from 'bem-cn-lite'; + import {Contributor} from '../../models'; -import {getName} from './utils'; -import {AvatarData, AvatarSizes} from './models'; -import HiddenAvatars from './Avatars/HiddenAvatars'; -import AvatarWithDescription from './Avatars/AvatarWithDescription'; import Link from '../Link'; + import Avatar from './Avatars/Avatar'; +import AvatarWithDescription from './Avatars/AvatarWithDescription'; +import HiddenAvatars from './Avatars/HiddenAvatars'; +import {AvatarData, AvatarSizes} from './models'; +import {getName} from './utils'; import './ContributorAvatars.scss'; diff --git a/src/components/Contributors/Contributors.tsx b/src/components/Contributors/Contributors.tsx index 33ebc6ee..f472c87c 100644 --- a/src/components/Contributors/Contributors.tsx +++ b/src/components/Contributors/Contributors.tsx @@ -33,7 +33,7 @@ const Contributors: React.FC = (props) => { return (
-
{t('title')}
+
{t('title')}
); diff --git a/src/components/DocLayout/DocLayout.tsx b/src/components/DocLayout/DocLayout.tsx index 8516557e..ad9ee9f7 100644 --- a/src/components/DocLayout/DocLayout.tsx +++ b/src/components/DocLayout/DocLayout.tsx @@ -1,4 +1,5 @@ -import React, {ReactElement} from 'react'; +import React, {PropsWithChildren, ReactElement} from 'react'; + import block from 'bem-cn-lite'; import {TocData, Router, Lang} from '../../models'; @@ -9,9 +10,9 @@ import './DocLayout.scss'; const b = block('dc-doc-layout'); -const Left: React.FC = () => null; -const Center: React.FC = () => null; -const Right: React.FC = () => null; +const Left: React.FC = () => null; +const Center: React.FC = () => null; +const Right: React.FC = () => null; export interface DocLayoutProps { toc: TocData; diff --git a/src/components/DocLeadingPage/DocLeadingPage.tsx b/src/components/DocLeadingPage/DocLeadingPage.tsx index e32604de..eea825f7 100644 --- a/src/components/DocLeadingPage/DocLeadingPage.tsx +++ b/src/components/DocLeadingPage/DocLeadingPage.tsx @@ -1,13 +1,13 @@ import React from 'react'; + import block from 'bem-cn-lite'; import {DocLeadingPageData, DocLeadingLinks, Router, Lang} from '../../models'; +import {DEFAULT_SETTINGS} from '../../constants'; import {DocLayout} from '../DocLayout'; import {DocPageTitle} from '../DocPageTitle'; -import {Text} from '../Text'; import {HTML} from '../HTML'; - -import {DEFAULT_SETTINGS} from '../../constants'; +import {Text} from '../Text'; import './DocLeadingPage.scss'; diff --git a/src/components/DocPage/DocPage.tsx b/src/components/DocPage/DocPage.tsx index c778a1e7..fd6f4e55 100644 --- a/src/components/DocPage/DocPage.tsx +++ b/src/components/DocPage/DocPage.tsx @@ -1,41 +1,41 @@ import React from 'react'; -import ReactDOMServer from 'react-dom/server'; + import block from 'bem-cn-lite'; import '@doc-tools/transform/dist/js/yfm'; +import ReactDOMServer from 'react-dom/server'; +import {DEFAULT_SETTINGS} from '../../constants'; import { - FeedbackSendData, DocPageData, DocSettings, + FeedbackSendData, Lang, Router, - Vcs, + SubscribeData, TextSizes, Theme, - SubscribeData, + Vcs, } from '../../models'; -import {DocLayout} from '../DocLayout'; -import {DocPageTitle} from '../DocPageTitle'; -import {MiniToc} from '../MiniToc'; -import {HTML} from '../HTML'; -import {Breadcrumbs} from '../Breadcrumbs'; -import {TocNavPanel} from '../TocNavPanel'; -import {Controls} from '../Controls'; -import {EditButton} from '../EditButton'; -import {SearchBar, withHighlightedSearchWords} from '../SearchBar'; -import {Feedback, FeedbackView} from '../Feedback'; -import Contributors from '../Contributors/Contributors'; - import { + InnerProps, callSafe, createElementFromHTML, getHeaderTag, - getStateKey, - InnerProps, getRandomKey, + getStateKey, isContributor, } from '../../utils'; -import {DEFAULT_SETTINGS} from '../../constants'; +import {Breadcrumbs} from '../Breadcrumbs'; +import Contributors from '../Contributors/Contributors'; +import {Controls} from '../Controls'; +import {DocLayout} from '../DocLayout'; +import {DocPageTitle} from '../DocPageTitle'; +import {EditButton} from '../EditButton'; +import {Feedback, FeedbackView} from '../Feedback'; +import {HTML} from '../HTML'; +import {MiniToc} from '../MiniToc'; +import {SearchBar, withHighlightedSearchWords} from '../SearchBar'; +import {TocNavPanel} from '../TocNavPanel'; import LinkIcon from '../../../assets/icons/link.svg'; diff --git a/src/components/DocPageTitle/DocPageTitle.tsx b/src/components/DocPageTitle/DocPageTitle.tsx index a615f215..82a6f6d1 100644 --- a/src/components/DocPageTitle/DocPageTitle.tsx +++ b/src/components/DocPageTitle/DocPageTitle.tsx @@ -1,7 +1,7 @@ -import React from 'react'; +import React, {PropsWithChildren} from 'react'; + import block from 'bem-cn-lite'; -import {StageLabel, StageType} from '../StageLabel'; import {BookmarkButton} from '../BookmarkButton'; import './DocPageTitle.scss'; @@ -15,7 +15,7 @@ export interface DocPageTitleProps { onChangeBookmarkPage?: (value: boolean) => void; } -export const DocPageTitle: React.FC = ({ +export const DocPageTitle: React.FC> = ({ children, stage, className, diff --git a/src/components/ErrorBoundary/ErrorBoundary.tsx b/src/components/ErrorBoundary/ErrorBoundary.tsx index 413ee890..9b18705f 100644 --- a/src/components/ErrorBoundary/ErrorBoundary.tsx +++ b/src/components/ErrorBoundary/ErrorBoundary.tsx @@ -1,4 +1,5 @@ -import React from 'react'; +import React, {PropsWithChildren} from 'react'; + import {isEqual} from 'lodash'; import {ErrorPage} from '../ErrorPage'; @@ -8,7 +9,7 @@ interface ErrorBoundaryState { errorInfo?: unknown; } -class ErrorBoundary extends React.Component { +class ErrorBoundary extends React.Component> { state: ErrorBoundaryState = {}; componentDidUpdate(prevProps: ErrorBoundaryState) { diff --git a/src/components/ErrorPage/ErrorPage.tsx b/src/components/ErrorPage/ErrorPage.tsx index b19904d8..980ea549 100644 --- a/src/components/ErrorPage/ErrorPage.tsx +++ b/src/components/ErrorPage/ErrorPage.tsx @@ -1,7 +1,8 @@ import React from 'react'; + +import {Button, Link} from '@gravity-ui/uikit'; import block from 'bem-cn-lite'; import {withTranslation, WithTranslation, WithTranslationProps} from 'react-i18next'; -import {Link, Button} from '@gravity-ui/uikit'; import {Lang} from '../../models'; import {ERROR_CODES} from '../../constants'; @@ -38,7 +39,7 @@ const ErrorPage = ({ const href = homeUrl || '/'; const homeLink = ( - + ); @@ -51,7 +52,7 @@ const ErrorPage = ({ {receiveAccessUrl && ( )} @@ -66,12 +67,12 @@ const ErrorPage = ({ title = t('label_title-500'); description = ( - {t('label_description-1')} + {t('label_description-1')} ); @@ -83,7 +84,7 @@ const ErrorPage = ({ title={`Error image for ${code} code`} className={b('image', {code: code.toString()})} /> -

{t('label_title-code', {code})}

+

{t('label_title-code', {code})}

{title}

{description}

diff --git a/src/components/Link.tsx b/src/components/Link.tsx index e9e1dbd7..4ab3629b 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -1,10 +1,10 @@ -import React, {Fragment} from 'react'; +import React, {Fragment, PropsWithChildren} from 'react'; interface LinkProps { url?: string; } -const Link: React.FC = ({children, url}) => { +const Link: React.FC> = ({children, url}) => { return url ? ( {children} diff --git a/src/components/Mark/Mark.tsx b/src/components/Mark/Mark.tsx index d2a88956..e2e157dc 100644 --- a/src/components/Mark/Mark.tsx +++ b/src/components/Mark/Mark.tsx @@ -1,6 +1,7 @@ import React from 'react'; import block from 'bem-cn-lite'; + import './Mark.scss'; const b = block('dc-mark'); diff --git a/src/components/OutsideClick/OutsideClick.tsx b/src/components/OutsideClick/OutsideClick.tsx index bbdf81ef..446f5e63 100644 --- a/src/components/OutsideClick/OutsideClick.tsx +++ b/src/components/OutsideClick/OutsideClick.tsx @@ -1,11 +1,11 @@ -import React, {createRef} from 'react'; +import React, {PropsWithChildren, createRef} from 'react'; export interface OutsideClickProps { onOutsideClick: () => void; anchor?: HTMLElement | null; } -export default class OutsideClick extends React.Component { +export default class OutsideClick extends React.Component> { ref = createRef(); componentDidMount() { diff --git a/src/components/Scrollspy/Scrollspy.tsx b/src/components/Scrollspy/Scrollspy.tsx index c35b9f7d..abd6a362 100644 --- a/src/components/Scrollspy/Scrollspy.tsx +++ b/src/components/Scrollspy/Scrollspy.tsx @@ -1,9 +1,10 @@ import React, {ReactElement} from 'react'; -import {isEqual, debounce} from 'lodash'; + +import {debounce, isEqual} from 'lodash'; import scrollIntoView from 'scroll-into-view-if-needed'; -import {InnerProps} from '../../utils'; import {Router} from '../../models'; +import {InnerProps} from '../../utils'; interface ScrollspyDefaultProps { currentClassName: string; diff --git a/src/components/SearchBar/SearchBar.tsx b/src/components/SearchBar/SearchBar.tsx index df5a88b4..5fa8881c 100644 --- a/src/components/SearchBar/SearchBar.tsx +++ b/src/components/SearchBar/SearchBar.tsx @@ -1,4 +1,6 @@ import React from 'react'; + +import {Icon} from '@gravity-ui/uikit'; import block from 'bem-cn-lite'; import {withTranslation, WithTranslation, WithTranslationProps} from 'react-i18next'; import {useHotkeys} from 'react-hotkeys-hook'; @@ -57,7 +59,9 @@ const SearchBar: React.FC = (props) => { } + icon={(args) => ( + + )} /> {searchCurrentIndex}/{searchCountResults} @@ -65,22 +69,26 @@ const SearchBar: React.FC = (props) => { } + icon={(args) => ( + + )} /> - {t('search-query-label')}:  + + {t('search-query-label')}:  + {searchQuery}
} + icon={() => } />
); -}; +}); SearchBar.displayName = 'DCSearchBar'; diff --git a/src/components/SearchBar/hooks.ts b/src/components/SearchBar/hooks.ts index 06315d1e..dea79fe2 100644 --- a/src/components/SearchBar/hooks.ts +++ b/src/components/SearchBar/hooks.ts @@ -1,8 +1,9 @@ -import {useEffect, useRef, useState, useCallback} from 'react'; +import {SyntheticEvent, useCallback, useEffect, useRef, useState} from 'react'; + import {throttle} from 'lodash'; -import {getHighlightedItemIndexInView, highlight, scrollToItem} from './utils'; import {CLASSNAME, CLASSNAME_SELECTED, HIGHLIGHT_OPTIONS} from './constants'; +import {getHighlightedItemIndexInView, highlight, scrollToItem} from './utils'; type UseHighlightedSearchWords = { html: string; @@ -223,7 +224,7 @@ export function useSearchBarNavigation({ }, [highlightedDOMElements, headerHeight, hash]); const onClickNextSearch = useCallback( - (e) => { + (e: SyntheticEvent) => { e.stopPropagation(); e.preventDefault(); @@ -244,7 +245,7 @@ export function useSearchBarNavigation({ ); const onClickPrevSearch = useCallback( - (e) => { + (e: SyntheticEvent) => { e.stopPropagation(); e.preventDefault(); diff --git a/src/components/SearchBar/withHighlightedSearchWords.tsx b/src/components/SearchBar/withHighlightedSearchWords.tsx index 6b7007f9..75ab11a1 100644 --- a/src/components/SearchBar/withHighlightedSearchWords.tsx +++ b/src/components/SearchBar/withHighlightedSearchWords.tsx @@ -1,12 +1,12 @@ -import React, {useState, useCallback, useEffect} from 'react'; +import React, {useCallback, useEffect, useState} from 'react'; import {DocPageData, Router} from '../../models'; import { - useHighlightedSearchWords, - useSearchBarNavigation, useCurrentWordSelectionEffect, useCurrentWordSelectionSyncScrollEffect, + useHighlightedSearchWords, + useSearchBarNavigation, } from './hooks'; export interface SearchWordsHighlighterProps extends DocPageData { diff --git a/src/components/SearchPage/SearchPage.tsx b/src/components/SearchPage/SearchPage.tsx index 7a3bc551..2c5e21d4 100644 --- a/src/components/SearchPage/SearchPage.tsx +++ b/src/components/SearchPage/SearchPage.tsx @@ -1,6 +1,7 @@ import React, {useRef, useState} from 'react'; + +import {Button, Loader, TextInput} from '@gravity-ui/uikit'; import block from 'bem-cn-lite'; -import {TextInput, Button, Loader} from '@gravity-ui/uikit'; import {TFunction, withTranslation, WithTranslation, WithTranslationProps} from 'react-i18next'; import {Paginator, PaginatorProps} from '../Paginator'; @@ -123,7 +124,7 @@ function renderFound({ }: RenderFoundProps) { return (
-

{t('search_request-query')}

+

{t('search_request-query')}

{items.map((item: ISearchItem) => ( ) : (
-

{t('search_not-found-title')}

-
{t('search_not-found-text')}
+

{t('search_not-found-title')}

+
{t('search_not-found-text')}
); } @@ -194,7 +195,7 @@ function renderInput({query, onQueryUpdate, onSubmit, inputRef, t}: RenderInput) onSubmit(query); }} > - {t('search_action')} + {t('search_action')}
diff --git a/src/components/StageLabel/StageLabel.tsx b/src/components/StageLabel/StageLabel.tsx index 155e55e9..b2041a5b 100644 --- a/src/components/StageLabel/StageLabel.tsx +++ b/src/components/StageLabel/StageLabel.tsx @@ -1,4 +1,5 @@ import React from 'react'; + import {Mark, MarkColor} from '../Mark'; export type StageType = 'preview' | 'new'; diff --git a/src/components/Subscribe/utils.ts b/src/components/Subscribe/utils.ts index abcf88c3..8aa4ddb6 100644 --- a/src/components/Subscribe/utils.ts +++ b/src/components/Subscribe/utils.ts @@ -1,6 +1,7 @@ -import {SubscribeView} from './Subscribe'; import {PopperPosition} from '../../hooks'; +import {SubscribeView} from './Subscribe'; + export const getPopupPosition = (isVerticalView: boolean | undefined, view?: SubscribeView) => { if (!view || view === SubscribeView.Regular) { return isVerticalView ? PopperPosition.LEFT_START : PopperPosition.BOTTOM_END; diff --git a/src/components/Toc/Toc.tsx b/src/components/Toc/Toc.tsx index dbfdd308..ec9e4822 100644 --- a/src/components/Toc/Toc.tsx +++ b/src/components/Toc/Toc.tsx @@ -1,17 +1,18 @@ import React from 'react'; -import block from 'bem-cn-lite'; +import block from 'bem-cn-lite'; import {omit} from 'lodash'; import {ControlSizes, Lang, Router, TocData, TocItem} from '../../models'; -import {TocItem as Item} from '../TocItem'; -import {HTML} from '../HTML'; -import {Controls} from '../Controls'; +import {PopperPosition} from '../../hooks'; import {isActiveItem, normalizeHash, normalizePath} from '../../utils'; +import {Controls} from '../Controls'; +import {HTML} from '../HTML'; +import {TocItem as Item} from '../TocItem'; + import {TocItemRegistry} from './TocItemRegistry'; import './Toc.scss'; -import {PopperPosition} from '../../hooks'; const b = block('dc-toc'); const HEADER_DEFAULT_HEIGHT = 0; diff --git a/src/components/TocItem/TocItem.tsx b/src/components/TocItem/TocItem.tsx index d33d263a..5979fb21 100644 --- a/src/components/TocItem/TocItem.tsx +++ b/src/components/TocItem/TocItem.tsx @@ -2,9 +2,8 @@ import React from 'react'; import block from 'bem-cn-lite'; import {TocItem as ITocItem} from '../../models'; -import {ToggleArrow} from '../ToggleArrow'; - import {isExternalHref} from '../../utils'; +import {ToggleArrow} from '../ToggleArrow'; import './TocItem.scss'; From 31c0ee5b97967caa57d4422e90f23a2c99ff5213 Mon Sep 17 00:00:00 2001 From: 3y3 <3y3@ya.ru> Date: Tue, 5 Sep 2023 20:22:07 +0300 Subject: [PATCH 04/52] chore: Add internal buildable styles --- src/components/Breadcrumbs/Breadcrumbs.scss | 4 +- .../ContributorAvatars.scss | 4 +- src/components/Contributors/Contributors.scss | 2 +- src/components/Control/Control.scss | 2 +- src/components/Controls/Controls.scss | 4 +- .../SettingsControl/SettingsControl.scss | 2 +- src/components/DocLayout/DocLayout.scss | 4 +- .../DocLeadingPage/DocLeadingPage.scss | 4 +- src/components/DocPage/DocPage.scss | 4 +- src/components/ErrorPage/ErrorPage.scss | 4 +- src/components/Feedback/Feedback.scss | 4 +- src/components/MiniToc/MiniToc.scss | 4 +- src/components/Paginator/Paginator.scss | 4 +- src/components/SearchBar/SearchBar.scss | 4 +- src/components/SearchItem/SearchItem.scss | 4 +- src/components/SearchPage/SearchPage.scss | 4 +- src/components/Subscribe/Subscribe.scss | 4 +- src/components/Toc/Toc.scss | 4 +- src/components/TocItem/TocItem.scss | 4 +- src/components/TocNavPanel/TocNavPanel.scss | 2 +- src/index.scss | 2 + src/styles/mixins.scss | 114 ++++++++++ src/styles/themes.scss | 26 +++ src/styles/variables.scss | 18 ++ src/styles/yfm.scss | 202 ++++++++++++++++++ 25 files changed, 398 insertions(+), 36 deletions(-) create mode 100644 src/index.scss create mode 100644 src/styles/mixins.scss create mode 100644 src/styles/themes.scss create mode 100644 src/styles/variables.scss create mode 100644 src/styles/yfm.scss diff --git a/src/components/Breadcrumbs/Breadcrumbs.scss b/src/components/Breadcrumbs/Breadcrumbs.scss index 18fd19e0..b5d39c4e 100644 --- a/src/components/Breadcrumbs/Breadcrumbs.scss +++ b/src/components/Breadcrumbs/Breadcrumbs.scss @@ -1,5 +1,5 @@ -@import '../../../styles/variables'; -@import '../../../styles/mixins'; +@import '../../styles/variables'; +@import '../../styles/mixins'; .dc-breadcrumbs { @include text-size(body-1); diff --git a/src/components/ContributorAvatars/ContributorAvatars.scss b/src/components/ContributorAvatars/ContributorAvatars.scss index 58694f0f..a610fdd1 100644 --- a/src/components/ContributorAvatars/ContributorAvatars.scss +++ b/src/components/ContributorAvatars/ContributorAvatars.scss @@ -1,5 +1,5 @@ -@import '../../../styles/variables'; -@import '../../../styles/mixins'; +@import '../../styles/variables'; +@import '../../styles/mixins'; $avatarsDistance: 2px; $avatarTextFontSize: 12px; diff --git a/src/components/Contributors/Contributors.scss b/src/components/Contributors/Contributors.scss index e5563a34..75e2e2b4 100644 --- a/src/components/Contributors/Contributors.scss +++ b/src/components/Contributors/Contributors.scss @@ -1,4 +1,4 @@ -@import '../../../styles/mixins'; +@import '../../styles/mixins'; .contributors { display: flex; diff --git a/src/components/Control/Control.scss b/src/components/Control/Control.scss index 92b6686b..5d42388b 100644 --- a/src/components/Control/Control.scss +++ b/src/components/Control/Control.scss @@ -1,4 +1,4 @@ -@import '../../../styles/mixins'; +@import '../../styles/mixins'; .dc-control { &__tooltip { diff --git a/src/components/Controls/Controls.scss b/src/components/Controls/Controls.scss index 5348578c..5620405c 100644 --- a/src/components/Controls/Controls.scss +++ b/src/components/Controls/Controls.scss @@ -1,5 +1,5 @@ -@import '../../../styles/mixins'; -@import '../../../styles/variables'; +@import '../../styles/mixins'; +@import '../../styles/variables'; .dc-controls { display: flex; diff --git a/src/components/Controls/single-controls/SettingsControl/SettingsControl.scss b/src/components/Controls/single-controls/SettingsControl/SettingsControl.scss index be5a3e65..688fa4e9 100644 --- a/src/components/Controls/single-controls/SettingsControl/SettingsControl.scss +++ b/src/components/Controls/single-controls/SettingsControl/SettingsControl.scss @@ -1,4 +1,4 @@ -@import '../../../../../styles/mixins'; +@import '../../../../styles/mixins'; .dc-settings-control { &__text-size-control { diff --git a/src/components/DocLayout/DocLayout.scss b/src/components/DocLayout/DocLayout.scss index 19b78135..780fd52e 100644 --- a/src/components/DocLayout/DocLayout.scss +++ b/src/components/DocLayout/DocLayout.scss @@ -1,5 +1,5 @@ -@import '../../../styles/variables'; -@import '../../../styles/mixins'; +@import '../../styles/variables'; +@import '../../styles/mixins'; .dc-doc-layout { display: flex; diff --git a/src/components/DocLeadingPage/DocLeadingPage.scss b/src/components/DocLeadingPage/DocLeadingPage.scss index f86be419..42f7fdb4 100644 --- a/src/components/DocLeadingPage/DocLeadingPage.scss +++ b/src/components/DocLeadingPage/DocLeadingPage.scss @@ -1,5 +1,5 @@ -@import '../../../styles/variables'; -@import '../../../styles/mixins'; +@import '../../styles/variables'; +@import '../../styles/mixins'; .dc-doc-leading-page { display: flex; diff --git a/src/components/DocPage/DocPage.scss b/src/components/DocPage/DocPage.scss index 4b58b94d..b65f84bb 100644 --- a/src/components/DocPage/DocPage.scss +++ b/src/components/DocPage/DocPage.scss @@ -1,5 +1,5 @@ -@import '../../../styles/variables'; -@import '../../../styles/mixins'; +@import '../../styles/variables'; +@import '../../styles/mixins'; .dc-doc-page { position: relative; diff --git a/src/components/ErrorPage/ErrorPage.scss b/src/components/ErrorPage/ErrorPage.scss index b0f97aef..e3b64a64 100644 --- a/src/components/ErrorPage/ErrorPage.scss +++ b/src/components/ErrorPage/ErrorPage.scss @@ -1,5 +1,5 @@ -@import '../../../styles/variables'; -@import '../../../styles/mixins'; +@import '../../styles/variables'; +@import '../../styles/mixins'; .ErrorPage { display: flex; diff --git a/src/components/Feedback/Feedback.scss b/src/components/Feedback/Feedback.scss index 2b53f2a6..6ff31966 100644 --- a/src/components/Feedback/Feedback.scss +++ b/src/components/Feedback/Feedback.scss @@ -1,5 +1,5 @@ -@import '../../../styles/mixins'; -@import '../../../styles/variables'; +@import '../../styles/mixins'; +@import '../../styles/variables'; $popupWidth: 320px; diff --git a/src/components/MiniToc/MiniToc.scss b/src/components/MiniToc/MiniToc.scss index 09d0c9bc..1d144022 100644 --- a/src/components/MiniToc/MiniToc.scss +++ b/src/components/MiniToc/MiniToc.scss @@ -1,5 +1,5 @@ -@import '../../../styles/variables'; -@import '../../../styles/mixins'; +@import '../../styles/variables'; +@import '../../styles/mixins'; .dc-mini-toc { $class: &; diff --git a/src/components/Paginator/Paginator.scss b/src/components/Paginator/Paginator.scss index b9b9bda2..41d1c733 100644 --- a/src/components/Paginator/Paginator.scss +++ b/src/components/Paginator/Paginator.scss @@ -1,5 +1,5 @@ -@import '../../../styles/variables'; -@import '../../../styles/mixins'; +@import '../../styles/variables'; +@import '../../styles/mixins'; .Paginator { $block: &; diff --git a/src/components/SearchBar/SearchBar.scss b/src/components/SearchBar/SearchBar.scss index 6737ae1e..34d7e79d 100644 --- a/src/components/SearchBar/SearchBar.scss +++ b/src/components/SearchBar/SearchBar.scss @@ -1,5 +1,5 @@ -@import '../../../styles/variables'; -@import '../../../styles/mixins'; +@import '../../styles/variables'; +@import '../../styles/mixins'; .dc-search-bar { width: 100%; diff --git a/src/components/SearchItem/SearchItem.scss b/src/components/SearchItem/SearchItem.scss index 1a35f26b..6faf6a18 100644 --- a/src/components/SearchItem/SearchItem.scss +++ b/src/components/SearchItem/SearchItem.scss @@ -1,5 +1,5 @@ -@import '../../../styles/variables'; -@import '../../../styles/mixins'; +@import '../../styles/variables'; +@import '../../styles/mixins'; .SearchItem { &__link { diff --git a/src/components/SearchPage/SearchPage.scss b/src/components/SearchPage/SearchPage.scss index d9ab9c12..c858caa7 100644 --- a/src/components/SearchPage/SearchPage.scss +++ b/src/components/SearchPage/SearchPage.scss @@ -1,5 +1,5 @@ -@import '../../../styles/variables'; -@import '../../../styles/mixins'; +@import '../../styles/variables'; +@import '../../styles/mixins'; .dc-search-page { margin-top: 26px; diff --git a/src/components/Subscribe/Subscribe.scss b/src/components/Subscribe/Subscribe.scss index 95055420..870d8315 100644 --- a/src/components/Subscribe/Subscribe.scss +++ b/src/components/Subscribe/Subscribe.scss @@ -1,5 +1,5 @@ -@import '../../../styles/mixins'; -@import '../../../styles/variables'; +@import '../../styles/mixins'; +@import '../../styles/variables'; $popupWidth: 320px; diff --git a/src/components/Toc/Toc.scss b/src/components/Toc/Toc.scss index fc1c0fd3..421906f6 100644 --- a/src/components/Toc/Toc.scss +++ b/src/components/Toc/Toc.scss @@ -1,5 +1,5 @@ -@import '../../../styles/variables'; -@import '../../../styles/mixins'; +@import '../../styles/variables'; +@import '../../styles/mixins'; $iconSize: 18px; $toggleSize: 10px; diff --git a/src/components/TocItem/TocItem.scss b/src/components/TocItem/TocItem.scss index 2698ac12..4221f43d 100644 --- a/src/components/TocItem/TocItem.scss +++ b/src/components/TocItem/TocItem.scss @@ -1,5 +1,5 @@ -@import '../../../styles/variables'; -@import '../../../styles/mixins'; +@import '../../styles/variables'; +@import '../../styles/mixins'; .dc-toc-item { cursor: pointer; diff --git a/src/components/TocNavPanel/TocNavPanel.scss b/src/components/TocNavPanel/TocNavPanel.scss index 1b15da6b..ac62d3a9 100644 --- a/src/components/TocNavPanel/TocNavPanel.scss +++ b/src/components/TocNavPanel/TocNavPanel.scss @@ -1,4 +1,4 @@ -@import '../../../styles/mixins'; +@import '../../styles/mixins'; .dc-nav-toc-panel { display: flex; diff --git a/src/index.scss b/src/index.scss new file mode 100644 index 00000000..2c028bf9 --- /dev/null +++ b/src/index.scss @@ -0,0 +1,2 @@ +@import './styles/themes.scss'; +@import './styles/yfm.scss'; diff --git a/src/styles/mixins.scss b/src/styles/mixins.scss new file mode 100644 index 00000000..d57000aa --- /dev/null +++ b/src/styles/mixins.scss @@ -0,0 +1,114 @@ +@import './variables'; + +@mixin text-size($name) { + font-size: var(--g-text-#{$name}-font-size); + line-height: var(--g-text-#{$name}-line-height); +} + +@mixin reset-list-style() { + margin: 0; + padding: 0; + list-style: none; +} + +@mixin fixAnchorPosition($offset: 0px) { + padding-top: calc(var(--dc-header-height, #{$headerHeight})); + margin-top: calc(#{$offset} - var(--dc-header-height, #{$headerHeight})); +} + +@mixin reset-link-style() { + color: inherit; + text-decoration: none; +} + +@mixin islands-focus() { + outline: none; + + .utilityfocus &:focus { + outline: 2px solid #ffdb4d; + } +} + +@mixin link() { + @include islands-focus(); + + color: var(--g-color-text-link); + text-decoration: none; + cursor: pointer; + + &:hover, + &:active { + color: var(--g-color-text-link-hover); + } +} + +@mixin heading1() { + @include text-size(display-3); + font-weight: 500; + + @media (max-width: map-get($screenBreakpoints, 'md') - 1) { + @include text-size(display-2); + } +} + +@mixin heading2() { + @include text-size(display-1); + font-weight: 500; +} + +@mixin heading3() { + @include text-size(header-2); + font-weight: 500; +} + +@mixin heading4() { + @include text-size(header-1); + font-weight: 500; +} + +@mixin heading5() { + @include text-size(body-3); + font-weight: 500; +} + +@mixin heading6() { + @include text-size(body-3); + font-weight: 400; +} + +@mixin contributors-text() { + font-size: 13px; + font-weight: 400; + margin-right: 5px; + align-self: center; +} + +@mixin text-body-1() { + font-size: var(--g-text-body-1-font-size); + line-height: var(--g-text-body-1-line-height); + font-weight: var(--g-text-body-font-weight); +} + +@mixin desktop-only() { + @media (max-width: map-get($screenBreakpoints, 'md') - 1) { + display: none; + } +} + +@mixin desktop-tablet-only() { + @media (max-width: map-get($screenBreakpoints, 'sm') - 1) { + display: none; + } +} + +@mixin mobile-tablet-only() { + @media (min-width: map-get($screenBreakpoints, 'md')) { + display: none; + } +} + +@mixin mobile-only() { + @media (min-width: map-get($screenBreakpoints, 'sm')) { + display: none; + } +} diff --git a/src/styles/themes.scss b/src/styles/themes.scss new file mode 100644 index 00000000..9075881c --- /dev/null +++ b/src/styles/themes.scss @@ -0,0 +1,26 @@ +@import '@gravity-ui/uikit/styles/styles.css'; +@import './yfm.scss'; + +.g-root { + --dc-text-highlight: var(--g-color-base-warning-heavy); + --dc-text-highlight-selected: #ffab3b; + --dc-header-height: 0px; + --dc-subheader-height: 40px; + + --dc-error-image-403: url('data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='); + --dc-error-image-404: url('data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='); + --dc-error-image-500: url('data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='); + + --g-scrollbar-width: 6px; + + &::-webkit-scrollbar, + *::-webkit-scrollbar { + height: var(--g-scrollbar-width); + background: transparent; + } + + &::-webkit-scrollbar-track, + *::-webkit-scrollbar-track { + background: transparent; + } +} diff --git a/src/styles/variables.scss b/src/styles/variables.scss new file mode 100644 index 00000000..c071e8e0 --- /dev/null +++ b/src/styles/variables.scss @@ -0,0 +1,18 @@ +// Sizes +$headerHeight: 0px; +$normalOffset: 20px; +$miniTocOffset: 56px; +$blockMarginBottomLarge: 20px; +$blockMarginBottomMedium: 15px; +$headerMarginTopLarge: 40px; +$headerMarginTopMedium: 32px; +$popupPadding: 16px; +$centerBlockMaxWidth: 736px; + +$screenBreakpoints: ( + 'xs': 375px, + 'sm': 577px, + 'md': 769px, + 'lg': 1081px, + 'xl': 1185px, +); diff --git a/src/styles/yfm.scss b/src/styles/yfm.scss new file mode 100644 index 00000000..09232e08 --- /dev/null +++ b/src/styles/yfm.scss @@ -0,0 +1,202 @@ +@import './mixins'; +@import './variables'; + +.dc-doc-page .yfm { + color: var(--g-color-text-primary); + + --yfm-color-hljs-background: var(--g-color-base-background); + --yfm-color-hljs-subst: var(--g-color-text-complementary); + --yfm-color-hljs-comment: var(--g-color-text-secondary); + --yfm-color-hljs-deletion: var(--g-color-line-danger); + --yfm-color-hljs-section: var(--g-color-line-danger); + + a { + color: var(--g-color-text-link); + + &:hover, + &:active { + color: var(--g-color-text-link-hover); + } + } + + img { + background-color: var(--g-color-base-background); + } + + $backgroundColors: ( + yfm-accent-info: var(--g-color-base-info-light), + yfm-accent-tip: var(--g-color-base-positive-light), + yfm-accent-alert: var(--g-color-base-danger-light), + yfm-accent-warning: var(--g-color-base-warning-light), + ); + + @each $type, $color in $backgroundColors { + &.#{$type} { + background: $color; + } + } + + code { + font-size: var(--dc-code-short-font-size); + line-height: var(--dc-code-short-line-height); + padding: var(--dc-code-short-padding); + background: var(--g-color-base-misc-light); + color: var(--g-color-text-misc); + } + + pre > code { + font-size: var(--dc-code-font-size); + line-height: var(--dc-code-line-height); + padding: 16px; + background: var(--g-color-base-misc-light); + color: var(--g-color-text-complementary); + } + + dfn { + background: var(--g-color-base-float); + box-shadow: 0 8px 20px var(--g-color-sfx-shadow); + } + + dfn:before { + box-shadow: 0 0 0 1px var(--g-color-line-generic-solid); + } + + table { + color: var(--g-color-text-primary); + border-color: var(--g-color-line-generic); + background: var(--g-color-base-background); + } + + table thead, + table tr:nth-child(2n) { + background: var(--g-color-base-generic); + } + + hr { + background-color: var(--g-color-base-background); + } + + blockquote { + border-left-color: var(--g-color-base-brand); + } + + .yfm-tab-list { + border-bottom-color: var(--g-color-line-generic); + } + + .yfm-tab:hover, + .yfm-tab:active { + color: var(--g-color-text-link-hover); + } + + .yfm-tab.active { + border-bottom-color: var(--g-color-base-brand); + } + + /* TODO: move yfm-cut styles to yfm-transform */ + .yfm-cut { + margin-bottom: $blockMarginBottomMedium; + } + + .yfm-cut.open .yfm-cut-content { + padding: 5px 0 0 30px; + } + + .yfm-term_title:hover { + color: var(--g-color-text-link-hover); + } + + // Margin collapse is not applied for a block and header with a negative margin. + // The margin is summed up. So, we need to take 15px away from the header margin-top + h1, + h2 { + @include fixAnchorPosition(calc(#{$headerMarginTopLarge} - #{$blockMarginBottomMedium})); + margin-bottom: $blockMarginBottomLarge; + } + + h3, + h4, + h5, + h6 { + @include fixAnchorPosition(calc(#{$headerMarginTopMedium} - #{$blockMarginBottomMedium})); + } + + h1 + h2, + h2 + h3, + h3 + h4, + h4 + h5, + h5 + h6 { + @include fixAnchorPosition(); + } + + // Use 0px for the top offset of the header if it is the first child element, + // because it is located after the page title + h1, + h2, + h3, + h4, + h5, + h6 { + &:first-child { + @include fixAnchorPosition(); + } + } + + .yfm-page__delimeter { + height: 4px; + border-radius: 2px; + background: var(--g-color-line-generic); + margin-top: 20px; + margin-bottom: 0; + + & + h1, + & + h2, + & + h3, + & + h4, + & + h5, + & + h6 { + @include fixAnchorPosition(#{$headerMarginTopLarge}); + } + } + + .yfm-original-link { + &:before { + content: none; + } + & svg { + visibility: hidden; + } + } + + h1, + h2, + h3, + h4, + h5, + h6 { + .yfm-original-link::before { + content: none; + } + .yfm-original-link svg { + visibility: hidden; + } + &:hover .yfm-original-link svg { + visibility: visible; + } + } + + /* stylelint-disable declaration-no-important */ + .yfm-anchor_hidden { + visibility: hidden; + &:before { + visibility: hidden !important; + } + } + /* stylelint-enable declaration-no-important */ +} + +.yc-root_theme_dark .dc-doc-page .yfm { + .yfm-cut-title:before { + background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+PHBhdGggc3Ryb2tlPSJyZ2JhKDI1NSwgMjU1LCAyNTUsIDAuNykiIGZpbGw9Im5vbmUiIGQ9Ik0zIDZsNSA1IDUtNSI+PC9wYXRoPjwvc3ZnPg=='); + } +} From f2d15aa7ffbb221ca43a0c3fe2eee5b50536cbbe Mon Sep 17 00:00:00 2001 From: 3y3 <3y3@ya.ru> Date: Tue, 5 Sep 2023 21:07:50 +0300 Subject: [PATCH 05/52] feat!: Update react and uikit deps --- package.json | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 264f6bb0..bc2ff45d 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,8 @@ "pre-commit": "lint-staged" }, "dependencies": { - "@gravity-ui/uikit": "^4.3.0", + "@gravity-ui/icons": "^2.5.0", + "@gravity-ui/uikit": "^5.9.0", "@popperjs/core": "^2.11.2", "bem-cn-lite": "4.1.0", "i18next": "^19.9.2", @@ -57,8 +58,8 @@ "scroll-into-view-if-needed": "2.2.29" }, "peerDependencies": { - "react": "^16.0.0", - "react-dom": "^16.0.0", + "react": ">=16.8.0 || >=17.0.0 || >=18.0.0", + "react-dom": ">=16.8.0 || >=17.0.0 || >=18.0.0", "@doc-tools/transform": "^3.10.2" }, "devDependencies": { @@ -69,8 +70,8 @@ "@gravity-ui/tsconfig": "^1.0.0", "@types/langs": "^2.0.1", "@types/lodash": "4.14.179", - "@types/react": "16.14.23", - "@types/react-dom": "16.9.14", + "@types/react": "^18.2.21", + "@types/react-dom": "^18.2.7", "copyfiles": "2.4.1", "node-sass": "4.14.1", "npm-run-all": "4.1.5", @@ -80,6 +81,9 @@ "lint-staged": "^14.0.1", "postcss": "^8.4.28", "prettier": "^2.8.8", + "prop-types": "^15.8.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", "stylelint": "^15.10.3", "svgo": "2.8.0", "typescript": "^5.2.2" From 2da870b89356d54235bfa1cf1fe23b51e748f835 Mon Sep 17 00:00:00 2001 From: 3y3 <3y3@ya.ru> Date: Fri, 25 Aug 2023 18:09:24 +0300 Subject: [PATCH 06/52] feat(deps)!: Migrate to uikit@5 --- .../BookmarkButton/BookmarkButton.scss | 4 +- src/components/Breadcrumbs/Breadcrumbs.scss | 6 +- .../ContributorAvatars/Avatars/Details.tsx | 1 + .../ContributorAvatars.scss | 12 ++-- src/components/Control/Control.tsx | 2 +- .../DividerControl/DividerControl.scss | 2 +- .../SettingsControl/SettingsControl.scss | 4 +- .../SettingsControl/SettingsControl.tsx | 2 +- src/components/DocLayout/DocLayout.scss | 4 +- .../DocLeadingPage/DocLeadingPage.scss | 2 +- src/components/DocPage/DocPage.scss | 4 +- src/components/Feedback/Feedback.scss | 18 +++--- src/components/Feedback/Feedback.tsx | 7 +-- src/components/MiniToc/MiniToc.scss | 12 ++-- src/components/Paginator/Paginator.scss | 10 +-- src/components/SearchBar/SearchBar.scss | 10 +-- src/components/SearchItem/SearchItem.scss | 10 +-- src/components/SearchPage/SearchPage.scss | 2 +- src/components/Subscribe/Subscribe.scss | 16 ++--- .../SubscribeSuccessPopup.tsx | 2 +- .../SubscribeVariantsPopup.tsx | 2 +- src/components/Toc/Toc.scss | 13 ++-- src/components/TocItem/TocItem.scss | 6 +- src/components/TocNavPanel/TocNavPanel.scss | 8 +-- styles/default.scss | 4 +- styles/mixins.scss | 14 ++--- styles/themes.scss | 2 +- styles/yfm.scss | 62 +++++++++---------- 28 files changed, 121 insertions(+), 120 deletions(-) diff --git a/src/components/BookmarkButton/BookmarkButton.scss b/src/components/BookmarkButton/BookmarkButton.scss index 889a6820..39faf819 100644 --- a/src/components/BookmarkButton/BookmarkButton.scss +++ b/src/components/BookmarkButton/BookmarkButton.scss @@ -1,12 +1,12 @@ .dc-bookmark-button { vertical-align: middle; - margin: 0 4px 4px 4px; + margin: 0 4px 4px; &:hover::before { background-color: transparent; } &_active &__icon { - color: var(--yc-color-base-special); + color: var(--g-color-base-brand); } } diff --git a/src/components/Breadcrumbs/Breadcrumbs.scss b/src/components/Breadcrumbs/Breadcrumbs.scss index b5d39c4e..50dda193 100644 --- a/src/components/Breadcrumbs/Breadcrumbs.scss +++ b/src/components/Breadcrumbs/Breadcrumbs.scss @@ -12,7 +12,7 @@ overflow: hidden; text-overflow: ellipsis; - color: var(--yc-color-text-secondary); + color: var(--g-color-text-secondary); } &__item { @@ -21,14 +21,14 @@ &::after { content: '\a0/ '; margin: 0 5px; - color: var(--yc-color-text-hint); + color: var(--g-color-text-hint); } &:last-child::after { display: none; } &:last-child { - color: var(--yc-color-text-primary); + color: var(--g-color-text-primary); } } diff --git a/src/components/ContributorAvatars/Avatars/Details.tsx b/src/components/ContributorAvatars/Avatars/Details.tsx index 6a341910..bb6e3d01 100644 --- a/src/components/ContributorAvatars/Avatars/Details.tsx +++ b/src/components/ContributorAvatars/Avatars/Details.tsx @@ -34,6 +34,7 @@ const Details: React.FC = (props) => { open={isVisiblePopup} onOutsideClick={() => changeVisiblilityPopup(false)} className={b('popup')} + contentClassName={b('popup_content')} placement={PopperPosition.BOTTOM} hasArrow={true} > diff --git a/src/components/ContributorAvatars/ContributorAvatars.scss b/src/components/ContributorAvatars/ContributorAvatars.scss index a610fdd1..6929300e 100644 --- a/src/components/ContributorAvatars/ContributorAvatars.scss +++ b/src/components/ContributorAvatars/ContributorAvatars.scss @@ -69,7 +69,7 @@ $avatarWrapperBigSize: 33px; &__avatar { & { border-radius: 50%; - box-shadow: 0 0 0 $avatarBorder var(--yc-color-line-generic-accent); + box-shadow: 0 0 0 $avatarBorder var(--g-color-line-generic-accent); } &_size_small { @@ -105,13 +105,13 @@ $avatarWrapperBigSize: 33px; &__hidden_avatars { font-size: $avatarTextFontSize; font-weight: $avatarTextFontWeight; - color: var(--yc-color-text-secondary); + color: var(--g-color-text-secondary); border-radius: 50%; text-align: center; cursor: pointer; & > * { - background: var(--yc-color-base-misc); + background: var(--g-color-base-misc-light); flex-direction: column; text-align: center; display: flex; @@ -119,7 +119,7 @@ $avatarWrapperBigSize: 33px; padding-left: 3px; padding-right: 1px; margin: 0 $avatarBorder; - box-shadow: 0 0 0 $avatarBorder var(--yc-color-line-generic-accent); + box-shadow: 0 0 0 $avatarBorder var(--g-color-line-generic-accent); } } @@ -134,14 +134,16 @@ $avatarWrapperBigSize: 33px; } &__details_name { - color: var(--yc-color-text-primary); + color: var(--g-color-text-primary); line-height: $textLineHeight; } &__popup { box-shadow: 0px 3px 24px rgba(0, 0, 0, 0.2); @include disable-link-styles; + } + &__popup_content { & > *:not(:first-child) { margin: $detailsIndent; } diff --git a/src/components/Control/Control.tsx b/src/components/Control/Control.tsx index da5944ae..8ee101bf 100644 --- a/src/components/Control/Control.tsx +++ b/src/components/Control/Control.tsx @@ -88,7 +88,7 @@ const Control = (props: ControlProps) => { anchorRef={controlRef} open={isVisibleTooltip} onOutsideClick={hideTooltip} - className={b('tooltip')} + contentClassName={b('tooltip')} placement={position} > {tooltipText} diff --git a/src/components/Controls/single-controls/DividerControl/DividerControl.scss b/src/components/Controls/single-controls/DividerControl/DividerControl.scss index 39c01f1a..8582b7d2 100644 --- a/src/components/Controls/single-controls/DividerControl/DividerControl.scss +++ b/src/components/Controls/single-controls/DividerControl/DividerControl.scss @@ -1,5 +1,5 @@ .dc-divider-control { - background: var(--yc-color-line-generic); + background: var(--g-color-line-generic); &:first-child { display: none; diff --git a/src/components/Controls/single-controls/SettingsControl/SettingsControl.scss b/src/components/Controls/single-controls/SettingsControl/SettingsControl.scss index 688fa4e9..24638b39 100644 --- a/src/components/Controls/single-controls/SettingsControl/SettingsControl.scss +++ b/src/components/Controls/single-controls/SettingsControl/SettingsControl.scss @@ -20,7 +20,7 @@ &__text-size-button-icon { &_active { - color: var(--yc-color-base-special); + color: var(--g-color-base-brand); } } @@ -49,7 +49,7 @@ } &__list-item-description { - color: var(--yc-color-text-secondary); + color: var(--g-color-text-secondary); } &__list-item-control { diff --git a/src/components/Controls/single-controls/SettingsControl/SettingsControl.tsx b/src/components/Controls/single-controls/SettingsControl/SettingsControl.tsx index 211ec4fe..14ac7fa9 100644 --- a/src/components/Controls/single-controls/SettingsControl/SettingsControl.tsx +++ b/src/components/Controls/single-controls/SettingsControl/SettingsControl.tsx @@ -205,7 +205,7 @@ const SettingsControl = (props: ControlInnerProps) => { diff --git a/src/components/DocLayout/DocLayout.scss b/src/components/DocLayout/DocLayout.scss index 780fd52e..866dcc9b 100644 --- a/src/components/DocLayout/DocLayout.scss +++ b/src/components/DocLayout/DocLayout.scss @@ -6,7 +6,7 @@ align-items: center; margin: 0 auto; height: 100%; - background: var(--yc-color-base-background); + background: var(--g-color-base-background); @media (max-width: map-get($screenBreakpoints, 'md') - 1) { align-items: normal; @@ -61,7 +61,7 @@ top: var(--dc-header-height, #{$headerHeight}); padding-left: 24px; width: 252px; - background-color: var(--yc-color-base-background); + background-color: var(--g-color-base-background); @include text-size(body-1); } diff --git a/src/components/DocLeadingPage/DocLeadingPage.scss b/src/components/DocLeadingPage/DocLeadingPage.scss index 42f7fdb4..08293250 100644 --- a/src/components/DocLeadingPage/DocLeadingPage.scss +++ b/src/components/DocLeadingPage/DocLeadingPage.scss @@ -58,7 +58,7 @@ width: calc(50% - 20px); padding-bottom: 40px; margin-bottom: $blockMarginBottomLarge; - border-bottom: 1px solid var(--yc-color-line-generic); + border-bottom: 1px solid var(--g-color-line-generic); } } diff --git a/src/components/DocPage/DocPage.scss b/src/components/DocPage/DocPage.scss index b65f84bb..6d7e59f1 100644 --- a/src/components/DocPage/DocPage.scss +++ b/src/components/DocPage/DocPage.scss @@ -35,7 +35,7 @@ } &__loader-wrapper { - background: var(--yc-color-base-background); + background: var(--g-color-base-background); padding-top: calc((100vh - var(--dc-header-height, #{$headerHeight})) / 2); display: flex; justify-content: center; @@ -141,7 +141,7 @@ } &__search-bar { - background: var(--yc-color-base-background); + background: var(--g-color-base-background); width: 100%; height: 40px; position: sticky; diff --git a/src/components/Feedback/Feedback.scss b/src/components/Feedback/Feedback.scss index 6ff31966..b3bea843 100644 --- a/src/components/Feedback/Feedback.scss +++ b/src/components/Feedback/Feedback.scss @@ -30,7 +30,7 @@ $popupWidth: 320px; width: 100%; padding: 28px; - border: 1px solid var(--yc-color-line-generic); + border: 1px solid var(--g-color-line-generic); border-radius: 8px; &_view_wide { @@ -41,7 +41,7 @@ $popupWidth: 320px; &__like-button { &_active { - color: var(--yc-color-base-special); + color: var(--g-color-base-brand); } } @@ -64,14 +64,14 @@ $popupWidth: 320px; } &__popup-title { - font-size: var(--yc-text-body-2-font-size); - line-height: var(--yc-text-body-2-line-height); + font-size: var(--g-text-body-2-font-size); + line-height: var(--g-text-body-2-line-height); padding-bottom: 8px; } &__popup-text { - font-size: var(--yc-text-body-short-font-size); - line-height: var(--yc-text-body-short-line-height); + font-size: var(--g-text-body-short-font-size); + line-height: var(--g-text-body-short-line-height); } &__variants { @@ -105,8 +105,8 @@ $popupWidth: 320px; &__title { &_view_wide { - font-size: var(--yc-text-body-2-font-size); - line-height: var(--yc-text-body-2-line-height); + font-size: var(--g-text-body-2-font-size); + line-height: var(--g-text-body-2-line-height); margin-right: 16px; font-weight: 400; } @@ -148,7 +148,7 @@ $popupWidth: 320px; &__feedback-button { &_active { - color: var(--yc-color-base-special); + color: var(--g-color-base-brand); } } } diff --git a/src/components/Feedback/Feedback.tsx b/src/components/Feedback/Feedback.tsx index a952cb4a..1d807d29 100644 --- a/src/components/Feedback/Feedback.tsx +++ b/src/components/Feedback/Feedback.tsx @@ -1,7 +1,7 @@ import React, {useCallback, useState, useEffect, useRef} from 'react'; import block from 'bem-cn-lite'; import {withTranslation, WithTranslation, WithTranslationProps} from 'react-i18next'; -import {Checkbox, Popup, TextInput, Button, Icon as IconComponent} from '@gravity-ui/uikit'; +import {Checkbox, Popup, TextArea, Button, Icon as IconComponent} from '@gravity-ui/uikit'; import {Control} from '../Control'; import {PopperPosition} from '../../hooks'; @@ -334,7 +334,7 @@ const Feedback: React.FC = (props) => { anchorRef={anchor} open={visible} onOutsideClick={hideFeedbackPopups} - className={b('success-popup', {view})} + contentClassName={b('success-popup', {view})} placement={getPopupPosition()} >

{t('success-title')}

@@ -378,8 +378,7 @@ const Feedback: React.FC = (props) => { const renderDislikeVariantsTextArea = useCallback(() => { return (
- #{$class}__section-link { - border-left-color: var(--yc-color-line-generic); + border-left-color: var(--g-color-line-generic); } &-link { @@ -34,12 +34,12 @@ padding: 6px 6px 6px 12px; min-height: 28px; - color: var(--yc-color-text-secondary); + color: var(--g-color-text-secondary); border-left: 2px solid transparent; text-decoration: none; &:hover { - color: var(--yc-color-text-complementary); + color: var(--g-color-text-complementary); } } @@ -50,8 +50,8 @@ } &_active > #{$class}__section-link { - color: var(--yc-color-text-primary); - border-left-color: var(--yc-color-text-primary); + color: var(--g-color-text-primary); + border-left-color: var(--g-color-text-primary); } } } diff --git a/src/components/Paginator/Paginator.scss b/src/components/Paginator/Paginator.scss index 41d1c733..5c40f552 100644 --- a/src/components/Paginator/Paginator.scss +++ b/src/components/Paginator/Paginator.scss @@ -20,7 +20,7 @@ min-height: 28px; margin: 4px; - color: var(--yc-color-text-primary); + color: var(--g-color-text-primary); @include text-size(body-1); &_type { @@ -34,7 +34,7 @@ &_dots { min-width: 0; - color: var(--yc-color-text-secondary); + color: var(--g-color-text-secondary); } &_page { @@ -42,12 +42,12 @@ border-radius: 14px; &:hover { - background: var(--yc-color-base-simple-hover); + background: var(--g-color-base-simple-hover); } &#{$item}_active { - color: var(--yc-color-text-light-primary); - background: var(--yc-color-base-special); + color: var(--g-color-text-light-primary); + background: var(--g-color-base-brand); cursor: default; } } diff --git a/src/components/SearchBar/SearchBar.scss b/src/components/SearchBar/SearchBar.scss index 34d7e79d..482afbb7 100644 --- a/src/components/SearchBar/SearchBar.scss +++ b/src/components/SearchBar/SearchBar.scss @@ -5,17 +5,17 @@ width: 100%; height: 100%; border-radius: 5px; - box-shadow: 0px 3px 10px var(--yc-color-sfx-shadow); + box-shadow: 0px 3px 10px var(--g-color-sfx-shadow); padding: 11px; display: flex; align-items: center; justify-content: space-between; - font-size: var(--yc-text-body-short-font-size); - line-height: var(--yc-text-body-short-line-height); + font-size: var(--g-text-body-short-font-size); + line-height: var(--g-text-body-short-line-height); &__search-query-label { - color: var(--yc-color-text-secondary); + color: var(--g-color-text-secondary); white-space: nowrap; text-overflow: ellipsis; overflow: hidden; @@ -70,6 +70,6 @@ $hl-class: '.dc-search-highlighted'; .yc-root_theme_dark { & #{$hl-class}, & #{$hl-class}_selected { - color: var(--yc-color-text-inverted-primary); + color: var(--g-color-text-inverted-primary); } } diff --git a/src/components/SearchItem/SearchItem.scss b/src/components/SearchItem/SearchItem.scss index 6faf6a18..490ea99e 100644 --- a/src/components/SearchItem/SearchItem.scss +++ b/src/components/SearchItem/SearchItem.scss @@ -9,7 +9,7 @@ &:hover, &:active { - color: var(--yc-color-text-link-hover); + color: var(--g-color-text-link-hover); } } @@ -63,17 +63,17 @@ &-title, &-title &-search-token { @include link(); - color: var(--yc-color-text-primary); + color: var(--g-color-text-primary); @include text-size(subheader-3); } &-title:hover &-search-token { - color: var(--yc-color-text-link-hover); + color: var(--g-color-text-link-hover); } &-description { display: inline-block; - color: var(--yc-color-text-secondary); + color: var(--g-color-text-secondary); em { font-weight: bolder; @@ -85,7 +85,7 @@ } } &-description &-search-token { - color: var(--yc-color-text-primary); + color: var(--g-color-text-primary); } } } diff --git a/src/components/SearchPage/SearchPage.scss b/src/components/SearchPage/SearchPage.scss index c858caa7..57a0b559 100644 --- a/src/components/SearchPage/SearchPage.scss +++ b/src/components/SearchPage/SearchPage.scss @@ -33,7 +33,7 @@ &__paginator { padding: 10px 0; - border-top: 1px solid var(--yc-color-line-generic); + border-top: 1px solid var(--g-color-line-generic); } @media (max-width: map-get($screenBreakpoints, 'md')) { diff --git a/src/components/Subscribe/Subscribe.scss b/src/components/Subscribe/Subscribe.scss index 870d8315..895526dc 100644 --- a/src/components/Subscribe/Subscribe.scss +++ b/src/components/Subscribe/Subscribe.scss @@ -30,7 +30,7 @@ $popupWidth: 320px; width: 100%; padding: 28px; - border: 1px solid var(--yc-color-line-generic); + border: 1px solid var(--g-color-line-generic); border-radius: 8px; &_view_wide { @@ -58,14 +58,14 @@ $popupWidth: 320px; } &__popup-title { - font-size: var(--yc-text-body2-font-size); - line-height: var(--yc-text-body2-line-height); + font-size: var(--g-text-body2-font-size); + line-height: var(--g-text-body2-line-height); padding-bottom: 8px; } &__popup-text { - font-size: var(--yc-text-body-1-font-size); - line-height: var(--yc-text-body-1-line-height); + font-size: var(--g-text-body-1-font-size); + line-height: var(--g-text-body-1-line-height); } &__variants-actions { @@ -87,8 +87,8 @@ $popupWidth: 320px; &__title { &_view_wide { - font-size: var(--yc-text-body2-font-size); - line-height: var(--yc-text-body2-line-height); + font-size: var(--g-text-body2-font-size); + line-height: var(--g-text-body2-line-height); margin-right: 16px; font-weight: 400; } @@ -136,7 +136,7 @@ $popupWidth: 320px; } &_active { - color: var(--yc-color-button-accent-background); + color: var(--g-color-button-accent-background); } } } diff --git a/src/components/Subscribe/SubscribeSuccessPopup/SubscribeSuccessPopup.tsx b/src/components/Subscribe/SubscribeSuccessPopup/SubscribeSuccessPopup.tsx index 24af9cd2..2f8d4733 100644 --- a/src/components/Subscribe/SubscribeSuccessPopup/SubscribeSuccessPopup.tsx +++ b/src/components/Subscribe/SubscribeSuccessPopup/SubscribeSuccessPopup.tsx @@ -29,7 +29,7 @@ const SubscribeSuccessPopup: React.FC< anchorRef={anchor} open={visible} onOutsideClick={hide} - className={b('success-popup', {view})} + contentClassName={b('success-popup', {view})} placement={getPopupPosition(isVerticalView, view)} >

{t('verify-title')}

diff --git a/src/components/Subscribe/SubscribeVariantsPopup/SubscribeVariantsPopup.tsx b/src/components/Subscribe/SubscribeVariantsPopup/SubscribeVariantsPopup.tsx index 672b6698..d5b87f6d 100644 --- a/src/components/Subscribe/SubscribeVariantsPopup/SubscribeVariantsPopup.tsx +++ b/src/components/Subscribe/SubscribeVariantsPopup/SubscribeVariantsPopup.tsx @@ -114,7 +114,7 @@ const SubscribeVariantsPopup: React.FC< anchorRef={anchor} open={visible} onOutsideClick={hide} - className={b('variants-popup', {view})} + contentClassName={b('variants-popup', {view})} placement={getPopupPosition(isVerticalView, view)} > {renderSubscribeVariantsList()} diff --git a/src/components/Toc/Toc.scss b/src/components/Toc/Toc.scss index 421906f6..8f075b66 100644 --- a/src/components/Toc/Toc.scss +++ b/src/components/Toc/Toc.scss @@ -17,7 +17,7 @@ $leftOffset: 57px; flex-direction: column; height: calc(100vh - var(--dc-header-height, #{$headerHeight})); overflow-y: hidden; - border-right: 1px solid var(--yc-color-line-generic); + border-right: 1px solid var(--g-color-line-generic); &__empty { padding: 5px 15px 5px 24px; @@ -31,7 +31,7 @@ $leftOffset: 57px; padding: 10px 10px 8px 0; &_scrolled { - border-bottom: 1px solid var(--yc-color-line-generic); + border-bottom: 1px solid var(--g-color-line-generic); } &-header-icon { @@ -56,7 +56,7 @@ $leftOffset: 57px; &:hover { @include link(); - color: var(--yc-color-text-primary); + color: var(--g-color-text-primary); } } } @@ -70,13 +70,12 @@ $leftOffset: 57px; padding: 4px 10px 20px 0; &_scrolled { - border-top: 1px solid var(--yc-color-line-generic); + border-top: 1px solid var(--g-color-line-generic); } } &__content { - --yc-scrollbar-width: 6px; - --yc-scrollbar-thin-width: 6px; + --g-scrollbar-width: 6px; position: relative; flex-grow: 1; @@ -98,7 +97,7 @@ $leftOffset: 57px; user-select: none; &_main > *:first-child { - color: var(--yc-color-text-primary); + color: var(--g-color-text-primary); } &:not(&_opened) > #{$class}__list { diff --git a/src/components/TocItem/TocItem.scss b/src/components/TocItem/TocItem.scss index 4221f43d..cf0ec312 100644 --- a/src/components/TocItem/TocItem.scss +++ b/src/components/TocItem/TocItem.scss @@ -15,11 +15,11 @@ padding: 7px 12px 7px 20px; word-break: break-word; - color: var(--yc-color-text-primary); + color: var(--g-color-text-primary); &_active { border-radius: 3px; - background: var(--yc-color-base-selection); + background: var(--g-color-base-selection); } &::before { @@ -34,7 +34,7 @@ &:hover { border-radius: 3px; - background: var(--yc-color-base-simple-hover); + background: var(--g-color-base-simple-hover); } } diff --git a/src/components/TocNavPanel/TocNavPanel.scss b/src/components/TocNavPanel/TocNavPanel.scss index ac62d3a9..d9985f92 100644 --- a/src/components/TocNavPanel/TocNavPanel.scss +++ b/src/components/TocNavPanel/TocNavPanel.scss @@ -4,7 +4,7 @@ display: flex; justify-content: center; width: 100%; - background: var(--yc-color-base-background); + background: var(--g-color-base-background); @include text-size(body-1); @@ -13,7 +13,7 @@ bottom: 0; z-index: 1000; left: 0; - border-top: 1px solid var(--yc-color-line-generic); + border-top: 1px solid var(--g-color-line-generic); } &_fixed &__control-hint { @@ -59,7 +59,7 @@ } &__control-hint { - color: var(--yc-color-text-hint); + color: var(--g-color-text-hint); } &__control-text { @@ -71,7 +71,7 @@ display: flex; align-items: center; min-height: 32px; - color: var(--yc-color-text-primary); + color: var(--g-color-text-primary); font-weight: 500; } } diff --git a/styles/default.scss b/styles/default.scss index a716ceb3..f856f1d2 100644 --- a/styles/default.scss +++ b/styles/default.scss @@ -18,11 +18,11 @@ body { --dc-error-image-500: url('data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='); .yc-root { - --yc-scrollbar-thin-width: 6px; + --g-scrollbar-width: 6px; &::-webkit-scrollbar, *::-webkit-scrollbar { - height: var(--yc-scrollbar-thin-width); + height: var(--g-scrollbar-width); background: transparent; } diff --git a/styles/mixins.scss b/styles/mixins.scss index 5d386856..d57000aa 100644 --- a/styles/mixins.scss +++ b/styles/mixins.scss @@ -1,8 +1,8 @@ @import './variables'; @mixin text-size($name) { - font-size: var(--yc-text-#{$name}-font-size); - line-height: var(--yc-text-#{$name}-line-height); + font-size: var(--g-text-#{$name}-font-size); + line-height: var(--g-text-#{$name}-line-height); } @mixin reset-list-style() { @@ -32,13 +32,13 @@ @mixin link() { @include islands-focus(); - color: var(--yc-color-text-link); + color: var(--g-color-text-link); text-decoration: none; cursor: pointer; &:hover, &:active { - color: var(--yc-color-text-link-hover); + color: var(--g-color-text-link-hover); } } @@ -84,9 +84,9 @@ } @mixin text-body-1() { - font-size: var(--yc-text-body-1-font-size); - line-height: var(--yc-text-body-1-line-height); - font-weight: var(--yc-text-body-font-weight); + font-size: var(--g-text-body-1-font-size); + line-height: var(--g-text-body-1-line-height); + font-weight: var(--g-text-body-font-weight); } @mixin desktop-only() { diff --git a/styles/themes.scss b/styles/themes.scss index ea8d632e..25260e46 100644 --- a/styles/themes.scss +++ b/styles/themes.scss @@ -2,6 +2,6 @@ @import './yfm.scss'; .yc-root { - --dc-text-highlight: var(--yc-color-base-warning-heavy); + --dc-text-highlight: var(--g-color-base-warning-heavy); --dc-text-highlight-selected: #ffab3b; } diff --git a/styles/yfm.scss b/styles/yfm.scss index 81b77d0f..09232e08 100644 --- a/styles/yfm.scss +++ b/styles/yfm.scss @@ -2,32 +2,32 @@ @import './variables'; .dc-doc-page .yfm { - color: var(--yc-color-text-primary); + color: var(--g-color-text-primary); - --yfm-color-hljs-background: var(--yc-color-base-background); - --yfm-color-hljs-subst: var(--yc-color-text-complementary); - --yfm-color-hljs-comment: var(--yc-color-text-secondary); - --yfm-color-hljs-deletion: var(--yc-color-line-danger); - --yfm-color-hljs-section: var(--yc-color-line-danger); + --yfm-color-hljs-background: var(--g-color-base-background); + --yfm-color-hljs-subst: var(--g-color-text-complementary); + --yfm-color-hljs-comment: var(--g-color-text-secondary); + --yfm-color-hljs-deletion: var(--g-color-line-danger); + --yfm-color-hljs-section: var(--g-color-line-danger); a { - color: var(--yc-color-text-link); + color: var(--g-color-text-link); &:hover, &:active { - color: var(--yc-color-text-link-hover); + color: var(--g-color-text-link-hover); } } img { - background-color: var(--yc-color-base-background); + background-color: var(--g-color-base-background); } $backgroundColors: ( - yfm-accent-info: var(--yc-color-base-info), - yfm-accent-tip: var(--yc-color-base-positive), - yfm-accent-alert: var(--yc-color-base-danger), - yfm-accent-warning: var(--yc-color-base-warning), + yfm-accent-info: var(--g-color-base-info-light), + yfm-accent-tip: var(--g-color-base-positive-light), + yfm-accent-alert: var(--g-color-base-danger-light), + yfm-accent-warning: var(--g-color-base-warning-light), ); @each $type, $color in $backgroundColors { @@ -40,57 +40,57 @@ font-size: var(--dc-code-short-font-size); line-height: var(--dc-code-short-line-height); padding: var(--dc-code-short-padding); - background: var(--yc-color-base-misc); - color: var(--yc-color-text-misc); + background: var(--g-color-base-misc-light); + color: var(--g-color-text-misc); } pre > code { font-size: var(--dc-code-font-size); line-height: var(--dc-code-line-height); padding: 16px; - background: var(--yc-color-base-misc); - color: var(--yc-color-text-complementary); + background: var(--g-color-base-misc-light); + color: var(--g-color-text-complementary); } dfn { - background: var(--yc-color-base-float); - box-shadow: 0 8px 20px var(--yc-color-sfx-shadow); + background: var(--g-color-base-float); + box-shadow: 0 8px 20px var(--g-color-sfx-shadow); } dfn:before { - box-shadow: 0 0 0 1px var(--yc-popup-border-color); + box-shadow: 0 0 0 1px var(--g-color-line-generic-solid); } table { - color: var(--yc-color-text-primary); - border-color: var(--yc-color-line-generic); - background: var(--yc-color-base-background); + color: var(--g-color-text-primary); + border-color: var(--g-color-line-generic); + background: var(--g-color-base-background); } table thead, table tr:nth-child(2n) { - background: var(--yc-color-base-generic); + background: var(--g-color-base-generic); } hr { - background-color: var(--yc-color-base-background); + background-color: var(--g-color-base-background); } blockquote { - border-left-color: var(--yc-color-base-special); + border-left-color: var(--g-color-base-brand); } .yfm-tab-list { - border-bottom-color: var(--yc-color-line-generic); + border-bottom-color: var(--g-color-line-generic); } .yfm-tab:hover, .yfm-tab:active { - color: var(--yc-color-text-link-hover); + color: var(--g-color-text-link-hover); } .yfm-tab.active { - border-bottom-color: var(--yc-color-base-special); + border-bottom-color: var(--g-color-base-brand); } /* TODO: move yfm-cut styles to yfm-transform */ @@ -103,7 +103,7 @@ } .yfm-term_title:hover { - color: var(--yc-color-text-link-hover); + color: var(--g-color-text-link-hover); } // Margin collapse is not applied for a block and header with a negative margin. @@ -145,7 +145,7 @@ .yfm-page__delimeter { height: 4px; border-radius: 2px; - background: var(--yc-color-line-generic); + background: var(--g-color-line-generic); margin-top: 20px; margin-bottom: 0; From c362b252e484ff61b650430656b8e4fd80d9c1f3 Mon Sep 17 00:00:00 2001 From: 3y3 <3y3@ya.ru> Date: Tue, 5 Sep 2023 21:25:19 +0300 Subject: [PATCH 07/52] feat!: Replace icons with equal from uikit --- assets/icons/arcanum.svg | 36 ------------------- assets/icons/arrow-left.svg | 3 -- assets/icons/arrow-right.svg | 3 -- assets/icons/checkbox-tick.svg | 3 -- assets/icons/checkmark.svg | 3 -- assets/icons/chevron.svg | 3 -- assets/icons/close.svg | 3 -- assets/icons/cog-marked.svg | 4 --- assets/icons/cog.svg | 3 -- assets/icons/dislike.svg | 3 -- assets/icons/edit.svg | 3 -- assets/icons/full-screen.svg | 3 -- assets/icons/github.svg | 3 -- assets/icons/lang.svg | 3 -- assets/icons/like.svg | 3 -- assets/icons/search-bar-arrow.svg | 1 - assets/icons/star-active.svg | 3 -- assets/icons/star-inactive.svg | 3 -- .../BookmarkButton/BookmarkButton.tsx | 4 +-- src/components/Controls/Controls.tsx | 2 +- .../single-controls/FullScreenControl.tsx | 2 +- .../Controls/single-controls/LangControl.tsx | 2 +- .../SettingsControl/SettingsControl.tsx | 4 +-- src/components/EditButton/EditButton.tsx | 2 +- src/components/Feedback/Feedback.tsx | 4 +-- src/components/Paginator/Paginator.tsx | 6 ++-- src/components/SearchBar/SearchBar.tsx | 4 +-- src/components/TocNavPanel/TocNavPanel.tsx | 4 +-- src/components/ToggleArrow/ToggleArrow.tsx | 2 +- 29 files changed, 18 insertions(+), 104 deletions(-) delete mode 100644 assets/icons/arcanum.svg delete mode 100644 assets/icons/arrow-left.svg delete mode 100644 assets/icons/arrow-right.svg delete mode 100644 assets/icons/checkbox-tick.svg delete mode 100644 assets/icons/checkmark.svg delete mode 100644 assets/icons/chevron.svg delete mode 100644 assets/icons/close.svg delete mode 100644 assets/icons/cog-marked.svg delete mode 100644 assets/icons/cog.svg delete mode 100644 assets/icons/dislike.svg delete mode 100644 assets/icons/edit.svg delete mode 100644 assets/icons/full-screen.svg delete mode 100644 assets/icons/github.svg delete mode 100644 assets/icons/lang.svg delete mode 100644 assets/icons/like.svg delete mode 100644 assets/icons/search-bar-arrow.svg delete mode 100644 assets/icons/star-active.svg delete mode 100644 assets/icons/star-inactive.svg diff --git a/assets/icons/arcanum.svg b/assets/icons/arcanum.svg deleted file mode 100644 index d334b2b2..00000000 --- a/assets/icons/arcanum.svg +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/icons/arrow-left.svg b/assets/icons/arrow-left.svg deleted file mode 100644 index 441560d1..00000000 --- a/assets/icons/arrow-left.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/arrow-right.svg b/assets/icons/arrow-right.svg deleted file mode 100644 index 5986a74d..00000000 --- a/assets/icons/arrow-right.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/checkbox-tick.svg b/assets/icons/checkbox-tick.svg deleted file mode 100644 index bb03ecbd..00000000 --- a/assets/icons/checkbox-tick.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/checkmark.svg b/assets/icons/checkmark.svg deleted file mode 100644 index 3a1edd1a..00000000 --- a/assets/icons/checkmark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/chevron.svg b/assets/icons/chevron.svg deleted file mode 100644 index aad5482c..00000000 --- a/assets/icons/chevron.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/close.svg b/assets/icons/close.svg deleted file mode 100644 index 1b8cf78e..00000000 --- a/assets/icons/close.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/cog-marked.svg b/assets/icons/cog-marked.svg deleted file mode 100644 index 7cee03d7..00000000 --- a/assets/icons/cog-marked.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/assets/icons/cog.svg b/assets/icons/cog.svg deleted file mode 100644 index 826b7b8b..00000000 --- a/assets/icons/cog.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/dislike.svg b/assets/icons/dislike.svg deleted file mode 100644 index 73c93be2..00000000 --- a/assets/icons/dislike.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/edit.svg b/assets/icons/edit.svg deleted file mode 100644 index 41c08acc..00000000 --- a/assets/icons/edit.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/full-screen.svg b/assets/icons/full-screen.svg deleted file mode 100644 index afc2990a..00000000 --- a/assets/icons/full-screen.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/github.svg b/assets/icons/github.svg deleted file mode 100644 index a1e4abdf..00000000 --- a/assets/icons/github.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/lang.svg b/assets/icons/lang.svg deleted file mode 100644 index 341413e4..00000000 --- a/assets/icons/lang.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/like.svg b/assets/icons/like.svg deleted file mode 100644 index 40245fca..00000000 --- a/assets/icons/like.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/search-bar-arrow.svg b/assets/icons/search-bar-arrow.svg deleted file mode 100644 index a26e8eb0..00000000 --- a/assets/icons/search-bar-arrow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/icons/star-active.svg b/assets/icons/star-active.svg deleted file mode 100644 index c8483b60..00000000 --- a/assets/icons/star-active.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/star-inactive.svg b/assets/icons/star-inactive.svg deleted file mode 100644 index 6d02364b..00000000 --- a/assets/icons/star-inactive.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/components/BookmarkButton/BookmarkButton.tsx b/src/components/BookmarkButton/BookmarkButton.tsx index 7e2e1389..375e6669 100644 --- a/src/components/BookmarkButton/BookmarkButton.tsx +++ b/src/components/BookmarkButton/BookmarkButton.tsx @@ -3,8 +3,8 @@ import React from 'react'; import {Button, Icon} from '@gravity-ui/uikit'; import block from 'bem-cn-lite'; -import StarActive from '../../../assets/icons/star-active.svg'; -import StarInactive from '../../../assets/icons/star-inactive.svg'; +import StarActive from '@gravity-ui/icons/svgs/star-fill.svg'; +import StarInactive from '@gravity-ui/icons/svgs/star.svg'; import './BookmarkButton.scss'; diff --git a/src/components/Controls/Controls.tsx b/src/components/Controls/Controls.tsx index b48078e8..a759e33d 100644 --- a/src/components/Controls/Controls.tsx +++ b/src/components/Controls/Controls.tsx @@ -14,10 +14,10 @@ import { PdfControl, } from './'; -import EditIcon from '../../../assets/icons/edit.svg'; import {PopperPosition} from '../../hooks'; import {Lang, TextSizes, Theme, FeedbackSendData, ControlSizes, SubscribeData} from '../../models'; +import EditIcon from '@gravity-ui/icons/svgs/pencil.svg'; import './Controls.scss'; diff --git a/src/components/Controls/single-controls/FullScreenControl.tsx b/src/components/Controls/single-controls/FullScreenControl.tsx index 542a4336..ae506e48 100644 --- a/src/components/Controls/single-controls/FullScreenControl.tsx +++ b/src/components/Controls/single-controls/FullScreenControl.tsx @@ -6,8 +6,8 @@ import {Control} from '../../Control'; import {ControlSizes, Lang} from '../../../models'; import FullScreenClickedIcon from '../../../../assets/icons/full-screen-clicked.svg'; -import FullScreenIcon from '../../../../assets/icons/full-screen.svg'; import {PopperPosition} from '../../../hooks'; +import FullScreenIcon from '@gravity-ui/icons/svgs/square-dashed.svg'; interface ControlProps { lang: Lang; diff --git a/src/components/Controls/single-controls/LangControl.tsx b/src/components/Controls/single-controls/LangControl.tsx index 22eb1c00..8fe65c8a 100644 --- a/src/components/Controls/single-controls/LangControl.tsx +++ b/src/components/Controls/single-controls/LangControl.tsx @@ -9,7 +9,7 @@ import {ControlSizes, Lang} from '../../../models'; import {getPopupPosition} from './utils'; import {PopperPosition} from '../../../hooks'; -import LangIcon from '../../../../assets/icons/lang.svg'; +import LangIcon from '@gravity-ui/icons/svgs/globe.svg'; import '../Controls.scss'; diff --git a/src/components/Controls/single-controls/SettingsControl/SettingsControl.tsx b/src/components/Controls/single-controls/SettingsControl/SettingsControl.tsx index 14ac7fa9..7a04f6e3 100644 --- a/src/components/Controls/single-controls/SettingsControl/SettingsControl.tsx +++ b/src/components/Controls/single-controls/SettingsControl/SettingsControl.tsx @@ -7,10 +7,10 @@ import {Control} from '../../../Control'; import {ControlSizes, Lang, TextSizes, Theme} from '../../../../models'; import {PopperPosition} from '../../../../hooks'; -import SettingsIcon from '../../../../../assets/icons/cog.svg'; - import {getPopupPosition} from '../utils'; +import SettingsIcon from '@gravity-ui/icons/svgs/gear.svg'; + import './SettingsControl.scss'; const ITEM_HEIGHT = 48; diff --git a/src/components/EditButton/EditButton.tsx b/src/components/EditButton/EditButton.tsx index 8fb77b0e..0d8e188a 100644 --- a/src/components/EditButton/EditButton.tsx +++ b/src/components/EditButton/EditButton.tsx @@ -3,9 +3,9 @@ import block from 'bem-cn-lite'; import {withTranslation, WithTranslation, WithTranslationProps} from 'react-i18next'; import {Button, Icon as IconComponent} from '@gravity-ui/uikit'; -import EditIcon from '../../../assets/icons/edit.svg'; import {Lang} from '../../models'; +import EditIcon from '@gravity-ui/icons/svgs/pencil.svg'; import './EditButton.scss'; diff --git a/src/components/Feedback/Feedback.tsx b/src/components/Feedback/Feedback.tsx index 1d807d29..08ef49f3 100644 --- a/src/components/Feedback/Feedback.tsx +++ b/src/components/Feedback/Feedback.tsx @@ -8,8 +8,8 @@ import {PopperPosition} from '../../hooks'; import {FeedbackSendData, FeedbackType, Lang} from '../../models'; import {DISLIKE_VARIANTS} from '../../constants'; -import LikeIcon from '../../../assets/icons/like.svg'; -import DislikeIcon from '../../../assets/icons/dislike.svg'; +import DislikeActiveIcon from '@gravity-ui/icons/svgs/thumbs-down-fill.svg'; +import DislikeIcon from '@gravity-ui/icons/svgs/thumbs-down.svg'; import './Feedback.scss'; diff --git a/src/components/Paginator/Paginator.tsx b/src/components/Paginator/Paginator.tsx index 02393b75..3312f0b7 100644 --- a/src/components/Paginator/Paginator.tsx +++ b/src/components/Paginator/Paginator.tsx @@ -1,9 +1,9 @@ import React, {ReactNode} from 'react'; -import block from 'bem-cn-lite'; import {Button, Icon} from '@gravity-ui/uikit'; +import block from 'bem-cn-lite'; -import arrowIcon from '../../../assets/icons/arrow-right.svg'; +import ArrowRightIcon from '@gravity-ui/icons/svgs/arrow-right.svg'; import './Paginator.scss'; @@ -142,7 +142,7 @@ const Paginator = ({ const arrowButton = (disable: boolean) => ( ); diff --git a/src/components/SearchBar/SearchBar.tsx b/src/components/SearchBar/SearchBar.tsx index 5fa8881c..81d660d5 100644 --- a/src/components/SearchBar/SearchBar.tsx +++ b/src/components/SearchBar/SearchBar.tsx @@ -8,8 +8,8 @@ import {useHotkeys} from 'react-hotkeys-hook'; import {Control} from '../Control'; import {Lang} from '../../models'; -import CloseIcon from '../../../assets/icons/close.svg'; -import ArrowIcon from '../../../assets/icons/search-bar-arrow.svg'; +import ArrowLeftIcon from '@gravity-ui/icons/svgs/chevron-left.svg'; +import CloseIcon from '@gravity-ui/icons/svgs/xmark.svg'; import './SearchBar.scss'; diff --git a/src/components/TocNavPanel/TocNavPanel.tsx b/src/components/TocNavPanel/TocNavPanel.tsx index 4007b9d6..eddf89c9 100644 --- a/src/components/TocNavPanel/TocNavPanel.tsx +++ b/src/components/TocNavPanel/TocNavPanel.tsx @@ -5,10 +5,10 @@ import {withTranslation, WithTranslation, WithTranslationProps} from 'react-i18n import {TocData, TocItem, Router, Lang} from '../../models'; -import ArrowLeft from '../../../assets/icons/arrow-left.svg'; -import ArrowRight from '../../../assets/icons/arrow-right.svg'; import {isExternalHref, isActiveItem} from '../../utils'; +import ArrowLeft from '@gravity-ui/icons/svgs/arrow-left.svg'; +import ArrowRight from '@gravity-ui/icons/svgs/arrow-right.svg'; import './TocNavPanel.scss'; diff --git a/src/components/ToggleArrow/ToggleArrow.tsx b/src/components/ToggleArrow/ToggleArrow.tsx index dda22afa..0749bba2 100644 --- a/src/components/ToggleArrow/ToggleArrow.tsx +++ b/src/components/ToggleArrow/ToggleArrow.tsx @@ -1,7 +1,7 @@ import React from 'react'; import block from 'bem-cn-lite'; -import ChevronIcon from '../../../assets/icons/chevron.svg'; +import ChevronIcon from '@gravity-ui/icons/svgs/chevron-right.svg'; import './ToggleArrow.scss'; From fac39ab0ca16a5a64c2763217ff97a2afe998b9d Mon Sep 17 00:00:00 2001 From: 3y3 <3y3@ya.ru> Date: Tue, 5 Sep 2023 21:48:11 +0300 Subject: [PATCH 08/52] feat!: Rebuild project with esbuild --- esbuild/FileWatcher.js | 28 +++ esbuild/SparsedBuild.js | 170 +++++++++++++++ esbuild/build.js | 63 ++++++ esbuild/use-from-source-plugin.js | 30 +++ package.json | 48 +++-- src/components/Controls/Controls.tsx | 2 - .../single-controls/FullScreenControl.tsx | 1 - styles/default.scss | 38 ---- styles/mixins.scss | 114 ---------- styles/themes.scss | 7 - styles/typography.scss | 62 ------ styles/variables.scss | 18 -- styles/yfm.scss | 202 ------------------ tsconfig.cjs.json | 9 + tsconfig.esm.json | 9 + tsconfig.json | 1 - tsconfig.publish.json | 7 - 17 files changed, 345 insertions(+), 464 deletions(-) create mode 100644 esbuild/FileWatcher.js create mode 100644 esbuild/SparsedBuild.js create mode 100755 esbuild/build.js create mode 100644 esbuild/use-from-source-plugin.js delete mode 100644 styles/default.scss delete mode 100644 styles/mixins.scss delete mode 100644 styles/themes.scss delete mode 100644 styles/typography.scss delete mode 100644 styles/variables.scss delete mode 100644 styles/yfm.scss create mode 100644 tsconfig.cjs.json create mode 100644 tsconfig.esm.json delete mode 100644 tsconfig.publish.json diff --git a/esbuild/FileWatcher.js b/esbuild/FileWatcher.js new file mode 100644 index 00000000..1ab3faea --- /dev/null +++ b/esbuild/FileWatcher.js @@ -0,0 +1,28 @@ +const {watch} = require('node:fs'); + +class FileWatcher { + constructor(watching) { + this.watching = watching; + } + + async watch(files, handle, dispose) { + files.forEach((file) => { + if (this.watching) { + watch(file, (event) => { + if (event === 'change') { + // eslint-disable-next-line no-console + handle(file).catch(console.error); + } else { + dispose(file); + } + }); + } else { + dispose(file); + } + }); + } +} + +module.exports = { + FileWatcher, +}; diff --git a/esbuild/SparsedBuild.js b/esbuild/SparsedBuild.js new file mode 100644 index 00000000..120b0e3c --- /dev/null +++ b/esbuild/SparsedBuild.js @@ -0,0 +1,170 @@ +const assert = require('node:assert'); +const {statSync, existsSync} = require('node:fs'); +const {join, resolve} = require('node:path'); + +class SparsedBuild { + constructor(context, {extension} = {}) { + extension = Object.assign( + { + '.ts': '.js', + '.tsx': '.js', + '.json': '.js', + }, + extension, + ); + + this._context = context; + + this._files = new Map(); + + this.plugin = { + name: 'SparsedBuild', + setup: ({onResolve, initialOptions}) => { + const { + bundle, + resolveExtensions = ['.tsx', '.ts', '.jsx', '.js', '.css', '.json'], + } = initialOptions; + + assert(bundle === true, `Option 'bundle' should be 'true' for sparsed build`); + + onResolve({filter: /.*/}, async ({path, resolveDir, kind}) => { + if (kind === 'entry-point') { + return {}; + } + + if (!path.match(/^\.{1,2}/)) { + return {external: true}; + } + + const fullpath = resolveFile( + resolve(resolveDir, path), + resolveExtensions, + true, + ); + + if (!this._has(fullpath)) { + await this.build(fullpath); + } + + return { + path: replaceExt(path, extension), + external: true, + }; + }); + }, + }; + } + + get files() { + return [...this._files.keys()]; + } + + build = async (file) => { + const state = await this._add(file); + + if (state.build) { + state.queued = true; + } else { + state.queued = false; + // eslint-disable-next-line consistent-return + state.build = state.context.rebuild().then(() => { + state.build = null; + + if (state.queued) { + return this.build(file); + } + }); + } + + return state.build; + }; + + dispose = (file) => { + const state = this._get(file); + + if (state) { + state.build = null; + state.queued = false; + state.context.dispose(); + this._delete(file); + } + }; + + async _add(file) { + if (this._has(file)) { + return this._get(file); + } + + const state = { + build: null, + queued: false, + context: await this._context(file), + }; + + this._files.set(file, state); + + return state; + } + + _has(file) { + return this._files.has(file); + } + + _get(file) { + return this._files.get(file); + } + + _delete(file) { + this._files.delete(file); + } +} + +function resolveFile(path, exts, strict) { + const isFileExists = existsSync(path); + const isLikeFile = exts.some((ext) => path.endsWith(ext)); + + if (isFileExists) { + const stat = statSync(path); + + if (stat.isFile()) { + return path; + } else if (!isLikeFile && stat.isDirectory()) { + for (const ext of exts) { + const file = resolveFile(join(path, 'index' + ext), exts); + if (file) { + return file; + } + } + } + } else if (!isLikeFile) { + for (const ext of exts) { + const file = resolveFile(path + ext, exts); + if (file) { + return file; + } + } + } + + if (strict) { + // throw ENOENT + return statSync(path); + } else { + return null; + } +} + +function replaceExt(path, extensions) { + for (const [from, to] of Object.entries(extensions)) { + if (path.endsWith(from)) { + const parts = path.split(from).slice(0, -1); + + return parts.concat(parts.pop() + to).join(from); + } + } + + return path; +} + +module.exports = { + SparsedBuild, +}; diff --git a/esbuild/build.js b/esbuild/build.js new file mode 100755 index 00000000..209c7461 --- /dev/null +++ b/esbuild/build.js @@ -0,0 +1,63 @@ +#!/usr/bin/env node + +const autoprefixer = require('autoprefixer'); +const esbuild = require('esbuild'); +const {sassPlugin} = require('esbuild-sass-plugin'); +const {resolve} = require('node:path'); +const postcss = require('postcss'); +const postcssPresetEnv = require('postcss-preset-env'); + +const tsconfigJson = require('../tsconfig.json'); + +const {FileWatcher} = require('./FileWatcher'); +const {SparsedBuild} = require('./SparsedBuild'); +const {useFromSourcePlugin} = require('./use-from-source-plugin'); + +const { + compilerOptions: {target}, +} = tsconfigJson; + +async function build({path, format}) { + const watcher = new FileWatcher(process.argv.indexOf('--watch') > -1); + const sparsed = new SparsedBuild( + async (entry) => { + return esbuild.context({ + bundle: true, + sourcemap: true, + target: target, + tsconfig: './tsconfig.json', + entryPoints: [entry], + outbase: './src', + outdir: `./build${format ? '/' + format : ''}`, + format: format, + plugins: [ + useFromSourcePlugin(/\.svg$/), + sparsed.plugin, + sassPlugin({ + async transform(source) { + const {css} = await postcss([ + autoprefixer({cascade: false}), + postcssPresetEnv({stage: 0}), + ]).process(source, {from: undefined}); + + return css; + }, + }), + ].filter(Boolean), + }); + }, + { + extension: { + '.scss': '.css', + }, + }, + ); + + await sparsed.build(resolve(path)); + await watcher.watch(sparsed.files, sparsed.build, sparsed.dispose); +} + +build({path: 'src/index.ts', format: 'cjs'}); +build({path: 'src/index.ts', format: 'esm'}); +build({path: 'src/index.scss'}); +build({path: 'src/themes/common/index.scss'}); diff --git a/esbuild/use-from-source-plugin.js b/esbuild/use-from-source-plugin.js new file mode 100644 index 00000000..0090ed0c --- /dev/null +++ b/esbuild/use-from-source-plugin.js @@ -0,0 +1,30 @@ +const {dirname, relative, resolve} = require('node:path'); + +function useFromSourcePlugin(match) { + return { + name: 'useFromSource', + setup(build) { + const {outfile} = build.initialOptions; + let {outdir, outbase = '.'} = build.initialOptions; + outdir = resolve(outdir || dirname(outfile)); + outbase = resolve(outbase); + + build.onResolve({filter: match}, ({path, resolveDir, importer}) => { + if (!path.match(/^\.{1,2}/)) { + return {external: true}; + } + + const outpath = resolve(outdir, relative(outbase, dirname(importer))); + + return { + external: true, + path: relative(outpath, resolve(resolveDir, path)), + }; + }); + }, + }; +} + +module.exports = { + useFromSourcePlugin, +}; diff --git a/package.json b/package.json index bc2ff45d..464778d9 100644 --- a/package.json +++ b/package.json @@ -13,15 +13,33 @@ "build", "styles" ], + "exports": { + ".": { + "import": { + "types": "./build/esm/index.d.js", + "style": "./build/index.css", + "default": "./build/esm/index.js" + }, + "require": { + "types": "./build/cjs/index.d.js", + "style": "./build/index.css", + "default": "./build/cjs/index.js" + }, + "default": { + "types": "./build/esm/index.d.js", + "style": "./build/index.css", + "default": "./build/esm/index.js" + } + }, + "./assets/icons/*.svg": "./assets/icons/*.svg", + "./styles": "./build/index.css" + }, "sideEffects": [ - "./build/index.js", - "./build/i18n/index.js", - "./src/index.ts", - "./src/i18n/index.ts", + "*.css", "*.scss" ], - "main": "build/index.js", - "typings": "build/index.d.ts", + "main": "build/esm/index.js", + "typings": "build/esm/index.d.ts", "scripts": { "deps:install": "npm ci", "deps:truncate": "npm prune --production", @@ -35,10 +53,15 @@ "_lint:styles:fix": "npm run _lint:styles -- --fix", "typecheck": "tsc --noEmit", "test": "exit 0", + "dev": "run-s build:clean build:compile _dev:watch", + "_dev:watch": "run-p _build:watch _storybook:watch", "build:clean": "rimraf build", - "build:copy": "copyfiles -u 1 \"src/components/**/*.scss\" \"src/components/**/*.svg\" build", - "build:compile": "tsc -p tsconfig.publish.json", - "build": "npm run build:clean && npm run build:compile && npm run build:copy", + "build:compile": "./esbuild/build.js", + "_build:declarations:esm": "tsc --emitDeclarationOnly -p tsconfig.esm.json", + "_build:declarations:cjs": "tsc --emitDeclarationOnly -p tsconfig.cjs.json", + "build": "run-s build:clean build:compile _build:declarations:*", + "_build:watch": "./esbuild/build.js --watch", + "_storybook:watch": "cd demo && start-storybook -p 7008", "prepublishOnly": "npm run lint && npm run test && npm run build", "prepare": "husky install", "pre-commit": "lint-staged" @@ -72,14 +95,15 @@ "@types/lodash": "4.14.179", "@types/react": "^18.2.21", "@types/react-dom": "^18.2.7", - "copyfiles": "2.4.1", - "node-sass": "4.14.1", - "npm-run-all": "4.1.5", "autoprefixer": "^10.4.15", + "esbuild": "^0.19.2", + "esbuild-sass-plugin": "^2.13.0", "eslint": "^8.48.0", "husky": "^8.0.3", "lint-staged": "^14.0.1", + "npm-run-all": "^4.1.5", "postcss": "^8.4.28", + "postcss-preset-env": "^9.1.2", "prettier": "^2.8.8", "prop-types": "^15.8.1", "react": "^18.2.0", diff --git a/src/components/Controls/Controls.tsx b/src/components/Controls/Controls.tsx index a759e33d..6d350b6e 100644 --- a/src/components/Controls/Controls.tsx +++ b/src/components/Controls/Controls.tsx @@ -14,11 +14,9 @@ import { PdfControl, } from './'; - import {PopperPosition} from '../../hooks'; import {Lang, TextSizes, Theme, FeedbackSendData, ControlSizes, SubscribeData} from '../../models'; import EditIcon from '@gravity-ui/icons/svgs/pencil.svg'; - import './Controls.scss'; const b = block('dc-controls'); diff --git a/src/components/Controls/single-controls/FullScreenControl.tsx b/src/components/Controls/single-controls/FullScreenControl.tsx index ae506e48..df913e4b 100644 --- a/src/components/Controls/single-controls/FullScreenControl.tsx +++ b/src/components/Controls/single-controls/FullScreenControl.tsx @@ -6,7 +6,6 @@ import {Control} from '../../Control'; import {ControlSizes, Lang} from '../../../models'; import FullScreenClickedIcon from '../../../../assets/icons/full-screen-clicked.svg'; -import {PopperPosition} from '../../../hooks'; import FullScreenIcon from '@gravity-ui/icons/svgs/square-dashed.svg'; interface ControlProps { diff --git a/styles/default.scss b/styles/default.scss deleted file mode 100644 index f856f1d2..00000000 --- a/styles/default.scss +++ /dev/null @@ -1,38 +0,0 @@ -*, -*::before, -*::after { - box-sizing: inherit; -} - -html, -body { - margin: 0; - padding: 0; - box-sizing: border-box; - - --dc-header-height: 0; - --dc-subheader-height: 40px; - - --dc-error-image-403: url('data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='); - --dc-error-image-404: url('data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='); - --dc-error-image-500: url('data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='); - - .yc-root { - --g-scrollbar-width: 6px; - - &::-webkit-scrollbar, - *::-webkit-scrollbar { - height: var(--g-scrollbar-width); - background: transparent; - } - - &::-webkit-scrollbar-track, - *::-webkit-scrollbar-track { - background: transparent; - } - } -} - -#root { - min-height: 100vh; -} diff --git a/styles/mixins.scss b/styles/mixins.scss deleted file mode 100644 index d57000aa..00000000 --- a/styles/mixins.scss +++ /dev/null @@ -1,114 +0,0 @@ -@import './variables'; - -@mixin text-size($name) { - font-size: var(--g-text-#{$name}-font-size); - line-height: var(--g-text-#{$name}-line-height); -} - -@mixin reset-list-style() { - margin: 0; - padding: 0; - list-style: none; -} - -@mixin fixAnchorPosition($offset: 0px) { - padding-top: calc(var(--dc-header-height, #{$headerHeight})); - margin-top: calc(#{$offset} - var(--dc-header-height, #{$headerHeight})); -} - -@mixin reset-link-style() { - color: inherit; - text-decoration: none; -} - -@mixin islands-focus() { - outline: none; - - .utilityfocus &:focus { - outline: 2px solid #ffdb4d; - } -} - -@mixin link() { - @include islands-focus(); - - color: var(--g-color-text-link); - text-decoration: none; - cursor: pointer; - - &:hover, - &:active { - color: var(--g-color-text-link-hover); - } -} - -@mixin heading1() { - @include text-size(display-3); - font-weight: 500; - - @media (max-width: map-get($screenBreakpoints, 'md') - 1) { - @include text-size(display-2); - } -} - -@mixin heading2() { - @include text-size(display-1); - font-weight: 500; -} - -@mixin heading3() { - @include text-size(header-2); - font-weight: 500; -} - -@mixin heading4() { - @include text-size(header-1); - font-weight: 500; -} - -@mixin heading5() { - @include text-size(body-3); - font-weight: 500; -} - -@mixin heading6() { - @include text-size(body-3); - font-weight: 400; -} - -@mixin contributors-text() { - font-size: 13px; - font-weight: 400; - margin-right: 5px; - align-self: center; -} - -@mixin text-body-1() { - font-size: var(--g-text-body-1-font-size); - line-height: var(--g-text-body-1-line-height); - font-weight: var(--g-text-body-font-weight); -} - -@mixin desktop-only() { - @media (max-width: map-get($screenBreakpoints, 'md') - 1) { - display: none; - } -} - -@mixin desktop-tablet-only() { - @media (max-width: map-get($screenBreakpoints, 'sm') - 1) { - display: none; - } -} - -@mixin mobile-tablet-only() { - @media (min-width: map-get($screenBreakpoints, 'md')) { - display: none; - } -} - -@mixin mobile-only() { - @media (min-width: map-get($screenBreakpoints, 'sm')) { - display: none; - } -} diff --git a/styles/themes.scss b/styles/themes.scss deleted file mode 100644 index 25260e46..00000000 --- a/styles/themes.scss +++ /dev/null @@ -1,7 +0,0 @@ -@import '~@gravity-ui/uikit/styles/styles.scss'; -@import './yfm.scss'; - -.yc-root { - --dc-text-highlight: var(--g-color-base-warning-heavy); - --dc-text-highlight-selected: #ffab3b; -} diff --git a/styles/typography.scss b/styles/typography.scss deleted file mode 100644 index 9d5b1c4c..00000000 --- a/styles/typography.scss +++ /dev/null @@ -1,62 +0,0 @@ -@import './variables'; -@import './mixins'; - -body.yc-root { - font-feature-settings: 'liga', 'kern'; - text-size-adjust: 100%; - - /* stylelint-disable-next-line value-keyword-case */ - text-rendering: optimizeLegibility; -} - -h1, -h2, -h3, -h4, -h5, -h6 { - margin: 0; -} - -h1 { - @include heading1(); -} - -h2 { - @include heading2(); -} - -h3 { - @include heading3(); -} - -h4 { - @include heading4(); -} - -h5 { - @include heading5(); -} - -h6 { - @include heading6(); -} - -p, -div.p { - margin: 0 0 20px; - - &:last-child { - margin-bottom: 0; - } -} - -sub, -sup { - font-size: 0.75em; - line-height: 0; -} - -a { - @include link(); -} diff --git a/styles/variables.scss b/styles/variables.scss deleted file mode 100644 index c071e8e0..00000000 --- a/styles/variables.scss +++ /dev/null @@ -1,18 +0,0 @@ -// Sizes -$headerHeight: 0px; -$normalOffset: 20px; -$miniTocOffset: 56px; -$blockMarginBottomLarge: 20px; -$blockMarginBottomMedium: 15px; -$headerMarginTopLarge: 40px; -$headerMarginTopMedium: 32px; -$popupPadding: 16px; -$centerBlockMaxWidth: 736px; - -$screenBreakpoints: ( - 'xs': 375px, - 'sm': 577px, - 'md': 769px, - 'lg': 1081px, - 'xl': 1185px, -); diff --git a/styles/yfm.scss b/styles/yfm.scss deleted file mode 100644 index 09232e08..00000000 --- a/styles/yfm.scss +++ /dev/null @@ -1,202 +0,0 @@ -@import './mixins'; -@import './variables'; - -.dc-doc-page .yfm { - color: var(--g-color-text-primary); - - --yfm-color-hljs-background: var(--g-color-base-background); - --yfm-color-hljs-subst: var(--g-color-text-complementary); - --yfm-color-hljs-comment: var(--g-color-text-secondary); - --yfm-color-hljs-deletion: var(--g-color-line-danger); - --yfm-color-hljs-section: var(--g-color-line-danger); - - a { - color: var(--g-color-text-link); - - &:hover, - &:active { - color: var(--g-color-text-link-hover); - } - } - - img { - background-color: var(--g-color-base-background); - } - - $backgroundColors: ( - yfm-accent-info: var(--g-color-base-info-light), - yfm-accent-tip: var(--g-color-base-positive-light), - yfm-accent-alert: var(--g-color-base-danger-light), - yfm-accent-warning: var(--g-color-base-warning-light), - ); - - @each $type, $color in $backgroundColors { - &.#{$type} { - background: $color; - } - } - - code { - font-size: var(--dc-code-short-font-size); - line-height: var(--dc-code-short-line-height); - padding: var(--dc-code-short-padding); - background: var(--g-color-base-misc-light); - color: var(--g-color-text-misc); - } - - pre > code { - font-size: var(--dc-code-font-size); - line-height: var(--dc-code-line-height); - padding: 16px; - background: var(--g-color-base-misc-light); - color: var(--g-color-text-complementary); - } - - dfn { - background: var(--g-color-base-float); - box-shadow: 0 8px 20px var(--g-color-sfx-shadow); - } - - dfn:before { - box-shadow: 0 0 0 1px var(--g-color-line-generic-solid); - } - - table { - color: var(--g-color-text-primary); - border-color: var(--g-color-line-generic); - background: var(--g-color-base-background); - } - - table thead, - table tr:nth-child(2n) { - background: var(--g-color-base-generic); - } - - hr { - background-color: var(--g-color-base-background); - } - - blockquote { - border-left-color: var(--g-color-base-brand); - } - - .yfm-tab-list { - border-bottom-color: var(--g-color-line-generic); - } - - .yfm-tab:hover, - .yfm-tab:active { - color: var(--g-color-text-link-hover); - } - - .yfm-tab.active { - border-bottom-color: var(--g-color-base-brand); - } - - /* TODO: move yfm-cut styles to yfm-transform */ - .yfm-cut { - margin-bottom: $blockMarginBottomMedium; - } - - .yfm-cut.open .yfm-cut-content { - padding: 5px 0 0 30px; - } - - .yfm-term_title:hover { - color: var(--g-color-text-link-hover); - } - - // Margin collapse is not applied for a block and header with a negative margin. - // The margin is summed up. So, we need to take 15px away from the header margin-top - h1, - h2 { - @include fixAnchorPosition(calc(#{$headerMarginTopLarge} - #{$blockMarginBottomMedium})); - margin-bottom: $blockMarginBottomLarge; - } - - h3, - h4, - h5, - h6 { - @include fixAnchorPosition(calc(#{$headerMarginTopMedium} - #{$blockMarginBottomMedium})); - } - - h1 + h2, - h2 + h3, - h3 + h4, - h4 + h5, - h5 + h6 { - @include fixAnchorPosition(); - } - - // Use 0px for the top offset of the header if it is the first child element, - // because it is located after the page title - h1, - h2, - h3, - h4, - h5, - h6 { - &:first-child { - @include fixAnchorPosition(); - } - } - - .yfm-page__delimeter { - height: 4px; - border-radius: 2px; - background: var(--g-color-line-generic); - margin-top: 20px; - margin-bottom: 0; - - & + h1, - & + h2, - & + h3, - & + h4, - & + h5, - & + h6 { - @include fixAnchorPosition(#{$headerMarginTopLarge}); - } - } - - .yfm-original-link { - &:before { - content: none; - } - & svg { - visibility: hidden; - } - } - - h1, - h2, - h3, - h4, - h5, - h6 { - .yfm-original-link::before { - content: none; - } - .yfm-original-link svg { - visibility: hidden; - } - &:hover .yfm-original-link svg { - visibility: visible; - } - } - - /* stylelint-disable declaration-no-important */ - .yfm-anchor_hidden { - visibility: hidden; - &:before { - visibility: hidden !important; - } - } - /* stylelint-enable declaration-no-important */ -} - -.yc-root_theme_dark .dc-doc-page .yfm { - .yfm-cut-title:before { - background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+PHBhdGggc3Ryb2tlPSJyZ2JhKDI1NSwgMjU1LCAyNTUsIDAuNykiIGZpbGw9Im5vbmUiIGQ9Ik0zIDZsNSA1IDUtNSI+PC9wYXRoPjwvc3ZnPg=='); - } -} diff --git a/tsconfig.cjs.json b/tsconfig.cjs.json new file mode 100644 index 00000000..365e3e7d --- /dev/null +++ b/tsconfig.cjs.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "CommonJS", + "target": "ES2017", + "outDir": "build/cjs", + }, + "include": ["src/**/*.ts", "src/**/*.tsx"] +} diff --git a/tsconfig.esm.json b/tsconfig.esm.json new file mode 100644 index 00000000..b6d0894c --- /dev/null +++ b/tsconfig.esm.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "ESNext", + "target": "ES2017", + "outDir": "build/cjs", + }, + "include": ["src/**/*.ts", "src/**/*.tsx"] +} diff --git a/tsconfig.json b/tsconfig.json index 5f0c9940..8bbafc06 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,6 @@ "resolveJsonModule": true, "declaration": true, "jsx": "react", - "outDir": "build", "target": "ESNext", "baseUrl": "." }, diff --git a/tsconfig.publish.json b/tsconfig.publish.json deleted file mode 100644 index 8bbb0e6d..00000000 --- a/tsconfig.publish.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig", - "compilerOptions": { - "paths": {} - }, - "exclude": ["src/stories", "src/**/*.test.ts", "src/**/*.test.tsx"] -} From a05549f71a6800d2f2e96b39c4cfa3e2831eee9a Mon Sep 17 00:00:00 2001 From: 3y3 <3y3@ya.ru> Date: Tue, 5 Sep 2023 21:55:27 +0300 Subject: [PATCH 09/52] feat!: Remove direct dependency on @doc-tools/transform --- package.json | 4 +--- src/components/DocPage/DocPage.tsx | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/package.json b/package.json index 464778d9..f90a6542 100644 --- a/package.json +++ b/package.json @@ -82,11 +82,9 @@ }, "peerDependencies": { "react": ">=16.8.0 || >=17.0.0 || >=18.0.0", - "react-dom": ">=16.8.0 || >=17.0.0 || >=18.0.0", - "@doc-tools/transform": "^3.10.2" + "react-dom": ">=16.8.0 || >=17.0.0 || >=18.0.0" }, "devDependencies": { - "@doc-tools/transform": "^3.10.2", "@gravity-ui/eslint-config": "^2.2.0", "@gravity-ui/prettier-config": "^1.0.1", "@gravity-ui/stylelint-config": "^3.0.0", diff --git a/src/components/DocPage/DocPage.tsx b/src/components/DocPage/DocPage.tsx index fd6f4e55..54865ed5 100644 --- a/src/components/DocPage/DocPage.tsx +++ b/src/components/DocPage/DocPage.tsx @@ -1,7 +1,6 @@ import React from 'react'; import block from 'bem-cn-lite'; -import '@doc-tools/transform/dist/js/yfm'; import ReactDOMServer from 'react-dom/server'; import {DEFAULT_SETTINGS} from '../../constants'; @@ -39,7 +38,6 @@ import {TocNavPanel} from '../TocNavPanel'; import LinkIcon from '../../../assets/icons/link.svg'; -import '@doc-tools/transform/dist/css/yfm.css'; import './DocPage.scss'; const b = block('dc-doc-page'); From 7dcf5d7c4aba908b2070dc1d7e64842ffc73f127 Mon Sep 17 00:00:00 2001 From: 3y3 <3y3@ya.ru> Date: Thu, 7 Sep 2023 17:11:14 +0300 Subject: [PATCH 10/52] feat: Add `configure` util --- .eslintrc | 4 ---- .eslintrc.js | 15 +++++++++++++ .eslintrc.publish.js | 4 ++++ src/config/i18n.ts | 44 +++++++++++++++++++++++++++++++++++++ src/config/index.ts | 34 ++++++++++++++++++++++++++++ src/hooks/index.ts | 1 + src/hooks/useTranslation.ts | 8 +++++++ 7 files changed, 106 insertions(+), 4 deletions(-) delete mode 100644 .eslintrc create mode 100644 .eslintrc.js create mode 100644 .eslintrc.publish.js create mode 100644 src/config/i18n.ts create mode 100644 src/config/index.ts create mode 100644 src/hooks/useTranslation.ts diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index b4c98570..00000000 --- a/.eslintrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": ["@gravity-ui/eslint-config"], - "root": true -} diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..bc9579de --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,15 @@ +module.exports = { + extends: ['@gravity-ui/eslint-config'], + root: true, + rules: { + 'no-param-reassign': 'off', + }, + overrides: [ + { + files: ['!src/**/*', '!demo/**/*'], + env: { + node: true, + }, + }, + ], +}; diff --git a/.eslintrc.publish.js b/.eslintrc.publish.js new file mode 100644 index 00000000..e54537de --- /dev/null +++ b/.eslintrc.publish.js @@ -0,0 +1,4 @@ +module.exports = { + extends: ['@gravity-ui/eslint-config/prettier', './.eslintrc.js'], + root: true, +}; diff --git a/src/config/i18n.ts b/src/config/i18n.ts new file mode 100644 index 00000000..4f04a66d --- /dev/null +++ b/src/config/i18n.ts @@ -0,0 +1,44 @@ +import i18n, {TFunction} from 'i18next'; +import {initReactI18next} from 'react-i18next'; + +import en from '../i18n/en.json'; +import ru from '../i18n/ru.json'; +import {Lang} from '../models'; + +export type Loc = Record; + +export interface I18NConfig { + lang?: string; + loc?: Loc; +} + +let initializePromise: Promise | null = null; + +export const configureI18N = ({lang, loc}: I18NConfig) => { + if (initializePromise === null) { + lang = lang || Lang.En; + loc = loc || {ru, en}; + + initializePromise = i18n.use(initReactI18next).init({ + lng: lang, + fallbackLng: lang, + ns: Object.keys(loc[lang as Lang]), + resources: loc, + interpolation: { + escapeValue: false, + }, + }); + } else { + if (lang && lang !== i18n.language) { + i18n.changeLanguage(lang); + } + + if (loc) { + for (const [lng, namespaces] of Object.entries(loc)) { + for (const [ns, resources] of Object.entries(namespaces as Loc[Lang])) { + i18n.addResources(lng, ns, resources); + } + } + } + } +}; diff --git a/src/config/index.ts b/src/config/index.ts new file mode 100644 index 00000000..c3eb0a54 --- /dev/null +++ b/src/config/index.ts @@ -0,0 +1,34 @@ +import {Config, Lang} from '../models'; + +import {configureI18N} from './i18n'; + +type Subscriber = (config: Config) => void; + +const subs: Set = new Set(); + +let config: Config = { + lang: Lang.En, +}; + +export const configure = (newConfig: Partial = {}) => { + config = Object.assign({}, config, newConfig); + + configureI18N({ + lang: config.lang, + loc: config.loc, + }); + + subs.forEach((sub) => { + sub(config); + }); +}; + +export const subscribeConfigure = (sub: Subscriber) => { + subs.add(sub); + + return () => { + subs.delete(sub); + }; +}; + +export const getConfig = () => config; diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 2667adf3..10e96b6b 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -1,2 +1,3 @@ export * from './useForkRef'; export * from './usePopper'; +export * from './useTranslation'; diff --git a/src/hooks/useTranslation.ts b/src/hooks/useTranslation.ts new file mode 100644 index 00000000..ce69ede0 --- /dev/null +++ b/src/hooks/useTranslation.ts @@ -0,0 +1,8 @@ +import {useTranslation as useTranslationI18N} from 'react-i18next'; + +import {configure} from '../config'; + +export function useTranslation(...args: Parameters) { + configure(); + return useTranslationI18N(...args); +} From 778caa41026ff0235d451f4a85782f1c6debe837 Mon Sep 17 00:00:00 2001 From: 3y3 <3y3@ya.ru> Date: Mon, 11 Sep 2023 16:04:45 +0300 Subject: [PATCH 11/52] feat!: Big v3 changes --- .eslintrc.js | 19 +- V3.md | 167 ++++++ assets/icons/full-screen-clicked.svg | 3 - assets/icons/single-page-clicked.svg | 3 - assets/icons/single-page.svg | 3 - assets/icons/subscribe.svg | 4 - assets/icons/unsubscribe.svg | 4 - package.json | 25 +- .../BookmarkButton/BookmarkButton.tsx | 23 +- src/components/Breadcrumbs/Breadcrumbs.tsx | 4 +- src/components/Contributors/Contributors.tsx | 21 +- src/components/Control/Control.tsx | 54 +- src/components/Controls/Controls.tsx | 363 +++++------- src/components/Controls/ControlsLayout.tsx | 42 ++ src/components/Controls/index.ts | 1 + .../DividerControl/DividerControl.tsx | 16 +- .../Controls/single-controls/EditControl.tsx | 58 ++ .../single-controls/FullScreenControl.tsx | 47 +- .../Controls/single-controls/LangControl.tsx | 152 ++--- .../Controls/single-controls/PdfControl.tsx | 48 +- .../SettingsControl/SettingsControl.tsx | 73 +-- .../single-controls/SinglePageControl.tsx | 56 +- .../Controls/single-controls/index.ts | 1 + src/components/DocLayout/DocLayout.tsx | 8 +- .../DocLeadingPage/DocLeadingPage.tsx | 8 +- src/components/DocPage/DocPage.tsx | 127 ++-- src/components/DocPageTitle/DocPageTitle.tsx | 16 +- src/components/EditButton/EditButton.scss | 8 - src/components/EditButton/EditButton.tsx | 45 -- src/components/EditButton/index.ts | 2 - src/components/ErrorPage/ErrorPage.tsx | 23 +- src/components/Feedback/Feedback.scss | 12 - src/components/Feedback/Feedback.tsx | 543 ++++-------------- .../Feedback/controls/DislikeControl.tsx | 56 ++ .../controls/DislikeVariantsPopup.tsx | 184 ++++++ .../Feedback/controls/LikeControl.tsx | 63 ++ .../Feedback/controls/SuccessPopup.tsx | 45 ++ src/components/MiniToc/MiniToc.tsx | 110 ++-- src/components/SearchBar/SearchBar.tsx | 32 +- src/components/SearchItem/SearchItem.tsx | 148 +++-- src/components/SearchPage/SearchPage.tsx | 172 +++--- src/components/Subscribe/Subscribe.tsx | 168 +++--- .../SubscribeSuccessPopup.tsx | 39 +- .../SubscribeVariantsPopup.tsx | 47 +- src/components/Toc/Toc.tsx | 29 +- src/components/TocItem/TocItem.tsx | 2 +- src/components/TocNavPanel/TocNavPanel.tsx | 190 +++--- src/components/ToggleArrow/ToggleArrow.tsx | 5 +- src/constants.ts | 16 +- src/hooks/index.ts | 3 +- src/hooks/useForkRef.ts | 25 - src/hooks/useIsomorphicLayoutEffect.ts | 5 - src/hooks/usePopper.ts | 2 +- src/hooks/usePopupState.ts | 42 ++ src/hooks/useTimeout.ts | 22 - src/hooks/useTimer.ts | 22 + src/i18n/en.json | 6 + src/i18n/index.ts | 15 - src/i18n/ru.json | 6 + src/index.ts | 5 +- src/models/index.ts | 7 + 61 files changed, 1668 insertions(+), 1777 deletions(-) create mode 100644 V3.md delete mode 100644 assets/icons/full-screen-clicked.svg delete mode 100644 assets/icons/single-page-clicked.svg delete mode 100644 assets/icons/single-page.svg delete mode 100644 assets/icons/subscribe.svg delete mode 100644 assets/icons/unsubscribe.svg create mode 100644 src/components/Controls/ControlsLayout.tsx create mode 100644 src/components/Controls/single-controls/EditControl.tsx delete mode 100644 src/components/EditButton/EditButton.scss delete mode 100644 src/components/EditButton/EditButton.tsx delete mode 100644 src/components/EditButton/index.ts create mode 100644 src/components/Feedback/controls/DislikeControl.tsx create mode 100644 src/components/Feedback/controls/DislikeVariantsPopup.tsx create mode 100644 src/components/Feedback/controls/LikeControl.tsx create mode 100644 src/components/Feedback/controls/SuccessPopup.tsx delete mode 100644 src/hooks/useForkRef.ts delete mode 100644 src/hooks/useIsomorphicLayoutEffect.ts create mode 100644 src/hooks/usePopupState.ts delete mode 100644 src/hooks/useTimeout.ts create mode 100644 src/hooks/useTimer.ts delete mode 100644 src/i18n/index.ts diff --git a/.eslintrc.js b/.eslintrc.js index bc9579de..15cc4d55 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,8 +1,25 @@ module.exports = { - extends: ['@gravity-ui/eslint-config'], + extends: ['@gravity-ui/eslint-config', '@gravity-ui/eslint-config/prettier'], root: true, rules: { 'no-param-reassign': 'off', + 'import/order': [ + 'error', + { + alphabetize: { + order: 'asc', + }, + 'newlines-between': 'always', + groups: [['builtin', 'external'], 'internal', 'parent', 'sibling', 'index'], + warnOnUnassignedImports: true, + pathGroups: [ + { + pattern: '*.s?css$', + group: 'index', + }, + ], + }, + ], }, overrides: [ { diff --git a/V3.md b/V3.md new file mode 100644 index 00000000..0a66afd1 --- /dev/null +++ b/V3.md @@ -0,0 +1,167 @@ +## Breaking + +- Update to `@gravity-ui/uilit@5` +- Remove peer dependency on `@doc-tools/transform`
+ `@doc-tools/transform` js and css bundles should be attached directly to final projects +- `DISLIKE_VARIANTS` not exported from package. Use `i18n['feedback-variants']` instead. +- Prop `lang` war removed from component. Now you should use `configure` helper + + ```js + import {configure} from '@doc-tools/components'; + + configure({ + lang: 'ru', + }); + ``` + +### BookmarkButton + +- `bookmarkedPage` prop was changed to `isBookmarked`. (Same as `isLiked`, `isDislaked` in Feedback component) +- `onChangeBookmarkPage` renamed to `onBookmark` + +### Contributors + +- Removed `lang` prop + +### Control + +- Replace `setRef` prop with `ref` (using forwardRef now) + +### Controls + +- Removed `lang` prop +- Removed `isVerticalView`, `controlSize`, `popupPosition` prop. Configure it with `ControlsLayout` wrapper component + ```jsx + + + + ``` +- Prop `showEditControl` replaced by `hideEditControl` + +### DividerControl + +- Removed `size`, `isVerticalView` props +- Value for `size`, `isVerticalView` now stored in `ControlsLayoutContext` + +### FullScreenControl + +- Removed `lang` prop +- Removed `size`, `isVerticalView`, `className`, `popupPosition` props +- Value for `size`, `isVerticalView`, `popupPosition` now stored in `ControlsLayoutContext` +- Value for `className` now stored in `ControlsLayoutContext.controlClassName` prop +- Icon replaced with equal from `@gravity-ui/uikit` + +### LangControl + +- Prop `onChangeLang` is required now +- Removed `size`, `isVerticalView`, `className`, `popupPosition` props +- Value for `size`, `isVerticalView`, `popupPosition` now stored in `ControlsLayoutContext` +- Value for `className` now stored in `ControlsLayoutContext.controlClassName` prop +- Icon replaced with equal from `@gravity-ui/uikit` + +### PdfControl + +- Prop `pdfLink` is required now +- Removed `lang` prop +- Removed `size`, `isVerticalView`, `className`, `popupPosition` props +- Value for `size`, `isVerticalView`, `popupPosition` now stored in `ControlsLayoutContext` +- Value for `className` now stored in `ControlsLayoutContext.controlClassName` prop + +### SettingsControl + +- Removed `lang` prop +- Removed `size`, `isVerticalView`, `className`, `popupPosition` props +- Value for `size`, `isVerticalView`, `popupPosition` now stored in `ControlsLayoutContext` +- Value for `className` now stored in `ControlsLayoutContext.controlClassName` prop + +### SinglePageControl + +- Prop `onChange` is required now +- Removed `lang` prop +- Removed `size`, `isVerticalView`, `className`, `popupPosition` props +- Value for `size`, `isVerticalView`, `popupPosition` now stored in `ControlsLayoutContext` +- Value for `className` now stored in `ControlsLayoutContext.controlClassName` prop +- Icons replaced with equal from `@gravity-ui/uikit` + +### DocLayout + +- Removed `lang` prop +- + +### DocLeadingPage + +- Removed `lang` prop + +### DocPageTitle + +- Ownership on bookmarks was removed. + Bookmarks should be passed as children. +- Removed `bookmarkedPage`, `onChangeBookmarkPage` props. + +### EditButton + +- Was removed in favor of EditControl + +### ErrorPage + +- Removed `lang` prop + +### Feedback + +- Removed `lang` prop +- Removed `singlePage` prop +- Removed `size`, `isVerticalView`, `className`, `popupPosition` props +- Removed `dislikeVariants` prop. Variants should be configured via `configure` util + ```js + configure({ + loc: { + en: { + 'feedback-variants': { + variant1: 'test1', + }, + }, + ru: { + 'feedback-variants': { + variant1: 'текс1', + }, + }, + }, + }); + ``` +- Value for `size`, `isVerticalView`, `popupPosition` now stored in `ControlsLayoutContext` +- Value for `className` now stored in `ControlsLayoutContext.controlClassName` prop +- Icons replaced with equal from `@gravity-ui/uikit` + +### MiniToc + +- Removed `lang` prop +- MiniToc returns `null` on empty headings. (Previously it render useless title) + +### SearchBar + +- Removed `lang` prop +- Icons replaced with equal from `@gravity-ui/uikit` + +### SearchItem + +- Removed `lang` prop + +### SearchPage + +- Removed `lang` prop + +### Subscribe + +- Prop `onSubscribe` is required now +- Removed `lang` prop +- Removed `size`, `isVerticalView`, `className`, `popupPosition` props +- Value for `size`, `isVerticalView`, `popupPosition` now stored in `ControlsLayoutContext` +- Value for `className` now stored in `ControlsLayoutContext.controlClassName` prop + +### Toc + +- Removed `lang` prop + +### TocNavPanel + +- Removed `lang` prop diff --git a/assets/icons/full-screen-clicked.svg b/assets/icons/full-screen-clicked.svg deleted file mode 100644 index 46628f63..00000000 --- a/assets/icons/full-screen-clicked.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/single-page-clicked.svg b/assets/icons/single-page-clicked.svg deleted file mode 100644 index 2d486fa9..00000000 --- a/assets/icons/single-page-clicked.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/single-page.svg b/assets/icons/single-page.svg deleted file mode 100644 index 63a0b1c3..00000000 --- a/assets/icons/single-page.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/subscribe.svg b/assets/icons/subscribe.svg deleted file mode 100644 index c7ce627b..00000000 --- a/assets/icons/subscribe.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/assets/icons/unsubscribe.svg b/assets/icons/unsubscribe.svg deleted file mode 100644 index 3ff24e61..00000000 --- a/assets/icons/unsubscribe.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/package.json b/package.json index f90a6542..ca882a30 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,11 @@ } }, "./assets/icons/*.svg": "./assets/icons/*.svg", - "./styles": "./build/index.css" + "./styles": "./build/index.css", + "./themes/*": { + "style": "./build/themes/*/index.css", + "default": "./build/themes/*/index.css" + } }, "sideEffects": [ "*.css", @@ -45,8 +49,8 @@ "deps:truncate": "npm prune --production", "lint": "run-p _lint:js _lint:styles _lint:prettier", "lint:fix": "run-s _lint:js:fix _lint:styles:fix _lint:prettier:fix", - "_lint:js": "eslint 'src/**/*.{js,jsx,ts,tsx}'", - "_lint:js:fix": "npm run _lint:js -- --quiet --fix", + "_lint:js": "eslint '**/*.{js,jsx,ts,tsx}'", + "_lint:js:fix": "npm run _lint:js -- --fix", "_lint:prettier": "prettier --check 'src/**/*.{js,jsx,ts,tsx}'", "_lint:prettier:fix": "prettier --write 'src/**/*.{js,jsx,ts,tsx}'", "_lint:styles": "stylelint src/**/*.scss", @@ -61,7 +65,7 @@ "_build:declarations:cjs": "tsc --emitDeclarationOnly -p tsconfig.cjs.json", "build": "run-s build:clean build:compile _build:declarations:*", "_build:watch": "./esbuild/build.js --watch", - "_storybook:watch": "cd demo && start-storybook -p 7008", + "_storybook:watch": "cd demo && npm run storybook", "prepublishOnly": "npm run lint && npm run test && npm run build", "prepare": "husky install", "pre-commit": "lint-staged" @@ -78,7 +82,8 @@ "react-hotkeys-hook": "^3.3.1", "react-i18next": "11.15.6", "react-popper": "^2.2.5", - "scroll-into-view-if-needed": "2.2.29" + "scroll-into-view-if-needed": "2.2.29", + "url": "^0.11.1" }, "peerDependencies": { "react": ">=16.8.0 || >=17.0.0 || >=18.0.0", @@ -106,20 +111,22 @@ "prop-types": "^15.8.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "rimraf": "^5.0.1", + "sass": "^1.66.1", "stylelint": "^15.10.3", "svgo": "2.8.0", "typescript": "^5.2.2" }, "lint-staged": { - "src/**/*.{js,jsx,ts,tsx}": [ - "eslint --max-warnings=0 --fix -c eslint.publish.json", + "**/*.{js,jsx,ts,tsx}": [ + "eslint --max-warnings=0 --fix -c .eslintrc.publish.js", "prettier --write" ], - "src/**/*.{css,scss}": [ + "**/*.{css,scss}": [ "stylelint --fix", "prettier --write" ], - "src/**/*.{json,yaml,yml,md}": [ + "**/*.{json,yaml,yml,md}": [ "prettier --write" ], "*.svg": [ diff --git a/src/components/BookmarkButton/BookmarkButton.tsx b/src/components/BookmarkButton/BookmarkButton.tsx index 375e6669..be8045c9 100644 --- a/src/components/BookmarkButton/BookmarkButton.tsx +++ b/src/components/BookmarkButton/BookmarkButton.tsx @@ -1,10 +1,8 @@ -import React from 'react'; - -import {Button, Icon} from '@gravity-ui/uikit'; -import block from 'bem-cn-lite'; - import StarActive from '@gravity-ui/icons/svgs/star-fill.svg'; import StarInactive from '@gravity-ui/icons/svgs/star.svg'; +import {Button, Icon} from '@gravity-ui/uikit'; +import block from 'bem-cn-lite'; +import React from 'react'; import './BookmarkButton.scss'; @@ -12,24 +10,21 @@ const b = block('dc-bookmark-button'); export interface BookmarkButtonProps { className?: string; - bookmarkedPage: boolean; - onChangeBookmarkPage: (value: boolean) => void; + isBookmarked: boolean; + onBookmark: (value: boolean) => void; } -export const BookmarkButton: React.FC = ({ - bookmarkedPage, - onChangeBookmarkPage, -}) => { +export const BookmarkButton: React.FC = ({isBookmarked, onBookmark}) => { return ( ); diff --git a/src/components/Breadcrumbs/Breadcrumbs.tsx b/src/components/Breadcrumbs/Breadcrumbs.tsx index c41bc5e7..f80b23b0 100644 --- a/src/components/Breadcrumbs/Breadcrumbs.tsx +++ b/src/components/Breadcrumbs/Breadcrumbs.tsx @@ -1,6 +1,5 @@ -import React from 'react'; - import block from 'bem-cn-lite'; +import React from 'react'; import {BreadcrumbItem} from '../../models'; @@ -12,6 +11,7 @@ export interface BreadcrumbsProps { items: BreadcrumbItem[]; className?: string; } + export const Breadcrumbs: React.FC = ({items, className}) => { if (!items || !items.length) { return null; diff --git a/src/components/Contributors/Contributors.tsx b/src/components/Contributors/Contributors.tsx index f472c87c..d8dc3fda 100644 --- a/src/components/Contributors/Contributors.tsx +++ b/src/components/Contributors/Contributors.tsx @@ -1,16 +1,15 @@ -import React, {useEffect} from 'react'; import block from 'bem-cn-lite'; -import {useTranslation} from 'react-i18next'; +import React from 'react'; +import {useTranslation} from '../../hooks'; +import {Contributor} from '../../models'; import {ContributorAvatars} from '../ContributorAvatars'; -import {Lang, Contributor} from '../../models'; import './Contributors.scss'; const b = block('contributors'); export interface ContributorsProps { - lang: Lang; users: Contributor[]; onlyAuthor?: boolean; isAuthor?: boolean; @@ -18,18 +17,8 @@ export interface ContributorsProps { } const Contributors: React.FC = (props) => { - const { - users, - lang, - onlyAuthor = false, - isAuthor = false, - translationName = 'contributors', - } = props; - const {t, i18n} = useTranslation(translationName); - - useEffect(() => { - i18n.changeLanguage(lang); - }, [i18n, lang]); + const {users, onlyAuthor = false, isAuthor = false, translationName = 'contributors'} = props; + const {t} = useTranslation(translationName); return (
diff --git a/src/components/Control/Control.tsx b/src/components/Control/Control.tsx index 8ee101bf..11960ad5 100644 --- a/src/components/Control/Control.tsx +++ b/src/components/Control/Control.tsx @@ -1,9 +1,9 @@ -import React, {useCallback, useState, useRef} from 'react'; +import {Button, Popup} from '@gravity-ui/uikit'; import block from 'bem-cn-lite'; -import {Popup, Button} from '@gravity-ui/uikit'; +import React, {forwardRef, useCallback, useImperativeHandle, useRef} from 'react'; +import {PopperPosition, usePopupState} from '../../hooks'; import {ControlSizes} from '../../models'; -import {PopperPosition} from '../../hooks'; import './Control.scss'; @@ -30,23 +30,21 @@ const ICONS_SIZES = { [ControlSizes.L]: 20, }; -const Control = (props: ControlProps) => { +const Control = forwardRef((props: ControlProps, ref) => { const { onClick, className, tooltipText, isVerticalView, - setRef, size = ControlSizes.M, icon, popupPosition, } = props; const controlRef = useRef(null); - const [isVisibleTooltip, setIsVisibleTooltip] = useState(false); - const showTooltip = () => setIsVisibleTooltip(true); - const hideTooltip = () => setIsVisibleTooltip(false); + const popupState = usePopupState({autoclose: 3000}); + const getTooltipAlign = useCallback(() => { if (popupPosition) { return popupPosition; @@ -54,16 +52,8 @@ const Control = (props: ControlProps) => { return isVerticalView ? PopperPosition.LEFT_START : PopperPosition.BOTTOM_END; }, [isVerticalView, popupPosition]); - const _setRef = useCallback( - (ref: HTMLButtonElement) => { - controlRef.current = ref; - - if (setRef) { - setRef(ref); - } - }, - [setRef], - ); + + useImperativeHandle(ref, () => controlRef.current, [controlRef]); const position = getTooltipAlign(); const Icon = icon; @@ -74,9 +64,9 @@ const Control = (props: ControlProps) => { - - {tooltipText} - + {controlRef.current && ( + + {tooltipText} + + )} ); -}; +}); Control.displayName = 'DCControl'; diff --git a/src/components/Controls/Controls.tsx b/src/components/Controls/Controls.tsx index 6d350b6e..08326215 100644 --- a/src/components/Controls/Controls.tsx +++ b/src/components/Controls/Controls.tsx @@ -1,28 +1,27 @@ -import React from 'react'; import block from 'bem-cn-lite'; -import {withTranslation, WithTranslation, WithTranslationProps} from 'react-i18next'; +import React, {memo, useContext} from 'react'; -import {Control} from '../Control'; +import {FeedbackSendData, Lang, SubscribeData, TextSizes, Theme} from '../../models'; import {Feedback, FeedbackView} from '../Feedback'; import {Subscribe, SubscribeView} from '../Subscribe'; + +import './Controls.scss'; +import {ControlsLayoutContext} from './ControlsLayout'; + import { + DividerControl, + EditControl, FullScreenControl, - SettingsControl, - SinglePageControl, LangControl, - DividerControl, PdfControl, + SettingsControl, + SinglePageControl, } from './'; -import {PopperPosition} from '../../hooks'; -import {Lang, TextSizes, Theme, FeedbackSendData, ControlSizes, SubscribeData} from '../../models'; -import EditIcon from '@gravity-ui/icons/svgs/pencil.svg'; -import './Controls.scss'; - const b = block('dc-controls'); export interface ControlsProps { - lang: Lang; + lang?: Lang; langs?: string[]; fullScreen?: boolean; singlePage?: boolean; @@ -32,10 +31,8 @@ export interface ControlsProps { textSize?: TextSizes; vcsUrl?: string; vcsType?: string; - showEditControl?: boolean; isLiked?: boolean; isDisliked?: boolean; - dislikeVariants?: string[]; onChangeLang?: (lang: Lang) => void; onChangeFullScreen?: (value: boolean) => void; onChangeSinglePage?: (value: boolean) => void; @@ -47,213 +44,137 @@ export interface ControlsProps { onSubscribe?: (data: SubscribeData) => void; pdfLink?: string; className?: string; - isVerticalView?: boolean; - controlSize?: ControlSizes; + hideEditControl?: boolean; hideFeedbackControls?: boolean; - popupPosition?: PopperPosition; -} - -type ControlsInnerProps = ControlsProps & WithTranslation & WithTranslationProps; - -class Controls extends React.Component { - componentDidUpdate(prevProps: ControlsProps) { - const {i18n, lang} = this.props; - - if (prevProps.lang !== lang) { - i18n.changeLanguage(lang); - } - } - - render() { - const {lang, i18n, className, isVerticalView} = this.props; - - if (i18n.language !== lang) { - i18n.changeLanguage(lang); - } - - return ( -
- {this.renderCommonControls()} - {this.renderEditLink()} - {this.renderFeedbackControls()} - {this.renderSubscribeControls()} -
- ); - } - - private renderEditLink() { - const { - vcsUrl, - vcsType, - showEditControl, - singlePage, - isVerticalView, - controlSize, - popupPosition, - t, - } = this.props; - - if (!showEditControl || singlePage) { - return null; - } - - return ( - - -
- - - - ); - } - - private renderCommonControls() { - const { - fullScreen, - singlePage, - theme, - wideFormat, - showMiniToc, - textSize, - onChangeFullScreen, - onChangeTheme, - onChangeShowMiniToc, - onChangeTextSize, - onChangeWideFormat, - onChangeLang, - onChangeSinglePage, - isVerticalView, - controlSize, - lang, - langs, - popupPosition, - pdfLink, - } = this.props; - - return ( - - - - - - - - ); - } - - private renderFeedbackControls = () => { - const { - lang, - singlePage, - onSendFeedback, - isLiked, - isDisliked, - dislikeVariants, - isVerticalView, - hideFeedbackControls, - popupPosition, - } = this.props; - - if (singlePage || !onSendFeedback || hideFeedbackControls) { - return null; - } - - return ( - - - - - ); - }; - - private renderSubscribeControls = () => { - const {lang, singlePage, onSubscribe, isVerticalView, popupPosition} = this.props; - - if (singlePage || !onSubscribe) { - return null; - } - - return ( - - - - - ); - }; } -export default withTranslation('controls')(Controls); +type Defined = { + [P in keyof ControlsProps]-?: ControlsProps[P]; +}; + +const Controls = memo((props) => { + const {isVerticalView} = useContext(ControlsLayoutContext); + const { + className, + fullScreen, + singlePage, + theme, + wideFormat, + showMiniToc, + hideEditControl, + hideFeedbackControls, + textSize, + onChangeFullScreen, + onChangeTheme, + onChangeShowMiniToc, + onChangeTextSize, + onChangeWideFormat, + onChangeLang, + onChangeSinglePage, + onSendFeedback, + onSubscribe, + lang, + langs, + pdfLink, + vcsUrl, + vcsType, + isLiked, + isDisliked, + } = props; + + const withFullscreenControl = Boolean(onChangeFullScreen); + const withSettingsControl = Boolean( + onChangeWideFormat || onChangeTheme || onChangeShowMiniToc || onChangeTextSize, + ); + const withLangControl = Boolean(lang && onChangeLang); + const withSinglePageControl = Boolean(onChangeSinglePage); + const withPdfControl = Boolean(pdfLink); + const withEditControl = Boolean(!singlePage && !hideEditControl && vcsUrl); + const withFeedbackControl = Boolean(!singlePage && !hideFeedbackControls && onSendFeedback); + const withSubscribeControls = Boolean(!singlePage && onSubscribe); + + const controls = [ + withFullscreenControl && ( + + ), + withSettingsControl && ( + + ), + withLangControl && ( + + ), + withSinglePageControl && ( + + ), + withPdfControl && , + '---', + withEditControl && ( + + ), + '---', + withFeedbackControl && ( + + ), + '---', + withSubscribeControls && ( + + ), + ] + .filter(Boolean) + .reduce((result, control, index, array) => { + if (control === '---') { + if (array[index - 1] && array[index + 1] && array[index + 1] !== '---') { + result.push( + , + ); + } + } else { + result.push(control as React.ReactElement); + } + + return result; + }, [] as React.ReactElement[]); + + return
{controls}
; +}); + +Controls.displayName = 'DCControls'; + +export default Controls; diff --git a/src/components/Controls/ControlsLayout.tsx b/src/components/Controls/ControlsLayout.tsx new file mode 100644 index 00000000..0b9e8004 --- /dev/null +++ b/src/components/Controls/ControlsLayout.tsx @@ -0,0 +1,42 @@ +import block from 'bem-cn-lite'; +import React, {PropsWithChildren, createContext} from 'react'; + +import {PopperPosition} from '../../hooks'; +import {ControlSizes} from '../../models'; + +type ControlsLayoutProps = { + isVerticalView?: boolean; + controlClassName?: string; + controlSize?: ControlSizes; + popupPosition?: PopperPosition; +}; + +export const ControlsLayoutContext = createContext({ + controlClassName: '', + isVerticalView: false, + controlSize: ControlSizes.M, + popupPosition: PopperPosition.BOTTOM_END, +}); + +const b = block('dc-controls'); + +export const ControlsLayout: React.FC> = ({ + isVerticalView, + controlClassName, + controlSize, + popupPosition, + children, +}) => { + return ( + + {children} + + ); +}; diff --git a/src/components/Controls/index.ts b/src/components/Controls/index.ts index 362e8750..92651153 100644 --- a/src/components/Controls/index.ts +++ b/src/components/Controls/index.ts @@ -1,3 +1,4 @@ export * from './Controls'; export {default as Controls} from './Controls'; export * from './single-controls'; +export {ControlsLayout} from './ControlsLayout'; diff --git a/src/components/Controls/single-controls/DividerControl/DividerControl.tsx b/src/components/Controls/single-controls/DividerControl/DividerControl.tsx index 9c58c159..830f955a 100644 --- a/src/components/Controls/single-controls/DividerControl/DividerControl.tsx +++ b/src/components/Controls/single-controls/DividerControl/DividerControl.tsx @@ -1,24 +1,20 @@ -import React from 'react'; import cn from 'bem-cn-lite'; +import React, {useContext} from 'react'; -import {ControlSizes} from '../../../../models'; +import {ControlsLayoutContext} from '../../ControlsLayout'; import './DividerControl.scss'; const b = cn('dc-divider-control'); interface DividerControlProps { - size?: ControlSizes; className?: string; - isVerticalView?: boolean; } -const DividerControl = ({ - size = ControlSizes.M, - className, - isVerticalView = true, -}: DividerControlProps) => { - return
; +const DividerControl: React.FC = ({className}) => { + const {isVerticalView, controlSize} = useContext(ControlsLayoutContext); + + return
; }; export default DividerControl; diff --git a/src/components/Controls/single-controls/EditControl.tsx b/src/components/Controls/single-controls/EditControl.tsx new file mode 100644 index 00000000..1eaf1ea5 --- /dev/null +++ b/src/components/Controls/single-controls/EditControl.tsx @@ -0,0 +1,58 @@ +import EditIcon from '@gravity-ui/icons/svgs/pencil.svg'; +import {Button, Icon} from '@gravity-ui/uikit'; +import block from 'bem-cn-lite'; +import React, {memo, useContext} from 'react'; + +import {useTranslation} from '../../../hooks'; +import {Control} from '../../Control'; +import {ControlsLayoutContext} from '../ControlsLayout'; + +interface EditControlProps { + vcsUrl: string; + vcsType?: string; + view?: string; + className?: string; +} + +const b = block('dc-controls'); + +const EditControl = memo(({vcsUrl, vcsType = 'github', view, className}) => { + const {t} = useTranslation('controls'); + const {controlClassName, controlSize, isVerticalView, popupPosition} = + useContext(ControlsLayoutContext); + + if (view === 'wide') { + return ( + + + + ); + } + + return ( + + + + ); +}); + +EditControl.displayName = 'EditControl'; + +export default EditControl; diff --git a/src/components/Controls/single-controls/FullScreenControl.tsx b/src/components/Controls/single-controls/FullScreenControl.tsx index df913e4b..2650652c 100644 --- a/src/components/Controls/single-controls/FullScreenControl.tsx +++ b/src/components/Controls/single-controls/FullScreenControl.tsx @@ -1,27 +1,22 @@ -import React, {useCallback, useEffect} from 'react'; -import {WithTranslation, withTranslation, WithTranslationProps} from 'react-i18next'; -import {Icon as IconComponent} from '@gravity-ui/uikit'; +import FullScreenClickedIcon from '@gravity-ui/icons/svgs/square-dashed-circle.svg'; +import FullScreenIcon from '@gravity-ui/icons/svgs/square-dashed.svg'; +import {Icon} from '@gravity-ui/uikit'; +import React, {memo, useCallback, useContext, useEffect} from 'react'; +import {useTranslation} from '../../../hooks'; import {Control} from '../../Control'; -import {ControlSizes, Lang} from '../../../models'; - -import FullScreenClickedIcon from '../../../../assets/icons/full-screen-clicked.svg'; -import FullScreenIcon from '@gravity-ui/icons/svgs/square-dashed.svg'; +import {ControlsLayoutContext} from '../ControlsLayout'; interface ControlProps { - lang: Lang; value?: boolean; onChange?: (value: boolean) => void; - isVerticalView?: boolean; - className?: string; - size?: ControlSizes; - popupPosition?: PopperPosition; } -type ControlInnerProps = ControlProps & WithTranslation & WithTranslationProps; - -const FullScreenControl = (props: ControlInnerProps) => { - const {className, isVerticalView, size, value, onChange, lang, popupPosition, i18n, t} = props; +const FullScreenControl = memo((props) => { + const {t} = useTranslation('controls'); + const {controlClassName, controlSize, isVerticalView, popupPosition} = + useContext(ControlsLayoutContext); + const {value, onChange} = props; const onClick = useCallback(() => { if (onChange) { @@ -46,29 +41,23 @@ const FullScreenControl = (props: ControlInnerProps) => { }; }, [onKeyDown]); - useEffect(() => { - i18n.changeLanguage(lang); - }, [i18n, lang]); - const activeMode = value ? 'enabled' : 'disabled'; - if (!onChange) { - return null; - } - return ( ( - + )} popupPosition={popupPosition} /> ); -}; +}); + +FullScreenControl.displayName = 'FullScreenControl'; -export default withTranslation('controls')(FullScreenControl); +export default FullScreenControl; diff --git a/src/components/Controls/single-controls/LangControl.tsx b/src/components/Controls/single-controls/LangControl.tsx index 8fe65c8a..f09ecc1a 100644 --- a/src/components/Controls/single-controls/LangControl.tsx +++ b/src/components/Controls/single-controls/LangControl.tsx @@ -1,18 +1,22 @@ -import React, {useCallback, useEffect, useState, useRef} from 'react'; -import {WithTranslation, withTranslation, WithTranslationProps} from 'react-i18next'; -import allLangs from 'langs'; -import {Popup, Icon as IconComponent, List} from '@gravity-ui/uikit'; +import LangIcon from '@gravity-ui/icons/svgs/globe.svg'; +import {Icon, List, Popup} from '@gravity-ui/uikit'; import block from 'bem-cn-lite'; +import allLangs from 'langs'; +import React, {useCallback, useContext, useMemo, useRef} from 'react'; +import {usePopupState, useTranslation} from '../../../hooks'; +import {Lang} from '../../../models'; import {Control} from '../../Control'; -import {ControlSizes, Lang} from '../../../models'; -import {getPopupPosition} from './utils'; -import {PopperPosition} from '../../../hooks'; - -import LangIcon from '@gravity-ui/icons/svgs/globe.svg'; - import '../Controls.scss'; +import {ControlsLayoutContext} from '../ControlsLayout'; + +import {getPopupPosition} from './utils'; +const ICONS: Record = { + en: '🇬🇧', + ru: '🇷🇺', +}; +const DEFAULT_LANGS = ['en', 'ru']; const LEGACY_LANG_ITEMS = [ {value: Lang.En, text: 'English', icon: '🇬🇧'}, {value: Lang.Ru, text: 'Русский', icon: '🇷🇺'}, @@ -23,11 +27,7 @@ const b = block('dc-controls'); interface ControlProps { lang: Lang; langs?: string[]; - isVerticalView?: boolean; - className?: string; - size?: ControlSizes; - onChangeLang?: (lang: Lang) => void; - popupPosition?: PopperPosition; + onChangeLang: (lang: Lang) => void; } interface ListItem { @@ -38,37 +38,16 @@ interface ListItem { const LIST_ITEM_HEIGHT = 36; -type ControlInnerProps = ControlProps & WithTranslation & WithTranslationProps; - -const LangControl = (props: ControlInnerProps) => { - const { - className, - isVerticalView, - size, - lang, - langs = [], - i18n, - onChangeLang, - popupPosition, - t, - } = props; +const LangControl = (props: ControlProps) => { + const {t} = useTranslation('controls'); + const {controlClassName, controlSize, isVerticalView, popupPosition} = + useContext(ControlsLayoutContext); + const {lang, langs = DEFAULT_LANGS, onChangeLang} = props; - const [langItems, setLangItems] = useState(LEGACY_LANG_ITEMS); const controlRef = useRef(null); - const [isVisiblePopup, setIsVisiblePopup] = useState(false); - const showPopup = () => setIsVisiblePopup(true); - const hidePopup = () => setIsVisiblePopup(false); - const _onChangeLang = useCallback( - (value: Lang) => { - if (onChangeLang) { - onChangeLang(value); - } - }, - [onChangeLang], - ); - - useEffect(() => { + const popupState = usePopupState(); + const langItems = useMemo(() => { const preparedLangs = langs .map((code) => { const langData = allLangs.where('1', code); @@ -77,29 +56,28 @@ const LangControl = (props: ControlInnerProps) => { ? { text: langData.name, value: langData['1'], + icon: ICONS[code] || '', } : undefined; }) .filter(Boolean) as ListItem[]; - if (preparedLangs.length) { - setLangItems(preparedLangs); - } else { - setLangItems(LEGACY_LANG_ITEMS); - } + return preparedLangs.length ? preparedLangs : LEGACY_LANG_ITEMS; }, [langs]); - - useEffect(() => { - i18n.changeLanguage(lang); - }, [i18n, lang]); - - const setRef = useCallback((ref: HTMLButtonElement) => { - controlRef.current = ref; + const renderItem = useCallback((item: ListItem) => { + return ( +
+
{item.icon}
+ {item.text} +
+ ); }, []); - - if (!onChangeLang) { - return null; - } + const onItemClick = useCallback( + (item: ListItem) => { + onChangeLang(item.value as Lang); + }, + [onChangeLang], + ); const itemsHeight = LIST_ITEM_HEIGHT * langItems.length; const selectedItemIndex = langItems.findIndex(({value}) => value === lang); @@ -107,42 +85,36 @@ const LangControl = (props: ControlInnerProps) => { return ( } - setRef={setRef} + icon={(args) => } popupPosition={popupPosition} /> - - { - _onChangeLang(item.value as Lang); - }} - selectedItemIndex={selectedItemIndex} - itemHeight={LIST_ITEM_HEIGHT} - itemsHeight={itemsHeight} - renderItem={(item) => { - return ( -
-
{item.icon}
{item.text} -
- ); - }} - /> -
+ {popupState.visible && ( + + + + )}
); }; -export default withTranslation('controls')(LangControl); +export default LangControl; diff --git a/src/components/Controls/single-controls/PdfControl.tsx b/src/components/Controls/single-controls/PdfControl.tsx index 1b5a4acb..3090abc8 100644 --- a/src/components/Controls/single-controls/PdfControl.tsx +++ b/src/components/Controls/single-controls/PdfControl.tsx @@ -1,47 +1,35 @@ -import React, {useEffect} from 'react'; -import {WithTranslation, withTranslation, WithTranslationProps} from 'react-i18next'; -import {Icon as IconComponent} from '@gravity-ui/uikit'; - -import {Control} from '../../Control'; -import {ControlSizes, Lang} from '../../../models'; -import {PopperPosition} from '../../../hooks'; +import {Icon} from '@gravity-ui/uikit'; +import React, {memo, useContext} from 'react'; import PdfIcon from '../../../../assets/icons/pdf.svg'; +import {useTranslation} from '../../../hooks'; +import {Control} from '../../Control'; +import {ControlsLayoutContext} from '../ControlsLayout'; interface ControlProps { - lang: Lang; - pdfLink?: string; - isVerticalView?: boolean; - className?: string; - size?: ControlSizes; - popupPosition?: PopperPosition; + pdfLink: string; } -type ControlInnerProps = ControlProps & WithTranslation & WithTranslationProps; - -const PdfControl = (props: ControlInnerProps) => { - const {className, isVerticalView, size, pdfLink, lang, i18n, popupPosition, t} = props; - - useEffect(() => { - i18n.changeLanguage(lang); - }, [i18n, lang]); - - if (!pdfLink) { - return null; - } +const PdfControl = memo((props) => { + const {t} = useTranslation('controls'); + const {controlClassName, controlSize, isVerticalView, popupPosition} = + useContext(ControlsLayoutContext); + const {pdfLink} = props; return ( } + icon={(args) => } popupPosition={popupPosition} /> ); -}; +}); + +PdfControl.displayName = 'PdfControl'; -export default withTranslation('controls')(PdfControl); +export default PdfControl; diff --git a/src/components/Controls/single-controls/SettingsControl/SettingsControl.tsx b/src/components/Controls/single-controls/SettingsControl/SettingsControl.tsx index 7a04f6e3..b1e180c4 100644 --- a/src/components/Controls/single-controls/SettingsControl/SettingsControl.tsx +++ b/src/components/Controls/single-controls/SettingsControl/SettingsControl.tsx @@ -1,16 +1,14 @@ -import React, {useCallback, useEffect, useState, useRef} from 'react'; -import {WithTranslation, withTranslation, WithTranslationProps} from 'react-i18next'; +import SettingsIcon from '@gravity-ui/icons/svgs/gear.svg'; +import {Button, Icon, List, Popup, Switch} from '@gravity-ui/uikit'; import cn from 'bem-cn-lite'; -import {Button, Popup, Switch, List, Icon as IconComponent} from '@gravity-ui/uikit'; +import React, {ReactElement, useCallback, useContext, useRef, useState} from 'react'; +import {useTranslation} from '../../../../hooks'; +import {TextSizes, Theme} from '../../../../models'; import {Control} from '../../../Control'; -import {ControlSizes, Lang, TextSizes, Theme} from '../../../../models'; -import {PopperPosition} from '../../../../hooks'; - +import {ControlsLayoutContext} from '../../ControlsLayout'; import {getPopupPosition} from '../utils'; -import SettingsIcon from '@gravity-ui/icons/svgs/gear.svg'; - import './SettingsControl.scss'; const ITEM_HEIGHT = 48; @@ -23,32 +21,23 @@ interface ControlProps { showMiniToc?: boolean; theme?: Theme; textSize?: TextSizes; - lang: Lang; - isVerticalView?: boolean; - className?: string; - size?: ControlSizes; onChangeWideFormat?: (value: boolean) => void; onChangeShowMiniToc?: (value: boolean) => void; onChangeTheme?: (theme: Theme) => void; onChangeTextSize?: (textSize: TextSizes) => void; - popupPosition?: PopperPosition; } -type ControlInnerProps = ControlProps & WithTranslation & WithTranslationProps; - interface SettingControlItem { text: string; description: string; - control: Element; + control: ReactElement; } -const SettingsControl = (props: ControlInnerProps) => { +const SettingsControl = (props: ControlProps) => { + const {t} = useTranslation('controls'); + const {controlClassName, controlSize, isVerticalView, popupPosition} = + useContext(ControlsLayoutContext); const { - className, - isVerticalView, - size, - lang, - i18n, textSize, theme, wideFormat, @@ -59,8 +48,6 @@ const SettingsControl = (props: ControlInnerProps) => { onChangeWideFormat, onChangeShowMiniToc, onChangeTextSize, - popupPosition, - t, } = props; const controlRef = useRef(null); @@ -68,8 +55,8 @@ const SettingsControl = (props: ControlInnerProps) => { const showPopup = () => setIsVisiblePopup(true); const hidePopup = () => setIsVisiblePopup(false); - const makeOnChangeTextSize = useCallback( - (textSizeKey) => () => { + const _onChangeTextSize = useCallback( + (textSizeKey: TextSizes) => () => { if (onChangeTextSize) { onChangeTextSize(textSizeKey); } @@ -142,7 +129,7 @@ const SettingsControl = (props: ControlInnerProps) => { [textSizeKey]: true, })} view="flat" - onClick={makeOnChangeTextSize(textSizeKey)} + onClick={_onChangeTextSize(textSizeKey)} > { showMiniToc, fullScreen, singlePage, - onChangeTheme, - onChangeWideFormat, - onChangeShowMiniToc, _onChangeTheme, _onChangeWideFormat, _onChangeShowMiniToc, + _onChangeTextSize, + onChangeWideFormat, + onChangeShowMiniToc, + onChangeTheme, onChangeTextSize, - makeOnChangeTextSize, ]); - useEffect(() => { - i18n.changeLanguage(lang); - }, [i18n, lang]); - - const setRef = useCallback((ref: HTMLButtonElement) => { - controlRef.current = ref; - }, []); - - if (!(onChangeWideFormat || onChangeTheme || onChangeShowMiniToc || onChangeTextSize)) { - return null; - } - const settingsItems = getSettingsItems(); return ( } + icon={(args) => } /> { itemHeight={ITEM_HEIGHT} itemsHeight={ITEM_HEIGHT * settingsItems.length} filterable={false} - renderItem={(item) => { + renderItem={(item: SettingControlItem) => { return (
@@ -234,4 +209,4 @@ const SettingsControl = (props: ControlInnerProps) => { ); }; -export default withTranslation('controls')(SettingsControl); +export default SettingsControl; diff --git a/src/components/Controls/single-controls/SinglePageControl.tsx b/src/components/Controls/single-controls/SinglePageControl.tsx index bed7cd43..b2f2d75e 100644 --- a/src/components/Controls/single-controls/SinglePageControl.tsx +++ b/src/components/Controls/single-controls/SinglePageControl.tsx @@ -1,58 +1,44 @@ -import React, {useCallback, useEffect} from 'react'; -import {WithTranslation, withTranslation, WithTranslationProps} from 'react-i18next'; -import {Icon as IconComponent} from '@gravity-ui/uikit'; +import {ChevronsCollapseToLine, ChevronsExpandToLines} from '@gravity-ui/icons'; +import block from 'bem-cn-lite'; +import React, {memo, useCallback, useContext} from 'react'; +import {useTranslation} from '../../../hooks'; import {Control} from '../../Control'; -import {ControlSizes, Lang} from '../../../models'; -import {PopperPosition} from '../../../hooks'; - -import SinglePageIcon from '../../../../assets/icons/single-page.svg'; -import SinglePageClickedIcon from '../../../../assets/icons/single-page-clicked.svg'; +import {ControlsLayoutContext} from '../ControlsLayout'; interface ControlProps { - lang: Lang; value?: boolean; - onChange?: (value: boolean) => void; - isVerticalView?: boolean; - className?: string; - size?: ControlSizes; - popupPosition?: PopperPosition; + onChange: (value: boolean) => void; } -type ControlInnerProps = ControlProps & WithTranslation & WithTranslationProps; +const b = block('dc-controls'); -const SinglePageControl = (props: ControlInnerProps) => { - const {className, isVerticalView, size, value, onChange, lang, i18n, popupPosition, t} = props; +const SinglePageControl = memo((props) => { + const {t} = useTranslation('controls'); + const {controlClassName, controlSize, isVerticalView, popupPosition} = + useContext(ControlsLayoutContext); + const {value, onChange} = props; const onClick = useCallback(() => { - if (onChange) { - onChange(!value); - } + onChange(!value); }, [value, onChange]); - useEffect(() => { - i18n.changeLanguage(lang); - }, [i18n, lang]); - const activeMode = value ? 'enabled' : 'disabled'; - - if (!onChange) { - return null; - } + const Icon = value ? ChevronsExpandToLines : ChevronsCollapseToLine; return ( ( - - )} + icon={(args) => } popupPosition={popupPosition} /> ); -}; +}); + +SinglePageControl.displayName = 'SinglePageControl'; -export default withTranslation('controls')(SinglePageControl); +export default SinglePageControl; diff --git a/src/components/Controls/single-controls/index.ts b/src/components/Controls/single-controls/index.ts index b0c435a8..e3211962 100644 --- a/src/components/Controls/single-controls/index.ts +++ b/src/components/Controls/single-controls/index.ts @@ -4,3 +4,4 @@ export {default as SinglePageControl} from './SinglePageControl'; export {default as LangControl} from './LangControl'; export {default as DividerControl} from './DividerControl/DividerControl'; export {default as PdfControl} from './PdfControl'; +export {default as EditControl} from './EditControl'; diff --git a/src/components/DocLayout/DocLayout.tsx b/src/components/DocLayout/DocLayout.tsx index ad9ee9f7..87ea494d 100644 --- a/src/components/DocLayout/DocLayout.tsx +++ b/src/components/DocLayout/DocLayout.tsx @@ -1,8 +1,7 @@ -import React, {PropsWithChildren, ReactElement} from 'react'; - import block from 'bem-cn-lite'; +import React, {PropsWithChildren, ReactElement} from 'react'; -import {TocData, Router, Lang} from '../../models'; +import {Router, TocData} from '../../models'; import {getStateKey} from '../../utils'; import {Toc} from '../Toc'; @@ -17,7 +16,6 @@ const Right: React.FC = () => null; export interface DocLayoutProps { toc: TocData; router: Router; - lang: Lang; children: (ReactElement | null)[] | ReactElement; fullScreen?: boolean; hideRight?: boolean; @@ -104,7 +102,6 @@ export class DocLayout extends React.Component { wideFormat, hideTocHeader, hideToc, - lang, singlePage, onChangeSinglePage, pdfLink, @@ -124,7 +121,6 @@ export class DocLayout extends React.Component { headerHeight={headerHeight} tocTitleIcon={tocTitleIcon} hideTocHeader={hideTocHeader} - lang={lang} singlePage={singlePage} onChangeSinglePage={onChangeSinglePage} pdfLink={pdfLink} diff --git a/src/components/DocLeadingPage/DocLeadingPage.tsx b/src/components/DocLeadingPage/DocLeadingPage.tsx index eea825f7..01bc4bfb 100644 --- a/src/components/DocLeadingPage/DocLeadingPage.tsx +++ b/src/components/DocLeadingPage/DocLeadingPage.tsx @@ -1,9 +1,8 @@ -import React from 'react'; - import block from 'bem-cn-lite'; +import React from 'react'; -import {DocLeadingPageData, DocLeadingLinks, Router, Lang} from '../../models'; import {DEFAULT_SETTINGS} from '../../constants'; +import {DocLeadingLinks, DocLeadingPageData, Router} from '../../models'; import {DocLayout} from '../DocLayout'; import {DocPageTitle} from '../DocPageTitle'; import {HTML} from '../HTML'; @@ -17,7 +16,6 @@ const {wideFormat: defaultWideFormat} = DEFAULT_SETTINGS; export interface DocLeadingPageProps extends DocLeadingPageData { router: Router; - lang: Lang; headerHeight?: number; wideFormat?: boolean; hideTocHeader?: boolean; @@ -106,7 +104,6 @@ export const DocLeadingPage: React.FC = ({ data: {title, description, links}, toc, router, - lang, headerHeight, wideFormat = defaultWideFormat, hideTocHeader, @@ -122,7 +119,6 @@ export const DocLeadingPage: React.FC = ({ { const { toc, router, - lang, headerHeight, wideFormat, fullScreen, @@ -155,7 +152,6 @@ class DocPage extends React.Component { { } private addLinksToOriginalArticle = () => { - const {singlePage, lang, convertPathToOriginalArticle, generatePathToVcs} = this.props; + const {singlePage, convertPathToOriginalArticle, generatePathToVcs, vcsType} = this.props; if (singlePage) { const elements = document.querySelectorAll('[data-original-article]'); @@ -291,7 +287,12 @@ class DocPage extends React.Component { const vcsHref = callSafe(generatePathToVcs, href); const linkToVcs = createElementFromHTML( ReactDOMServer.renderToStaticMarkup( - , + , ), ); linkWrapperEl.append(linkToVcs); @@ -336,19 +337,21 @@ class DocPage extends React.Component { private renderTitle() { const {title, meta, bookmarkedPage, onChangeBookmarkPage} = this.props; + const withBookmarks = onChangeBookmarkPage; if (!title) { return null; } return ( - + {title} + {withBookmarks && ( + + )} ); } @@ -373,7 +376,7 @@ class DocPage extends React.Component { } private renderAuthor(onlyAuthor: boolean) { - const {meta, lang} = this.props; + const {meta} = this.props; if (!isContributor(meta?.author)) { return null; @@ -381,7 +384,6 @@ class DocPage extends React.Component { return ( { } private renderContributors() { - const {meta, lang} = this.props; + const {meta} = this.props; if (!meta?.contributors || meta.contributors.length === 0) { return null; } - return ; + return ; } private renderContentMiniToc() { @@ -463,7 +465,7 @@ class DocPage extends React.Component { } private renderAsideMiniToc() { - const {headings, router, headerHeight, lang} = this.props; + const {headings, router, headerHeight} = this.props; const {keyDOM} = this.state; return ( @@ -472,7 +474,6 @@ class DocPage extends React.Component { headings={headings} router={router} headerHeight={headerHeight} - lang={lang} key={keyDOM} />
@@ -480,32 +481,17 @@ class DocPage extends React.Component { } private renderFeedback() { - const { - toc, - lang, - singlePage, - isLiked, - isDisliked, - onSendFeedback, - dislikeVariants, - hideFeedbackControls, - } = this.props; + const {singlePage, isLiked, isDisliked, onSendFeedback, hideFeedbackControls} = this.props; - if (!toc || toc.singlePage || hideFeedbackControls) { + if (singlePage || hideFeedbackControls || !onSendFeedback) { return null; } - const isVerticalView = this.getIsVerticalView(); - return (
@@ -514,16 +500,15 @@ class DocPage extends React.Component { } private renderTocNavPanel() { - const {toc, router, fullScreen, lang} = this.props; + const {toc, singlePage, router, fullScreen} = this.props; - if (!toc || toc.singlePage) { + if (singlePage) { return null; } return ( { onClickPrevSearch, onClickNextSearch, onCloseSearchBar, - lang, singlePage, } = this.props; @@ -567,7 +551,6 @@ class DocPage extends React.Component { return (
{ onSubscribe, isLiked, isDisliked, - dislikeVariants, hideControls, hideEditControl, hideFeedbackControls, @@ -610,37 +592,36 @@ class DocPage extends React.Component { return null; } - const showEditControl = !hideEditControl && !fullScreen && this.isEditable(); const isVerticalView = this.getIsVerticalView(); return (
- + + +
); } diff --git a/src/components/DocPageTitle/DocPageTitle.tsx b/src/components/DocPageTitle/DocPageTitle.tsx index 82a6f6d1..3378f01e 100644 --- a/src/components/DocPageTitle/DocPageTitle.tsx +++ b/src/components/DocPageTitle/DocPageTitle.tsx @@ -1,8 +1,7 @@ -import React, {PropsWithChildren} from 'react'; - import block from 'bem-cn-lite'; +import React, {PropsWithChildren} from 'react'; -import {BookmarkButton} from '../BookmarkButton'; +import {StageLabel, StageType} from '../StageLabel'; import './DocPageTitle.scss'; @@ -11,16 +10,12 @@ const b = block('dc-doc-page-title'); export interface DocPageTitleProps { stage?: string; className?: string; - bookmarkedPage?: boolean; - onChangeBookmarkPage?: (value: boolean) => void; } export const DocPageTitle: React.FC> = ({ children, stage, className, - bookmarkedPage, - onChangeBookmarkPage, }) => { const visibleStage = stage === 'tech-preview' ? 'preview' : (stage as StageType | undefined); const label = ; @@ -29,13 +24,6 @@ export const DocPageTitle: React.FC> = ({

{label} {children} - {typeof bookmarkedPage !== 'undefined' && - typeof onChangeBookmarkPage !== 'undefined' && ( - - )}

); }; diff --git a/src/components/EditButton/EditButton.scss b/src/components/EditButton/EditButton.scss deleted file mode 100644 index 13c046e1..00000000 --- a/src/components/EditButton/EditButton.scss +++ /dev/null @@ -1,8 +0,0 @@ -.dc-edit-button { - position: absolute; - right: 0; - - &__text { - margin-left: 8px; - } -} diff --git a/src/components/EditButton/EditButton.tsx b/src/components/EditButton/EditButton.tsx deleted file mode 100644 index 0d8e188a..00000000 --- a/src/components/EditButton/EditButton.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react'; -import block from 'bem-cn-lite'; -import {withTranslation, WithTranslation, WithTranslationProps} from 'react-i18next'; -import {Button, Icon as IconComponent} from '@gravity-ui/uikit'; - - -import {Lang} from '../../models'; -import EditIcon from '@gravity-ui/icons/svgs/pencil.svg'; - -import './EditButton.scss'; - -const b = block('dc-edit-button'); - -export interface EditButtonProps { - lang: Lang; - href: string; -} - -type EditButtonInnerProps = EditButtonProps & WithTranslation & WithTranslationProps; - -class EditButton extends React.Component { - componentDidUpdate(prevProps: EditButtonProps) { - const {i18n, lang} = this.props; - if (prevProps.lang !== lang) { - i18n.changeLanguage(lang); - } - } - - render() { - const {t, href} = this.props; - - return ( - - - - ); - } -} - -export default withTranslation('controls')(EditButton); diff --git a/src/components/EditButton/index.ts b/src/components/EditButton/index.ts deleted file mode 100644 index 77e2f618..00000000 --- a/src/components/EditButton/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {default as EditButton} from './EditButton'; -export * from './EditButton'; diff --git a/src/components/ErrorPage/ErrorPage.tsx b/src/components/ErrorPage/ErrorPage.tsx index 980ea549..4f9a3778 100644 --- a/src/components/ErrorPage/ErrorPage.tsx +++ b/src/components/ErrorPage/ErrorPage.tsx @@ -1,11 +1,9 @@ -import React from 'react'; - import {Button, Link} from '@gravity-ui/uikit'; import block from 'bem-cn-lite'; -import {withTranslation, WithTranslation, WithTranslationProps} from 'react-i18next'; +import React from 'react'; -import {Lang} from '../../models'; import {ERROR_CODES} from '../../constants'; +import {useTranslation} from '../../hooks'; import './ErrorPage.scss'; @@ -13,29 +11,20 @@ const b = block('ErrorPage'); export interface ErrorPageProps { code?: number; - lang?: Lang; pageGroup?: string; homeUrl?: string; receiveAccessUrl?: string; } -type ErrorPagePropsInnerProps = ErrorPageProps & WithTranslation & WithTranslationProps; - -const ErrorPage = ({ +const ErrorPage: React.FC = ({ code = 500, - lang = Lang.En, - i18n, - t, homeUrl, receiveAccessUrl, pageGroup, -}: ErrorPagePropsInnerProps): JSX.Element => { - if (i18n.language !== lang) { - i18n.changeLanguage(lang); - } - +}) => { let title; let description; + const {t} = useTranslation('error'); const href = homeUrl || '/'; const homeLink = ( @@ -91,4 +80,4 @@ const ErrorPage = ({ ); }; -export default withTranslation('error')(ErrorPage); +export default ErrorPage; diff --git a/src/components/Feedback/Feedback.scss b/src/components/Feedback/Feedback.scss index b3bea843..92469aaf 100644 --- a/src/components/Feedback/Feedback.scss +++ b/src/components/Feedback/Feedback.scss @@ -39,12 +39,6 @@ $popupWidth: 320px; } } - &__like-button { - &_active { - color: var(--g-color-base-brand); - } - } - &__success-popup { padding: $popupPadding 50px $popupPadding $popupPadding; width: $popupWidth; @@ -145,10 +139,4 @@ $popupWidth: 320px; } } } - - &__feedback-button { - &_active { - color: var(--g-color-base-brand); - } - } } diff --git a/src/components/Feedback/Feedback.tsx b/src/components/Feedback/Feedback.tsx index 08ef49f3..98ec9657 100644 --- a/src/components/Feedback/Feedback.tsx +++ b/src/components/Feedback/Feedback.tsx @@ -1,17 +1,14 @@ -import React, {useCallback, useState, useEffect, useRef} from 'react'; import block from 'bem-cn-lite'; -import {withTranslation, WithTranslation, WithTranslationProps} from 'react-i18next'; -import {Checkbox, Popup, TextArea, Button, Icon as IconComponent} from '@gravity-ui/uikit'; +import React, {PropsWithChildren, useCallback, useEffect, useRef, useState} from 'react'; -import {Control} from '../Control'; -import {PopperPosition} from '../../hooks'; -import {FeedbackSendData, FeedbackType, Lang} from '../../models'; -import {DISLIKE_VARIANTS} from '../../constants'; - -import DislikeActiveIcon from '@gravity-ui/icons/svgs/thumbs-down-fill.svg'; -import DislikeIcon from '@gravity-ui/icons/svgs/thumbs-down.svg'; +import {usePopupState, useTranslation} from '../../hooks'; +import {FeedbackSendData, FeedbackType} from '../../models'; import './Feedback.scss'; +import DislikeControl from './controls/DislikeControl'; +import DislikeVariantsPopup, {FormData} from './controls/DislikeVariantsPopup'; +import LikeControl from './controls/LikeControl'; +import SuccessPopup from './controls/SuccessPopup'; const b = block('dc-feedback'); @@ -21,455 +18,143 @@ export enum FeedbackView { } export interface FeedbackProps { - lang: Lang; - singlePage?: boolean; isLiked?: boolean; isDisliked?: boolean; - dislikeVariants?: string[]; - isVerticalView?: boolean; - onSendFeedback?: (data: FeedbackSendData) => void; + onSendFeedback: (data: FeedbackSendData) => void; view?: FeedbackView; - classNameControl?: string; - popupPosition?: PopperPosition; } -interface FeedbackCheckboxes { - [key: string]: boolean; -} +const getInnerState = (isLiked: boolean, isDisliked: boolean) => { + switch (true) { + case Boolean(isDisliked): + return FeedbackType.dislike; + case Boolean(isLiked): + return FeedbackType.like; + default: + return FeedbackType.indeterminate; + } +}; + +const ControlsLayout: React.FC> = ({view, children}) => { + const {t} = useTranslation('feedback'); -type FeedbackInnerProps = FeedbackProps & WithTranslation & WithTranslationProps; + if (view === FeedbackView.Regular) { + return {children}; + } + + return ( +
+
+

{t('main-question')}

+
{children}
+
+
+ ); +}; -const Feedback: React.FC = (props) => { +const Feedback: React.FC = (props) => { const { - lang, - singlePage, - isLiked, - isDisliked, - dislikeVariants = DISLIKE_VARIANTS[lang], - isVerticalView, + isLiked = false, + isDisliked = false, onSendFeedback, - view, - classNameControl, - i18n, - popupPosition, - t, + view = FeedbackView.Regular, } = props; const likeControlRef = useRef(null); const dislikeControlRef = useRef(null); - const timerId = useRef(); - const timeout = 3000; - - const [innerIsDisliked, setInnerIsDisliked] = useState(isDisliked); - const [feedbackComment, setFeedbackComment] = useState(''); - const [feedbackCheckboxes, setFeedbackCheckboxes] = useState({} as FeedbackCheckboxes); - const [showLikeSuccessPopup, setShowLikeSuccessPopup] = useState(false); - const [showDislikeSuccessPopup, setShowDislikeSuccessPopup] = useState(false); - const [showDislikeVariantsPopup, setShowDislikeVariantsPopup] = useState(false); - - const hideFeedbackPopups = useCallback(() => { - setShowDislikeSuccessPopup(false); - setShowLikeSuccessPopup(false); - setShowDislikeVariantsPopup(false); - }, []); - - const resetFeedbackAdditionalInfo = useCallback(() => { - setFeedbackComment(''); - setFeedbackCheckboxes({}); - }, []); + const [innerState, setInnerState] = useState(getInnerState(isLiked, isDisliked)); useEffect(() => { - setInnerIsDisliked(isDisliked); - }, [isDisliked]); + setInnerState(getInnerState(isLiked, isDisliked)); + }, [isLiked, isDisliked, setInnerState]); - useEffect(() => { - i18n.changeLanguage(lang); - }, [i18n, lang]); - - const setTimer = useCallback((callback: () => void) => { - timerId.current = setTimeout(async () => { - callback(); - }, timeout); - }, []); - - const clearTimer = useCallback(() => { - clearTimeout(timerId.current as number); - timerId.current = undefined; - }, []); + const likeSuccessPopup = usePopupState({autoclose: 3000}); + const dislikeSuccessPopup = usePopupState({autoclose: 3000}); + const dislikeVariantsPopup = usePopupState(); - useEffect(() => { - if (showLikeSuccessPopup || showDislikeSuccessPopup) { - setTimer(() => { - setShowDislikeSuccessPopup(false); - setShowLikeSuccessPopup(false); - clearTimer(); - }); - } - }, [isDisliked, clearTimer, setTimer, showLikeSuccessPopup, showDislikeSuccessPopup]); - - const setLikeControlRef = useCallback((ref) => { - likeControlRef.current = ref; - }, []); - - const setDislikeControlRef = useCallback((ref) => { - dislikeControlRef.current = ref; - }, []); - - const onOutsideClick = useCallback(() => { - hideFeedbackPopups(); - - if (showDislikeVariantsPopup && innerIsDisliked && !isDisliked) { - setInnerIsDisliked(false); - } - }, [ - isDisliked, - innerIsDisliked, - hideFeedbackPopups, - setInnerIsDisliked, - showDislikeVariantsPopup, - ]); - - const onSendDislikeInformation = useCallback(() => { - setShowDislikeSuccessPopup(true); - setShowLikeSuccessPopup(false); - setShowDislikeVariantsPopup(false); - - if (onSendFeedback) { - const type = FeedbackType.dislike; - - const additionalInfo = getPreparedFeedbackAdditionalInfo( - feedbackComment, - feedbackCheckboxes, - ); - const data = { - type, - ...additionalInfo, - }; - - onSendFeedback(data); - - resetFeedbackAdditionalInfo(); - } - }, [onSendFeedback, feedbackComment, feedbackCheckboxes, resetFeedbackAdditionalInfo]); - - const getPopupPosition = useCallback(() => { - if (!view || view === FeedbackView.Regular) { - return isVerticalView ? PopperPosition.LEFT_START : PopperPosition.BOTTOM_END; - } - - return PopperPosition.RIGHT; - }, [isVerticalView, view]); + const hideFeedbackPopups = useCallback(() => { + likeSuccessPopup.close(); + dislikeSuccessPopup.close(); + dislikeVariantsPopup.close(); + }, [likeSuccessPopup, dislikeSuccessPopup, dislikeVariantsPopup]); const onChangeLike = useCallback(() => { - setShowLikeSuccessPopup(true); - setShowDislikeSuccessPopup(false); - setShowDislikeVariantsPopup(false); - setInnerIsDisliked(false); + hideFeedbackPopups(); - if (onSendFeedback) { - onSendFeedback({ - type: isLiked ? FeedbackType.indeterminate : FeedbackType.like, - }); + if (innerState === FeedbackType.like) { + setInnerState(FeedbackType.indeterminate); + onSendFeedback({type: FeedbackType.indeterminate}); + } else { + setInnerState(FeedbackType.like); + onSendFeedback({type: FeedbackType.like}); + likeSuccessPopup.open(); } - }, [isLiked, onSendFeedback]); + }, [onSendFeedback, setInnerState, innerState, likeSuccessPopup, hideFeedbackPopups]); const onChangeDislike = useCallback(() => { - if (!isDisliked && !innerIsDisliked) { - // Нажать дизлайк и показать окно с доп. информацией - setShowDislikeVariantsPopup(true); - setInnerIsDisliked(true); - setShowLikeSuccessPopup(false); - setShowDislikeSuccessPopup(false); - - if (isLiked && onSendFeedback) { - onSendFeedback({type: FeedbackType.indeterminate}); - } - } else if (!isDisliked && innerIsDisliked) { - hideFeedbackPopups(); - setInnerIsDisliked(false); - } else if (isDisliked && innerIsDisliked) { - // Отжать дизлайк и отправить событие в неопределенное состояние - hideFeedbackPopups(); - setInnerIsDisliked(false); - - if (onSendFeedback) { - onSendFeedback({type: FeedbackType.indeterminate}); - } - } - }, [innerIsDisliked, isDisliked, isLiked, onSendFeedback, hideFeedbackPopups]); - - const renderLikeControl = useCallback(() => { - return ( - ( - - )} - popupPosition={popupPosition} - /> - ); - }, [ - onChangeLike, - classNameControl, - view, - isVerticalView, - isLiked, - setLikeControlRef, - popupPosition, - t, - ]); - - const renderDislikeControl = useCallback(() => { - return ( - ( - - )} - /> - ); - }, [ - innerIsDisliked, - onChangeDislike, - classNameControl, - view, - isVerticalView, - setDislikeControlRef, - t, - ]); - - const renderRegularFeedbackControls = useCallback(() => { - return ( - - {renderLikeControl()} - {renderDislikeControl()} - - ); - }, [renderLikeControl, renderDislikeControl]); - - const renderWideFeedbackControls = useCallback(() => { - return ( -
-
-

{t('main-question')}

-
- - -
-
-
- ); - }, [ - innerIsDisliked, - isLiked, - view, - t, - setLikeControlRef, - setDislikeControlRef, - onChangeLike, - onChangeDislike, - ]); - - const renderFeedbackControls = useCallback(() => { - return view === FeedbackView.Regular - ? renderRegularFeedbackControls() - : renderWideFeedbackControls(); - }, [view, renderRegularFeedbackControls, renderWideFeedbackControls]); - - const renderFeedbackSuccessPopup = useCallback(() => { - const anchor = showLikeSuccessPopup ? likeControlRef : dislikeControlRef; - const visible = showLikeSuccessPopup || showDislikeSuccessPopup; - - if (!visible) { - return null; - } - - return ( - -

{t('success-title')}

-

{t('success-text')}

-
- ); - }, [ - showLikeSuccessPopup, - showDislikeSuccessPopup, - hideFeedbackPopups, - view, - getPopupPosition, - t, - ]); - - const renderDislikeVariantsList = useCallback(() => { - if (!dislikeVariants.length) { - return null; - } - - return ( -
- {dislikeVariants.map((variant, index) => ( - { - setFeedbackCheckboxes({ - ...feedbackCheckboxes, - [variant]: checked, - }); - }} - content={variant} - /> - ))} -
- ); - }, [dislikeVariants, feedbackCheckboxes]); - - const renderDislikeVariantsTextArea = useCallback(() => { - return ( -
-