apostrophe 3.18.0 → 3.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/.stylelintrc +2 -1
  2. package/CHANGELOG.md +23 -1
  3. package/lib/moog-require.js +7 -1
  4. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarLocale.vue +1 -1
  5. package/modules/@apostrophecms/asset/index.js +281 -24
  6. package/modules/@apostrophecms/asset/lib/webpack/apos/webpack.config.js +7 -0
  7. package/modules/@apostrophecms/asset/lib/webpack/src/webpack.config.js +7 -0
  8. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +10 -1
  9. package/modules/@apostrophecms/i18n/i18n/en.json +3 -0
  10. package/modules/@apostrophecms/i18n/i18n/es.json +1 -0
  11. package/modules/@apostrophecms/i18n/i18n/pt-BR.json +1 -0
  12. package/modules/@apostrophecms/i18n/i18n/sk.json +1 -0
  13. package/modules/@apostrophecms/image/ui/apos/components/AposImageCropper.vue +2 -2
  14. package/modules/@apostrophecms/image/ui/apos/components/AposImageRelationshipEditor.vue +5 -5
  15. package/modules/@apostrophecms/login/ui/apos/components/TheAposLoginHeader.vue +3 -2
  16. package/modules/@apostrophecms/schema/index.js +2 -1123
  17. package/modules/@apostrophecms/schema/lib/addFieldTypes.js +1139 -0
  18. package/modules/@apostrophecms/schema/ui/apos/components/AposInputDateAndTime.vue +115 -0
  19. package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +21 -1
  20. package/modules/@apostrophecms/ui/ui/apos/components/AposSpinner.vue +7 -7
  21. package/modules/@apostrophecms/ui/ui/apos/components/AposToggle.vue +68 -0
  22. package/package.json +2 -3
  23. package/test/assets.js +462 -4
  24. package/test/schemas.js +25 -0
  25. package/test-lib/test.js +12 -0
  26. package/test-lib/util.js +4 -3
package/.stylelintrc CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  plugins: [
3
- 'stylelint-order', 'stylelint-declaration-strict-value'
3
+ 'stylelint-order',
4
+ 'stylelint-declaration-strict-value'
4
5
  ],
