Skip to content

Commit

Permalink
Merge pull request #366 from uploadcare/ui-rework
Browse files Browse the repository at this point in the history
UI rework
  • Loading branch information
nd0ut authored Jan 31, 2023
2 parents b39e402 + 6b064c8 commit 9666459
Show file tree
Hide file tree
Showing 96 changed files with 2,188 additions and 1,436 deletions.
13 changes: 10 additions & 3 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
{
"parser": "babel",
"plugins": ["prettier-plugin-jsdoc"],
"singleQuote": true,
"tabWidth": 2,
"semi": true,
"arrowParens": "always",
"printWidth": 120
"printWidth": 120,
"overrides": [
{
"files": "*.js",
"options": {
"parser": "babel",
"plugins": ["prettier-plugin-jsdoc"]
}
}
]
}
12 changes: 12 additions & 0 deletions .stylelintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ module.exports = {
'color-function-notation': null,
'order/properties-order': null,
'rule-empty-line-before': null,
'at-rule-no-unknown': [
true,
{
ignoreAtRules: ['container'],
},
],
'property-no-unknown': [
true,
{
ignoreProperties: ['container-type', 'container-name'],
},
],
},
overrides: [
{
Expand Down
28 changes: 16 additions & 12 deletions abstract/ActivityBlock.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Modal } from '../blocks/Modal/Modal.js';
import { debounce } from '../blocks/utils/debounce.js';
import { Block } from './Block.js';
import { activityBlockCtx } from './CTX.js';
Expand All @@ -6,7 +7,8 @@ const ACTIVE_ATTR = 'active';
const ACTIVE_PROP = '___ACTIVITY_IS_ACTIVE___';

export class ActivityBlock extends Block {
ctxInit = activityBlockCtx();
historyTracked = false;
ctxInit = activityBlockCtx(this);

_debouncedHistoryFlush = debounce(this._historyFlush.bind(this), 10);

Expand All @@ -33,10 +35,10 @@ export class ActivityBlock extends Block {
actDesc?.deactivateCallback?.();
// console.log(`Activity "${this.activityType}" deactivated`);
} else if (this.activityType === val && !this[ACTIVE_PROP]) {
this.$['*historyBack'] = this.historyBack.bind(this);
/** @private */
this[ACTIVE_PROP] = true;
this.setAttribute(ACTIVE_ATTR, '');
this.setForCtxTarget('lr-modal', '*modalCloseCallback', actDesc?.modalCloseCallback);
actDesc?.activateCallback?.();
// console.log(`Activity "${this.activityType}" activated`);

Expand All @@ -56,7 +58,9 @@ export class ActivityBlock extends Block {
if (history.length > 10) {
history = history.slice(history.length - 11, history.length - 1);
}
history.push(this.activityType);
if (this.historyTracked) {
history.push(this.activityType);
}
this.$['*history'] = history;
}
}
Expand All @@ -76,18 +80,16 @@ export class ActivityBlock extends Block {
* @param {Object} [options]
* @param {() => void} [options.onActivate]
* @param {() => void} [options.onDeactivate]
* @param {() => void} [options.onClose]
*/
registerActivity(name, options) {
const { onActivate, onDeactivate, onClose } = options;
registerActivity(name, options = {}) {
const { onActivate, onDeactivate } = options;
if (!ActivityBlock._activityRegistry) {
ActivityBlock._activityRegistry = Object.create(null);
}
let actKey = this.ctxName + name;
ActivityBlock._activityRegistry[actKey] = {
activateCallback: onActivate,
deactivateCallback: onDeactivate,
modalCloseCallback: onClose,
};
}

Expand All @@ -109,12 +111,14 @@ export class ActivityBlock extends Block {
/** @type {String[]} */
let history = this.$['*history'];
if (history) {
history.pop();
let prevActivity = history.pop();
this.$['*currentActivity'] = prevActivity;
let nextActivity = history.pop();
while (nextActivity === this.activityType) {
nextActivity = history.pop();
}
this.$['*currentActivity'] = nextActivity;
this.$['*history'] = history;
if (!prevActivity) {
this.setForCtxTarget('lr-modal', '*modalActive', false);
if (!nextActivity) {
this.setForCtxTarget(Modal.StateConsumerScope, '*modalActive', false);
}
}
}
Expand Down
70 changes: 48 additions & 22 deletions abstract/Block.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { BaseComponent, Data } from '@symbiotejs/symbiote';
import { applyTemplateData } from '../utils/applyTemplateData.js';
import { applyTemplateData, getPluralObjects } from '../utils/template-utils.js';
import { l10nProcessor } from './l10nProcessor.js';
import { blockCtx } from './CTX.js';
import { createWindowHeightTracker, getIsWindowHeightTracked } from '../utils/createWindowHeightTracker.js';

const TAG_PREFIX = 'lr-';

export class Block extends BaseComponent {
static StateConsumerScope = null;
allowCustomTemplate = true;

ctxInit = blockCtx();
Expand All @@ -18,11 +19,32 @@ export class Block extends BaseComponent {
* @returns {String}
*/
l10n(str, variables = {}) {
if (!str) {
return '';
}
let template = this.getCssData('--l10n-' + str, true) || str;
let pluralObjects = getPluralObjects(template);
for (let pluralObject of pluralObjects) {
variables[pluralObject.variable] = this.pluralize(
pluralObject.pluralKey,
Number(variables[pluralObject.countVariable])
);
}
let result = applyTemplateData(template, variables);
return result;
}

/**
* @param {string} key
* @param {number} count
* @returns {string}
*/
pluralize(key, count) {
const locale = this.l10n('locale-name') || 'en-US';
const pluralForm = new Intl.PluralRules(locale).select(count);
return this.l10n(`${key}__${pluralForm}`);
}

constructor() {
super();
/** @type {String} */
Expand Down Expand Up @@ -55,26 +77,40 @@ export class Block extends BaseComponent {
}

/**
* @param {String} targetTagName
* @param {(block: Block) => boolean} callback
* @returns {Boolean}
*/
checkCtxTarget(targetTagName) {
findBlockInCtx(callback) {
/** @type {Set} */
let registry = this.$['*ctxTargetsRegistry'];
return registry?.has(targetTagName);
let blocksRegistry = this.$['*blocksRegistry'];
for (let block of blocksRegistry) {
if (callback(block)) {
return true;
}
}
return false;
}

/**
* @param {String} targetTagName
* @param {String} consumerScope
* @param {String} prop
* @param {any} newVal
*/
setForCtxTarget(targetTagName, prop, newVal) {
if (this.checkCtxTarget(targetTagName)) {
setForCtxTarget(consumerScope, prop, newVal) {
if (this.findBlockInCtx((b) => /** @type {typeof Block} */ (b.constructor).StateConsumerScope === consumerScope)) {
this.$[prop] = newVal;
}
}

/** @param {String} activityType */
setActivity(activityType) {
if (this.findBlockInCtx((b) => b.activityType === activityType)) {
this.$['*currentActivity'] = activityType;
return;
}
console.warn(`Activity type "${activityType}" not found in the context`);
}

connectedCallback() {
if (!getIsWindowHeightTracked()) {
this._destroyInnerHeightTracker = createWindowHeightTracker();
Expand All @@ -98,23 +134,13 @@ export class Block extends BaseComponent {
}

initCallback() {
let tagName = this.constructor['is'];
let registry = this.$['*ctxTargetsRegistry'];
let counter = registry.has(tagName) ? registry.get(tagName) + 1 : 1;
registry.set(tagName, counter);
this.$['*ctxTargetsRegistry'] = registry;
let blocksRegistry = this.$['*blocksRegistry'];
blocksRegistry.add(this);
}

destroyCallback() {
let tagName = this.constructor['is'];
let registry = this.$['*ctxTargetsRegistry'];
let newCount = registry.has(registry) ? registry.get(tagName) - 1 : 0;
if (newCount === 0) {
registry.delete(tagName);
} else {
registry.set(tagName, newCount);
}
this.$['*ctxTargetsRegistry'] = registry;
let blocksRegistry = this.$['*blocksRegistry'];
blocksRegistry.delete(this);
}

/**
Expand Down
18 changes: 12 additions & 6 deletions abstract/CTX.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
export const blockCtx = () => ({
'*ctxTargetsRegistry': new Map(),
/** @type {Set<import('./Block').Block>} */
'*blocksRegistry': new Set(),
});

export const activityBlockCtx = () => ({
export const activityBlockCtx = (fnCtx) => ({
...blockCtx(),
'*currentActivity': '',
'*currentActivityParams': {},
'*history': [],
'*activityCaption': '',
'*activityIcon': '',
'*historyBack': null,
'*closeModal': () => {
fnCtx.set$({
'*modalActive': false,
'*currentActivity': '',
});
},
});

export const uploaderBlockCtx = () => ({
...activityBlockCtx(),
export const uploaderBlockCtx = (fnCtx) => ({
...activityBlockCtx(fnCtx),
'*commonProgress': 0,
'*uploadList': [],
'*outputData': null,
Expand Down
11 changes: 10 additions & 1 deletion abstract/SolutionBlock.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ import { ShadowWrapper } from '../blocks/ShadowWrapper/ShadowWrapper.js';
import { uploaderBlockCtx } from './CTX.js';

export class SolutionBlock extends ShadowWrapper {
ctxInit = uploaderBlockCtx();
ctxInit = uploaderBlockCtx(this);
ctxOwner = true;
_template = null;

static set template(value) {
this._template = value + /** HTML */ `<slot></slot>`;
}

static get template() {
return this._template;
}
}
28 changes: 19 additions & 9 deletions abstract/UploaderBlock.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import { customUserAgent } from '../blocks/utils/userAgent.js';
import { TypedCollection } from './TypedCollection.js';
import { uploaderBlockCtx } from './CTX.js';
import { EVENT_TYPES, EventData, EventManager } from './EventManager.js';
import { Modal } from '../blocks/Modal/Modal.js';

export class UploaderBlock extends ActivityBlock {
ctxInit = uploaderBlockCtx();
ctxInit = uploaderBlockCtx(this);

/** @private */
__initialUploadMetadata = null;
Expand Down Expand Up @@ -42,9 +43,10 @@ export class UploaderBlock extends ActivityBlock {
destroyCallback() {
super.destroyCallback();

let registry = this.$['*ctxTargetsRegistry'];
if (registry?.size === 0) {
let blocksRegistry = this.$['*blocksRegistry'];
if (blocksRegistry.has(this)) {
this.uploadCollection.unobserve(this._handleCollectionUpdate);
blocksRegistry.delete(this);
}
}

Expand All @@ -61,11 +63,13 @@ export class UploaderBlock extends ActivityBlock {
});
}

openSystemDialog() {
/** @param {{ captureCamera?: boolean }} options */
openSystemDialog(options = {}) {
let accept = mergeFileTypes([
this.getCssData('--cfg-accept'),
...(this.getCssData('--cfg-img-only') ? IMAGE_ACCEPT_LIST : []),
]).join(',');

if (this.getCssData('--cfg-accept') && !!this.getCssData('--cfg-img-only')) {
console.warn(
'There could be a mistake.\n' +
Expand All @@ -76,12 +80,18 @@ export class UploaderBlock extends ActivityBlock {
this.fileInput = document.createElement('input');
this.fileInput.type = 'file';
this.fileInput.multiple = !!this.getCssData('--cfg-multiple');
this.fileInput.accept = accept;
if (options.captureCamera) {
this.fileInput.capture = '';
this.fileInput.accept = IMAGE_ACCEPT_LIST.join(',');
} else {
this.fileInput.accept = accept;
}
this.fileInput.dispatchEvent(new MouseEvent('click'));
this.fileInput.onchange = () => {
this.addFiles([...this.fileInput['files']]);
// To call uploadTrigger UploadList should draw file items first:
this.$['*currentActivity'] = ActivityBlock.activities.UPLOAD_LIST;
this.setForCtxTarget(Modal.StateConsumerScope, '*modalActive', true);
this.fileInput['value'] = '';
this.fileInput = null;
};
Expand All @@ -106,7 +116,7 @@ export class UploaderBlock extends ActivityBlock {
this.set$({
'*currentActivity': ActivityBlock.activities.UPLOAD_LIST,
});
this.setForCtxTarget('lr-modal', '*modalActive', true);
this.setForCtxTarget(Modal.StateConsumerScope, '*modalActive', true);
} else {
if (this.sourceList?.length === 1) {
let srcKey = this.sourceList[0];
Expand All @@ -125,14 +135,14 @@ export class UploaderBlock extends ActivityBlock {
} else {
this.$['*currentActivity'] = srcKey;
}
this.setForCtxTarget('lr-modal', '*modalActive', true);
this.setForCtxTarget(Modal.StateConsumerScope, '*modalActive', true);
}
} else {
// Multiple sources case:
this.set$({
'*currentActivity': ActivityBlock.activities.START_FROM,
});
this.setForCtxTarget('lr-modal', '*modalActive', true);
this.setForCtxTarget(Modal.StateConsumerScope, '*modalActive', true);
}
}
}
Expand All @@ -143,7 +153,7 @@ export class UploaderBlock extends ActivityBlock {
'*history': this.doneActivity ? [this.doneActivity] : [],
});
if (!this.$['*currentActivity']) {
this.setForCtxTarget('lr-modal', '*modalActive', false);
this.setForCtxTarget(Modal.StateConsumerScope, '*modalActive', false);
}
}

Expand Down
Loading

0 comments on commit 9666459

Please sign in to comment.