Browse Source

feat: init datasources into style

ci/dependabot
danstarns 2 years ago
parent
commit
121b839088
  1. 14
      src/data_sources/model/StyleDataVariable.ts
  2. 2
      src/dom_components/model/Component.ts
  3. 2
      src/dom_components/model/types.ts
  4. 93
      src/domain_abstract/model/StyleableModel.ts
  5. 2
      src/style_manager/index.ts
  6. 28
      src/style_manager/model/PropertyComposite.ts
  7. 40
      test/specs/data_sources/index.ts

14
src/data_sources/model/StyleDataVariable.ts

@ -0,0 +1,14 @@
import { Model, ObjectAny } from '../../common';
export const type = 'data-variable-css';
interface StyleDataVariableProps extends ObjectAny {
type: string;
path: string;
value: string;
}
export default class StyleDataVariable<T extends StyleDataVariableProps = StyleDataVariableProps> extends Model<T> {
constructor(props: T, opts = {}) {
super(props, opts);
}
}

2
src/dom_components/model/Component.ts

@ -698,7 +698,7 @@ export default class Component extends StyleableModel<ComponentProperties> {
if (avoidInline(em) && !opt.temporary && !opts.inline) { if (avoidInline(em) && !opt.temporary && !opts.inline) {
const style = this.get('style') || {}; const style = this.get('style') || {};
prop = isString(prop) ? this.parseStyle(prop) : prop; prop = isString(prop) ? this.parseStyle(prop) : prop;
prop = { ...prop, ...style }; prop = { ...prop, ...(style as any) };
const state = em.get('state'); const state = em.get('state');
const cc = em.Css; const cc = em.Css;
const propOrig = this.getStyle(opts); const propOrig = this.getStyle(opts);

2
src/dom_components/model/types.ts

@ -175,7 +175,7 @@ export interface ComponentProperties {
* Component default style, eg. `{ width: '100px', height: '100px', 'background-color': 'red' }` * Component default style, eg. `{ width: '100px', height: '100px', 'background-color': 'red' }`
* @default {} * @default {}
*/ */
style?: any; style?: string | Record<string, any | { type: 'data-variable'; path: string; value: string }>;
/** /**
* Component related styles, eg. `.my-component-class { color: red }` * Component related styles, eg. `.my-component-class { color: red }`
* @default '' * @default ''

93
src/domain_abstract/model/StyleableModel.ts

@ -2,9 +2,20 @@ import { isArray, isString, keys } from 'underscore';
import { Model, ObjectAny, ObjectHash, SetOptions } from '../../common'; import { Model, ObjectAny, ObjectHash, SetOptions } from '../../common';
import ParserHtml from '../../parser/model/ParserHtml'; import ParserHtml from '../../parser/model/ParserHtml';
import Selectors from '../../selector_manager/model/Selectors'; import Selectors from '../../selector_manager/model/Selectors';
import { shallowDiff } from '../../utils/mixins'; import { shallowDiff, get, stringToPath } from '../../utils/mixins';
import EditorModel from '../../editor/model/Editor';
export type StyleProps = Record<string, string | string[]>; import StyleDataVariable from '../../data_sources/model/StyleDataVariable';
export type StyleProps = Record<
string,
| string
| string[]
| {
type: 'data-variable';
value: string;
path: string;
}
>;
export type UpdateStyleOptions = SetOptions & { export type UpdateStyleOptions = SetOptions & {
partial?: boolean; partial?: boolean;
@ -20,6 +31,8 @@ export const getLastStyleValue = (value: string | string[]) => {
}; };
export default class StyleableModel<T extends ObjectHash = any> extends Model<T> { export default class StyleableModel<T extends ObjectHash = any> extends Model<T> {
em?: EditorModel;
/** /**
* Forward style string to `parseStyle` to be parse to an object * Forward style string to `parseStyle` to be parse to an object
* @param {string} str * @param {string} str
@ -46,6 +59,11 @@ export default class StyleableModel<T extends ObjectHash = any> extends Model<T>
getStyle(prop?: string | ObjectAny): StyleProps { getStyle(prop?: string | ObjectAny): StyleProps {
const style = this.get('style') || {}; const style = this.get('style') || {};
const result: ObjectAny = { ...style }; const result: ObjectAny = { ...style };
if (this.em) {
const resolvedStyle = this.resolveDataVariables({ ...result });
// @ts-ignore
return prop && isString(prop) ? resolvedStyle[prop] : resolvedStyle;
}
return prop && isString(prop) ? result[prop] : result; return prop && isString(prop) ? result[prop] : result;
} }
@ -77,10 +95,14 @@ export default class StyleableModel<T extends ObjectHash = any> extends Model<T>
delete newStyle[prop]; delete newStyle[prop];
} }
}); });
this.convertToStyleDataVariable(newStyle);
this.set('style', newStyle, opts as any); this.set('style', newStyle, opts as any);
const diff = shallowDiff(propOrig, propNew); const diff = shallowDiff(propOrig, newStyle);
// Delete the property used for partial updates // Delete the property used for partial updates
delete diff.__p; delete diff.__p;
keys(diff).forEach(pr => { keys(diff).forEach(pr => {
// @ts-ignore // @ts-ignore
const { em } = this; const { em } = this;
@ -92,7 +114,61 @@ export default class StyleableModel<T extends ObjectHash = any> extends Model<T>
} }
}); });
return propNew; this.processDataVariableStyles(newStyle);
return newStyle;
}
convertToStyleDataVariable(style: StyleProps) {
keys(style).forEach(key => {
const styleValue = style[key];
// @ts-ignore
if (typeof styleValue === 'object' && styleValue.type === 'data-variable') {
// @ts-ignore
style[key] = new StyleDataVariable(styleValue, { em: this.em });
}
});
}
processDataVariableStyles(style: StyleProps) {
keys(style).forEach(key => {
const styleValue = style[key];
// @ts-ignore
if (styleValue instanceof StyleDataVariable) {
// @ts-ignore
this.listenToDataVariable(styleValue, key);
}
});
}
listenToDataVariable(dataVar: StyleDataVariable, styleProp: string) {
this.listenTo(dataVar, 'change:value', (model: any) => {
const newValue = model.get('value');
this.updateStyleProp(styleProp, newValue);
});
}
updateStyleProp(prop: string, value: string) {
const style = this.getStyle();
style[prop] = value;
// @ts-ignore
this.set('style', style, { noEvent: true });
this.trigger(`change:style:${prop}`);
}
resolveDataVariables(style: StyleProps): StyleProps {
const resolvedStyle = { ...style };
keys(resolvedStyle).forEach(key => {
const styleValue = resolvedStyle[key];
// @ts-ignore
if (styleValue instanceof StyleDataVariable) {
// @ts-ignore
const resolvedValue = this.em?.DataSources.getValue(styleValue.get('path'), styleValue.get('value'));
// @ts-ignore
resolvedStyle[key] = resolvedValue || styleValue.get('value');
}
});
return resolvedStyle;
} }
/** /**
@ -147,7 +223,7 @@ export default class StyleableModel<T extends ObjectHash = any> extends Model<T>
const value = style[prop]; const value = style[prop];
const values = isArray(value) ? (value as string[]) : [value]; const values = isArray(value) ? (value as string[]) : [value];
values.forEach((val: string) => { (values as string[]).forEach((val: string) => {
const value = `${val}${important ? ' !important' : ''}`; const value = `${val}${important ? ' !important' : ''}`;
value && result.push(`${prop}:${value};`); value && result.push(`${prop}:${value};`);
}); });
@ -164,9 +240,4 @@ export default class StyleableModel<T extends ObjectHash = any> extends Model<T>
// @ts-ignore // @ts-ignore
return this.selectorsToString ? this.selectorsToString(opts) : this.getSelectors().getFullString(); return this.selectorsToString ? this.selectorsToString(opts) : this.getSelectors().getFullString();
} }
// @ts-ignore
// _validate(attr, opts) {
// return true;
// }
} }

2
src/style_manager/index.ts

@ -395,6 +395,7 @@ export default class StyleManager extends ItemManagerModule<
if (isString(target)) { if (isString(target)) {
const rule = cssc.getRule(target) || cssc.setRule(target); const rule = cssc.getRule(target) || cssc.setRule(target);
!isUndefined(stylable) && rule.set({ stylable }); !isUndefined(stylable) && rule.set({ stylable });
// @ts-ignore
model = rule; model = rule;
} }
@ -652,6 +653,7 @@ export default class StyleManager extends ItemManagerModule<
.reverse(); .reverse();
// Slice removes rules not related to the current device // Slice removes rules not related to the current device
// @ts-ignore
result = all.slice(all.indexOf(target as CssRule) + 1); result = all.slice(all.indexOf(target as CssRule) + 1);
} }

28
src/style_manager/model/PropertyComposite.ts

@ -276,7 +276,13 @@ export default class PropertyComposite<T extends Record<string, any> = PropertyC
const result = this.getStyleFromProps()[this.getName()] || ''; const result = this.getStyleFromProps()[this.getName()] || '';
return getLastStyleValue(result); if (result && typeof result !== 'string' && 'type' in result) {
if (result.type === 'data-variable') {
console.log('Datasources __getFullValue');
}
}
return getLastStyleValue(result as string);
} }
__getJoin() { __getJoin() {
@ -300,7 +306,15 @@ export default class PropertyComposite<T extends Record<string, any> = PropertyC
} }
__splitStyleName(style: StyleProps, name: string, sep: string | RegExp) { __splitStyleName(style: StyleProps, name: string, sep: string | RegExp) {
return this.__splitValue(style[name] || '', sep); const value = style[name];
if (value && typeof value !== 'string' && 'type' in value) {
if (value.type === 'data-variable') {
console.log('Datasources __splitStyleName');
}
}
return this.__splitValue((value as string) || '', sep);
} }
__getSplitValue(value: string | string[] = '', { byName }: OptionByName = {}) { __getSplitValue(value: string | string[] = '', { byName }: OptionByName = {}) {
@ -340,7 +354,15 @@ export default class PropertyComposite<T extends Record<string, any> = PropertyC
if (!fromStyle) { if (!fromStyle) {
// Get props from the main property // Get props from the main property
result = this.__getSplitValue(style[name] || '', { byName }); const value = style[name];
if (value && typeof value !== 'string' && 'type' in value) {
if (value.type === 'data-variable') {
console.log('Datasources __getPropsFromStyle');
}
}
result = this.__getSplitValue((value as string) || '', { byName });
// Get props from the inner properties // Get props from the inner properties
props.forEach(prop => { props.forEach(prop => {

40
test/specs/data_sources/index.ts

@ -34,6 +34,46 @@ describe('DataSourceManager', () => {
expect(dsm).toBeTruthy(); expect(dsm).toBeTruthy();
}); });
describe.only('Style', () => {
let fixtures: HTMLElement;
let cmpRoot: ComponentWrapper;
beforeEach(() => {
document.body.innerHTML = '<div id="fixtures"></div>';
const { Pages, Components } = em;
Pages.onLoad();
cmpRoot = Components.getWrapper()!;
const View = Components.getType('wrapper')!.view;
const wrapperEl = new View({
model: cmpRoot,
config: { ...cmpRoot.config, em },
});
wrapperEl.render();
fixtures = document.body.querySelector('#fixtures')!;
fixtures.appendChild(wrapperEl.el);
});
test('todo', () => {
const styleDataSource: DataSourceProps = {
id: 'colors-data',
records: [{ id: 'id1', color: 'red' }],
};
const cmp = cmpRoot.append({
tagName: 'h1',
type: 'text',
content: 'Hello World',
style: {
color: {
type: 'data-variable',
default: 'black',
path: 'colors-data.id1.color',
},
},
})[0];
});
});
test('add DataSource with records', () => { test('add DataSource with records', () => {
const eventAdd = jest.fn(); const eventAdd = jest.fn();
em.on(dsm.events.add, eventAdd); em.on(dsm.events.add, eventAdd);

Loading…
Cancel
Save