5
6
  rules: {
6
7
  'color-hex-length': 'short',
package/CHANGELOG.md CHANGED
@@ -1,6 +1,27 @@
1
1
  # Changelog
2
2
 
3
- # 3.18.0 (2022-05-03)
3
+ ## 3.19.0
4
+
5
+ ### Adds
6
+
7
+ * New schema field type `dateAndTime` added. This schema field type saves in ISO8601 format, as UTC (Universal Coordinated Time), but is edited in a user-friendly way in the user's current time zone and locale.
8
+ * Webpack disk cache for better build performance in development and, if appropriately configured, production as well.
9
+ * In development, Webpack rebuilds the front end without the need to restart the Node.js process, yielding an additional speedup. To get this speedup for existing projects, see the `nodemonConfig` section of the latest `package.json` in [a3-boilerplate](https://github.com/apostrophecms/a3-boilerplate) for the new "ignore" rules you'll need to prevent nodemon from stopping the process and restarting.
10
+ * Added the new command line task `apostrophecms/asset:clear-cache` for clearing the webpack disk cache. This should be necessary only in rare cases where the configuration has changed in ways Apostrophe can't automatically detect.
11
+
12
+ ### 3.18.1
13
+
14
+ ### Adds
15
+
16
+ * Webpack cache for build performance in development and production mode.
17
+ * Add a watcher in development mode to rebuild the assets on changes in the same process.
18
+ * Add asset task `apostrophecms/asset:clear-cache` for force clearing the webpack build cache.
19
+
20
+ ### Fixes
21
+
22
+ * The admin UI now rebuilds properly in a development environment when new npm modules are installed in a multisite project (`apos.rootDir` differs from `apos.npmRootDir`).
23
+
24
+ ## 3.18.0 (2022-05-03)
4
25
 
5
26
  ### Adds
6
27
 
@@ -26,6 +47,7 @@
26
47
  * The `self.email` method of modules now correctly accepts a default `from` address configured for a specific module via the `from` subproperty of the `email` option to that module. Thanks to `chmdebeer` for pointing out the issue and the fix.
27
48
  * Fixes `_urls` not added on attachment fields when pieces API index is requested (#3643)
28
49
  * Fixes float field UI bug that transforms the value to integer when there is no field error and the first number after the decimal is `0`.
50
+ * The `nestedModuleSubdirs` feature no longer throws an error and interrupts startup if a project contains both `@apostrophecms/asset` and `asset`, which should be considered separate module names.
29
51
 
30
52
  ## 3.17.0 (2022-03-31)
31
53
 
@@ -5,6 +5,7 @@ const path = require('path');
5
5
  const glob = require('glob');
6
6
  const importFresh = require('import-fresh');
7
7
  const resolveFrom = require('resolve-from');
8
+ const regExpQuote = require('regexp-quote');
8
9
 
9
10
  module.exports = function(options) {
10
11
  const self = require('./moog')(options);
@@ -62,7 +63,12 @@ module.exports = function(options) {
62
63
  self._indexes = glob.sync(self.options.localModules + '/**/index.js');
63
64
  }
64
65
  const matches = self._indexes.filter(function(index) {
65
- return index.endsWith('/' + type + '/index.js');
66
+ // Double-check that we're not confusing "@apostrophecms/asset" with "asset" by
67
+ // making sure "type" is not preceded by an npm namespace folder. If type is itself namespaced
68
+ // this never comes up (because npm namespaces don't nest). The risk is that a legitimate
69
+ // project level module that happens to end with the same name as a namespaced module
70
+ // will be rejected as a duplicate when nestedModuleSubdirs is present
71
+ return index.endsWith('/' + type + '/index.js') && !index.match(new RegExp(`/@[^/]+/${regExpQuote(type)}/index\\.js$`));
66
72
  });
67
73
  if (matches.length > 1) {
68
74
  throw new Error('The module ' + type + ' appears in multiple locations:\n' + matches.join('\n'));
@@ -309,8 +309,8 @@ export default {
309
309
 
310
310
  .apos-available-locale {
311
311
  display: inline-block;
312
- font-size: 10px;
313
312
  color: var(--a-primary);
313
+ font-size: var(--a-type-small);
314
314
  }
315
315
 
316
316
  .apos-available-locale:not(:last-of-type) {
@@ -4,10 +4,13 @@ const Promise = require('bluebird');
4
4
  const webpackModule = require('webpack');
5
5
  const globalIcons = require('./lib/globalIcons');
6
6
  const path = require('path');
7
+ const util = require('util');
7
8
  const express = require('express');
8
9
  const { stripIndent } = require('common-tags');
9
10
  const { merge: webpackMerge } = require('webpack-merge');
10
11
  const cuid = require('cuid');
12
+ const chokidar = require('chokidar');
13
+ const _ = require('lodash');
11
14
  const {
12
15
  checkModulesWebpackConfig,
13
16
  getWebpackExtensions,
@@ -28,7 +31,13 @@ module.exports = {
28
31
  // If this option is true and process.env.NODE_ENV is not `production`,
29
32
  // the browser will refresh when the Apostrophe application
30
33
  // restarts. A useful companion to `nodemon`.
31
- refreshOnRestart: false
34
+ refreshOnRestart: false,
35
+ // If false no UI assets sources will be watched in development.
36
+ // This option has no effect in production (watch disabled).
37
+ watch: true,
38
+ // Miliseconds to wait between asset sources changes before
39
+ // performing a build.
40
+ watchDebounceMs: 1000
32
41
  },
33
42
 
34
43
  async init(self) {
@@ -47,26 +56,17 @@ module.exports = {
47
56
  self.extraBundles = fillExtraBundles(verifiedBundles);
48
57
  self.webpackExtensions = extensions;
49
58
  self.verifiedBundles = verifiedBundles;
59
+ self.buildWatcherEnable = process.env.APOS_ASSET_WATCH !== '0' && self.options.watch !== false;
60
+ self.buildWatcherDebounceMs = parseInt(self.options.watchDebounceMs || 1000, 10);
61
+ self.buildWatcher = null;
50
62
  },
51
63
  handlers (self) {
52
64
  return {
53
65
  'apostrophe:modulesRegistered': {
54
66
  async runUiBuildTask() {
55
- if (
56
- // Do not automatically build the UI if we're starting from a task
57
- !self.apos.isTask() &&
58
- // Or if we're in production
59
- process.env.NODE_ENV !== 'production' &&
60
- // Or if we've set an app option to skip the auto build
61
- self.apos.options.autoBuild !== false
62
- ) {
63
-
64
- checkModulesWebpackConfig(self.apos.modules, self.apos.task.getReq().t);
65
- // If starting up normally, run the build task, checking if we
66
- // really need to update the apos build
67
- await self.apos.task.invoke('@apostrophecms/asset:build', {
68
- 'check-apos-build': true
69
- });
67
+ const ran = await self.autorunUiBuildTask();
68
+ if (ran) {
69
+ await self.watchUiAndRebuild();
70
70
  }
71
71
  },
72
72
  injectAssetsPlaceholders() {
@@ -79,6 +79,12 @@ module.exports = {
79
79
  if (self.uploadfs && (self.uploadfs !== self.apos.uploadfs)) {
80
80
  await Promise.promisify(self.uploadfs.destroy)();
81
81
  }
82
+ },
83
+ async destroyBuildWatcher() {
84
+ if (self.buildWatcher) {
85
+ await self.buildWatcher.close();
86
+ self.buildWatcher = null;
87
+ }
82
88
  }
83
89
  }
84
90
  };
@@ -111,6 +117,8 @@ module.exports = {
111
117
  usage: 'Build Apostrophe frontend CSS and JS bundles',
112
118
  afterModuleInit: true,
113
119
  async task(argv) {
120
+ // The lock could become huge, cache it, see computeCacheMeta()
121
+ let packageLockContentCached;
114
122
  const req = self.apos.task.getReq();
115
123
  const namespace = self.getNamespace();
116
124
  const buildDir = `${self.apos.rootDir}/apos-build/${namespace}`;
@@ -317,7 +325,13 @@ module.exports = {
317
325
  ? webpackMerge(webpackInstanceConfig, ...Object.values(self.webpackExtensions))
318
326
  : webpackInstanceConfig;
319
327
 
328
+ // Inject the cache location at the end - we need the merged
329
+ const cacheMeta = await computeCacheMeta(name, webpackInstanceConfigMerged);
330
+ webpackInstanceConfigMerged.cache.cacheLocation = cacheMeta.location;
331
+
320
332
  const result = await webpack(webpackInstanceConfigMerged);
333
+ await writeCacheMeta(name, cacheMeta);
334
+
321
335
  if (result.compilation.errors.length) {
322
336
  // Throwing a string is appropriate in a command line task
323
337
  throw cleanErrors(result.toString('errors'));
@@ -632,11 +646,9 @@ module.exports = {
632
646
  async function lockFileIsNewer(name) {
633
647
  const timestamp = fs.readFileSync(`${bundleDir}/${name}-build-timestamp.txt`, 'utf8');
634
648
  let pkgStats;
635
-
636
- if (await fs.pathExists(`${self.apos.rootDir}/package-lock.json`)) {
637
- pkgStats = await fs.stat(`${self.apos.rootDir}/package-lock.json`);
638
- } else if (await fs.pathExists(`${self.apos.rootDir}/yarn.lock`)) {
639
- pkgStats = await fs.stat(`${self.apos.rootDir}/yarn.lock`);
649
+ const packageLock = await findPackageLock();
650
+ if (packageLock) {
651
+ pkgStats = await fs.stat(packageLock);
640
652
  }
641
653
 
642
654
  const pkgTimestamp = pkgStats && pkgStats.mtimeMs;
@@ -644,6 +656,18 @@ module.exports = {
644
656
  return pkgTimestamp > parseInt(timestamp);
645
657
  }
646
658
 
659
+ async function findPackageLock() {
660
+ const packageLockPath = path.join(self.apos.npmRootDir, 'package-lock.json');
661
+ const yarnPath = path.join(self.apos.npmRootDir, 'yarn.lock');
662
+ if (await fs.pathExists(packageLockPath)) {
663
+ return packageLockPath;
664
+ } else if (await fs.pathExists(yarnPath)) {
665
+ return yarnPath;
666
+ } else {
667
+ return false;
668
+ }
669
+ }
670
+
647
671
  function getComponentName(component, { enumerateImports } = {}, i) {
648
672
  return path
649
673
  .basename(component)
@@ -657,6 +681,93 @@ module.exports = {
657
681
  // confused by this
658
682
  return errors.replace(/(ERROR in[\s\S]*?Module parse failed[\s\S]*)You may need an appropriate loader.*/, '$1');
659
683
  }
684
+
685
+ // A (CPU intensive) webpack cache helper to compute a hash and build paths.
686
+ // Cache the result when possible.
687
+ // The base cache path is by default `data/temp/webpack-cache`
688
+ // but it can be overridden by an APOS_ASSET_CACHE environment.
689
+ // In order to compute an accurate hash, this helper needs
690
+ // the final, merged webpack configuration.
691
+ async function computeCacheMeta(name, webpackConfig) {
692
+ const cacheBase = self.getCacheBasePath();
693
+
694
+ if (!packageLockContentCached) {
695
+ const packageLock = await findPackageLock();
696
+ if (packageLock === false) {
697
+ // this should happen only when testing and
698
+ // we don't want to break all non-core module tests
699
+ packageLockContentCached = 'none';
700
+ } else {
701
+ packageLockContentCached = await fs.readFile(packageLock, 'utf8');
702
+ }
703
+ }
704
+
705
+ // Plugins can output timestamps or other random information as
706
+ // configuration (e.g. StylelintWebpackPlugin). As we don't have
707
+ // control over plugins, we need to remove their configuration values.
708
+ // We keep only the plugin name and config keys sorted list.
709
+ // Additionally plugins are sorted by their constructor names.
710
+ // A shallow clone is enough to avoid modification of the original
711
+ // config.
712
+ const config = { ...webpackConfig };
713
+ config.plugins = (config.plugins || []).map(p => {
714
+ const result = [];
715
+ if (p.constructor && p.constructor.name) {
716
+ result.push(p.constructor.name);
717
+ }
718
+ const keys = Object.keys(p);
719
+ keys.sort();
720
+ result.push(...keys);
721
+ return result;
722
+ });
723
+ config.plugins.sort((a, b) => (a[0] || '').localeCompare(b[0]));
724
+ const configString = util.inspect(config, {
725
+ depth: 10,
726
+ compact: true,
727
+ breakLength: Infinity
728
+ });
729
+ const hash = self.apos.util.md5(
730
+ `${self.getNamespace()}:${name}:${packageLockContentCached}:${configString}`
731
+ );
732
+ const location = path.resolve(cacheBase, hash);
733
+
734
+ return {
735
+ base: cacheBase,
736
+ hash,
737
+ location
738
+ };
739
+ }
740
+
741
+ // Add .apos, useful for debugging, testing and cache invalidation.
742
+ // It also keeps in sync the modified time of the cache folder.
743
+ async function writeCacheMeta(name, cacheMeta) {
744
+ try {
745
+ const cachePath = path.join(cacheMeta.location, '.apos');
746
+ const lastModified = new Date();
747
+ await fs.writeFile(
748
+ cachePath,
749
+ `${lastModified.toISOString()} ${self.getNamespace()}:${name}`,
750
+ 'utf8'
751
+ );
752
+ // should be the same as the meta
753
+ await fs.utimes(cachePath, lastModified, lastModified);
754
+ } catch (e) {
755
+ // Build probably failed, path is missing, ignore
756
+ }
757
+ }
758
+ }
759
+ },
760
+
761
+ 'clear-cache': {
762
+ usage: 'Clear build cache',
763
+ afterModuleInit: true,
764
+ async task(argv) {
765
+ const cacheBaseDir = self.getCacheBasePath();
766
+
767
+ await fs.emptyDir(cacheBaseDir);
768
+ self.apos.util.log(
769
+ self.apos.task.getReq().t('apostrophe:assetWebpackCacheCleared')
770
+ );
660
771
  }
661
772
  }
662
773
  };
@@ -757,6 +868,144 @@ module.exports = {
757
868
  return `/apos-frontend/${namespace}`;
758
869
  }
759
870
  },
871
+ getCacheBasePath() {
872
+ return process.env.APOS_ASSET_CACHE ||
873
+ path.join(self.apos.rootDir, 'data/temp/webpack-cache');
874
+ },
875
+ // Run build task automatically when appropriate
876
+ async autorunUiBuildTask() {
877
+ if (
878
+ // Do not automatically build the UI if we're starting from a task
879
+ !self.apos.isTask() &&
880
+ // Or if we're in production
881
+ process.env.NODE_ENV !== 'production' &&
882
+ // Or if we've set an app option to skip the auto build
883
+ self.apos.options.autoBuild !== false
884
+ ) {
885
+
886
+ checkModulesWebpackConfig(self.apos.modules, self.apos.task.getReq().t);
887
+ // If starting up normally, run the build task, checking if we
888
+ // really need to update the apos build
889
+ await self.apos.task.invoke('@apostrophecms/asset:build', {
890
+ 'check-apos-build': true
891
+ });
892
+ return true;
893
+ }
894
+ return false;
895
+ },
896
+ // Start watching assets from `modules/` and
897
+ // every symlinked package found in `node_modules/`.
898
+ // `rebuildCallback` is invoked with queue length argument
899
+ // on actual build attempt only.
900
+ // It's there mainly for testing and debugging purposes.
901
+ async watchUiAndRebuild(rebuildCallback) {
902
+ if (!self.buildWatcherEnable || self.buildWatcher) {
903
+ return;
904
+ }
905
+ const rootDir = self.apos.rootDir;
906
+ // chokidar may invoke ready event multiple times,
907
+ // we want one "watch enabled" message.
908
+ let loggedOnce = false;
909
+ const logOnce = (...msg) => {
910
+ if (!loggedOnce) {
911
+ self.apos.util.log(...msg);
912
+ loggedOnce = true;
913
+ }
914
+ };
915
+ const error = self.apos.util.error;
916
+ const queue = [];
917
+ let queueLength = 0;
918
+ let queueRunning = false;
919
+
920
+ const debounceRebuild = _.debounce(chain, self.buildWatcherDebounceMs, {
921
+ leading: false,
922
+ trailing: true
923
+ });
924
+
925
+ const symLinkModules = await findSymlinks();
926
+ const watchDirs = [
927
+ './modules/**/ui/apos/**',
928
+ './modules/**/ui/src/**',
929
+ './modules/**/ui/public/**',
930
+ ...symLinkModules.reduce(
931
+ (prev, m) => [
932
+ ...prev,
933
+ `./node_modules/${m}/modules/**/ui/apos/**`,
934
+ `./node_modules/${m}/modules/**/ui/src/**`,
935
+ `./node_modules/${m}/modules/**/ui/public/**`
936
+ ],
937
+ []
938
+ )
939
+ ];
940
+ self.buildWatcher = chokidar.watch(watchDirs, {
941
+ cwd: rootDir,
942
+ ignoreInitial: true
943
+ });
944
+
945
+ self.buildWatcher
946
+ .on('add', debounceRebuild)
947
+ .on('change', debounceRebuild)
948
+ .on('unlink', debounceRebuild)
949
+ .on('addDir', debounceRebuild)
950
+ .on('unlinkDir', debounceRebuild)
951
+ .on('error', e => error(`Watcher error: ${e}`))
952
+ .on('ready', () => logOnce(
953
+ self.apos.task.getReq().t('apostrophe:assetBuildWatchStarted')
954
+ ));
955
+
956
+ async function rebuild() {
957
+ await self.autorunUiBuildTask();
958
+ self.restartId = self.apos.util.generateId();
959
+ if (typeof rebuildCallback === 'function') {
960
+ rebuildCallback(queueLength);
961
+ };
962
+ };
963
+
964
+ // Simple, capped, self-exhausting queue implementation.
965
+ function enqueue(fn) {
966
+ if (queueLength === 2) {
967
+ return;
968
+ }
969
+ queue.push(fn);
970
+ queueLength++;
971
+ };
972
+ async function dequeue() {
973
+ if (!queueLength) {
974
+ queueRunning = false;
975
+ return;
976
+ }
977
+ queueRunning = true;
978
+ await queue.pop()();
979
+ queueLength--;
980
+ await dequeue();
981
+ }
982
+ async function chain(f) {
983
+ enqueue(rebuild);
984
+ if (!queueRunning) {
985
+ await dequeue();
986
+ }
987
+ }
988
+
989
+ // Find all symlinks in node modules.
990
+ // This would find both `module-name` and `@company/module-name`
991
+ // package symlinks
992
+ async function findSymlinks(sub = '') {
993
+ let result = [];
994
+ const handle = await fs.promises.opendir(path.join(rootDir, 'node_modules', sub));
995
+ let mod = await handle.read();
996
+ while (mod) {
997
+ if (mod.isSymbolicLink()) {
998
+ result.push(sub + mod.name);
999
+ } else if (!sub && mod.name.startsWith('@')) {
1000
+ const dres = await findSymlinks(`${mod.name}/`);
1001
+ result = [ ...result, ...dres ];
1002
+ }
1003
+ mod = await handle.read();
1004
+ }
1005
+ await handle.close();
1006
+ return result;
1007
+ }
1008
+ },
760
1009
  // An implementation method that you should not need to call.
761
1010
  // Sets a predetermined configuration for the frontend builds.
762
1011
  // If you are trying to enable IE11 support for ui/src, use the
@@ -903,9 +1152,17 @@ module.exports = {
903
1152
  // Long polling: keep the logs quiet by responding slowly, except the
904
1153
  // first time. If we restart, the request will fail immediately,
905
1154
  // and the client will know to try again with `fast`. The client also
906
- // uses `fast` the first time
907
- if (!req.query.fast) {
908
- await Promise.delay(30000);
1155
+ // uses `fast` the first time.
1156
+ if (req.query.fast) {
1157
+ return self.restartId;
1158
+ }
1159
+ // Long polling will be interrupted if restartId changes.
1160
+ let delay = 30000;
1161
+ const step = 300;
1162
+ const oldRestartId = self.restartId;
1163
+ while (delay > 0 && oldRestartId === self.restartId) {
1164
+ delay -= step;
1165
+ await Promise.delay(step);
909
1166
  }
910
1167
  return self.restartId;
911
1168
  }
@@ -33,6 +33,13 @@ module.exports = ({
33
33
  path: outputPath,
34
34
  filename: outputFilename
35
35
  },
36
+ // cacheLocation will be added dynamically later
37
+ cache: {
38
+ type: 'filesystem',
39
+ buildDependencies: {
40
+ config: [ __filename ]
41
+ }
42
+ },
36
43
  // we could extend this with aliases for other apostrophe modules
37
44
  // at a later date if needed
38
45
  resolveLoader: {
@@ -50,6 +50,13 @@ module.exports = ({
50
50
  : `[name]-${moduleName}-bundle.js`;
51
51
  }
52
52
  },
53
+ // cacheLocation will be added dynamically later
54
+ cache: {
55
+ type: 'filesystem',
56
+ buildDependencies: {
57
+ config: [ __filename ]
58
+ }
59
+ },
53
60
  resolveLoader: {
54
61
  extensions: [ '*', '.js' ],
55
62
  // Make sure css-loader and postcss-loader can always be found, even
@@ -125,6 +125,11 @@ export default {
125
125
  AposAdvisoryLockMixin,
126
126
  AposArchiveMixin
127
127
  ],
128
+ provide () {
129
+ return {
130
+ originalDoc: this.originalDoc
131
+ };
132
+ },
128
133
  props: {
129
134
  moduleName: {
130
135
  type: String,
@@ -153,6 +158,9 @@ export default {
153
158
  },
154
159
  triggerValidation: false,
155
160
  original: null,
161
+ originalDoc: {
162
+ ref: null
163
+ },
156
164
  published: null,
157
165
  errorCount: 0,
158
166
  restoreOnly: false,
@@ -363,7 +371,8 @@ export default {
363
371
  manuallyPublished() {
364
372
  this.saveMenu = this.computeSaveMenu();
365
373
  },
366
- original() {
374
+ original(newVal) {
375
+ this.originalDoc.ref = newVal;
367
376
  this.saveMenu = this.computeSaveMenu();
368
377
  },
369
378
  tabs() {
@@ -35,6 +35,9 @@
35
35
  "assetTypeBuilding": "🧑‍💻 Building the {{ label }}...",
36
36
  "assetWebpackConfigWarning": "⚠️ In the module {{ module }}, your webpack config is incorrect. It must be an object and should contain only two properties extensions and bundles.",
37
37
  "assetWebpackBundlesWarning": "⚠️ In the module {{ module }} your webpack config is incorrect. Each bundle can only have one property 'templates' that must be an array of strings.",
38
+ "assetWebpackCacheCleared": "Build cache cleared.",
39
+ "assetBuildWatchStarted": "Watching for UI changes...",
40
+ "at": "at",
38
41
  "back": "Back",
39
42
  "backToHome": "Back to Home",
40
43
  "basics": "Basics",
@@ -25,6 +25,7 @@
25
25
  "archivingWillLoseDraftChanges": "Cambios al borrador de este {{ type }} sin publicar serán eliminados permanentemente.",
26
26
  "archivingWillUnpublish": "Esto también despublicará el {{ type }}.",
27
27
  "areYouSure": "¿Está Seguro?",
28
+ "at": "a las",
28
29
  "aspectRatio": "Relación de aspecto",
29
30
  "aspectRatioFree": "Libre",
30
31
  "aspectRatioWarning": "La relación de aspecto no se puede cambiar para este widget",
@@ -30,6 +30,7 @@
30
30
  "aspectRatioWarning": "A proporção não pode ser alterada para este widget",
31
31
  "assetTypeBuildComplete": "👍 {{ label }} completado!",
32
32
  "assetTypeBuilding": "🧑‍💻 Fazendo o build: {{ label }}...",
33
+ "at": "às",
33
34
  "back": "Voltar",
34
35
  "backToHome": "Voltar ao início",
35
36
  "basics": "Básico",
@@ -33,6 +33,7 @@
33
33
  "aspectRatioWarning": "Pre túto miniaplikáciu nie je možné zmeniť pomer strán",
34
34
  "assetTypeBuildComplete": "👍 {{ label }} je kompletný!",
35
35
  "assetTypeBuilding": "🧑‍💻 Budovanie {{ label }}...",
36
+ "at": "o",
36
37
  "back": "Naspäť",
37
38
  "backToHome": "Naspäť na domovskú stránku",
38
39
  "basics": "Základy",
@@ -428,15 +428,15 @@ export default {
428
428
  // flex-grow: 0;
429
429
 
430
430
  .apos-image-focal-point {
431
+ z-index: $z-index-default;
431
432
  position: absolute;
432
- z-index: 1;
433
433
  width: 10px;
434
434
  height: 10px;
435
435
  border-radius: 50%;
436
436
  border: 1px solid var(--a-white);
437
437
  background-color: var(--a-primary);
438
438
  box-shadow: 0 0 4px var(--a-black);
439
- transition: left 0.1s ease, top 0.1s ease;
439
+ transition: left 0.15s ease, top 0.15s ease;
440
440
  cursor: grab;
441
441
  }
442
442
  }
@@ -427,7 +427,7 @@ export default {
427
427
  .apos-schema {
428
428
  margin: 30px 15px 0;
429
429
 
430
- .apos-field {
430
+ .apos-field {
431
431
  margin-bottom: 20px;
432
432
 
433
433
  &__label {
@@ -448,9 +448,9 @@ export default {
448
448
  flex-direction: row;
449
449
 
450
450
  .apos-field {
451
+ position: relative;
451
452
  display: flex;
452
453
  align-items: center;
453
- position: relative;
454
454
  flex-grow: 1;
455
455
 
456
456
  &:first-child {
@@ -472,7 +472,7 @@ export default {
472
472
  }
473
473
  }
474
474
 
475
- .apos-input[type="number"] {
475
+ .apos-input[type='number'] {
476
476
  padding-right: 5px;
477
477
  }
478
478
 
@@ -495,16 +495,16 @@ export default {
495
495
  }
496
496
 
497
497
  .apos-field__label--aligned {
498
- margin: 0
498
+ margin: 0;
499
499
  }
500
500
 
501
501
  .apos-image-cropper__container {
502
502
  display: flex;
503
503
  justify-content: center;
504
504
  align-items: center;
505
- margin: 30px 10%;
506
505
  // We remove the modal's paddings - header height - container margin
507
506
  height: calc(100vh - 40px - 75px - 60px);
507
+ margin: 30px 10%;
508
508
  box-sizing: border-box;
509
509
  }
510
510
  </style>
@@ -89,15 +89,16 @@ export default {
89
89
 
90
90
  &__header--tiny {
91
91
  flex-direction: row;
92
+ // stylelint-disable-next-line scale-unlimited/declaration-strict-value
92
93
  color: #F8F9FA;
93
94
 
94
95
  .apos-login__project {
95
- opacity: 0.7
96
+ opacity: 0.7;
96
97
  }
97
98
 
98
99
  .apos-login__project-name {
100
+ // stylelint-disable-next-line scale-unlimited/declaration-strict-value
99
101
  font-size: 21px;
100
-
101
102
  }
102
103
 
103
104
  .apos-login__project-env {