UNPKG

vue-simput

Version:

Simput component meant to be used with PyWebVue/Trame

268 lines (241 loc) 7.18 kB
import { TYPES, FALLBACK_CONVERT } from './types'; export const MANAGERS = {}; export const COMPUTED = { query: { textToQuery() { // ${this.help?.toLowerCase() || ''} return `${this.name?.toLowerCase() || ''} ${this.label?.toLowerCase() || ''}`; }, shouldShow() { if (this.query && this.decorator.query) { const tokens = this.query.split(' '); if (tokens.length > 1) { for (let i = 0; i < tokens.length; i++) { const t = tokens[i].trim(); if (t && this.textToQuery.includes(t)) { return true; } } return false; } return this.textToQuery.includes(this.query); } return this.decorator.show; }, }, decorator: { decorator() { /* eslint-disable no-unused-expressions */ this.mtime; // force refresh return ( this.domains()[this.name]?.decorator?.available || { show: true, enable: true, query: true } ); }, }, convert: { convert() { return TYPES[this.type]?.convert || FALLBACK_CONVERT; }, }, hints: { hints() { /* eslint-disable no-unused-expressions */ this.mtime; // force refresh return this.domains()?.[this.name]?.hints || []; }, }, rule: { rule() { return TYPES[this.type]?.rule || (() => true); }, }, }; export function debounce(func, wait = 100) { let timeout; const debounced = (...args) => { const context = this; const later = () => { timeout = null; func.apply(context, args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; debounced.cancel = () => clearTimeout(timeout); return debounced; } export class DataManager { constructor(namespace, wsClient) { this.namespace = namespace; this.cache = null; this.comm = []; this.pending = {}; this.wsClient = wsClient; this.resetCache(); this.nextTS = 1; this.subscription = this.wsClient .getConnection() .getSession() .subscribe('simput.push', ([event]) => { const { id, data, domains, type, ui, } = event; if (data) { delete this.pending[id]; const before = JSON.stringify(this.cache.data[id]?.properties); const after = JSON.stringify(data.properties); if (before !== after) { this.cache.data[id] = data; console.log(`data(${id}) == CHANGE`); // console.group('before'); // console.log(before); // console.groupEnd(); // console.group('after'); // console.log(after); // console.groupEnd(); } else { console.log(`data(${id}) == SAME`); } this.cache.data[id].mtime = data.mtime; this.cache.data[id].original = JSON.parse(after); } if (domains) { delete this.pending[`d-${id}`]; const before = JSON.stringify(this.cache.domains[id]); const after = JSON.stringify(domains); // console.log(JSON.stringify(domains, null, 2)); if (before !== after) { this.cache.domains[id] = domains; console.log(`domains(${id}) == CHANGE`); } else { console.log(`domains(${id}) == SAME`); } } if (ui) { console.log(`ui(${type})`); delete this.pending[type]; this.cache.ui[type] = ui; } this.notify('change', { id, type }); if (ui) { this.nextTS += 1; this.notify('templateTS'); } }); this.subscriptionUI = this.wsClient .getConnection() .getSession() .subscribe('simput.event', ([event]) => { if (event.topic === 'ui-change') { const typesToFetch = Object.keys(this.cache.ui); this.cache.ui = {}; for (let i = 0; i < typesToFetch.length; i++) { this.getUI(typesToFetch[i]); } } if (event.topic === 'data-change') { const { ids, action } = event; for (let i = 0; i < ids.length; i++) { if (this.cache.data[ids[i]]) { if (action === 'changed') { console.log('getData from data-change', ids[i]); this.getData(ids[i], true); } } } } }); this.onDirty = ({ id, name, names }) => { const dirtySet = []; if (name) { const value = this.cache.data[id].properties[name]; dirtySet.push({ id, name, value }); } if (names) { for (let i = 0; i < names.length; i++) { const n = names[i]; const value = this.cache.data[id].properties[n]; dirtySet.push({ id, name: n, value }); } } console.log(' > dirty', dirtySet); this.wsClient .getRemote() .PyWebVue.trigger(`${this.namespace}Update`, [dirtySet]); }; } resetCache() { this.cache = { data: {}, ui: {}, domains: {}, }; this.wsClient.getRemote().PyWebVue.trigger(`${this.namespace}ResetCache`, []); } connectBus(bus) { if (this.comm.indexOf(bus) === -1) { this.comm.push(bus); bus.$emit('connect'); bus.$on('dirty', this.onDirty); } } disconnectBus(bus) { const index = this.comm.indexOf(bus); if (index > -1) { bus.$emit('disconnect'); bus.$off('dirty', this.onDirty); this.comm.splice(index, 1); } } notify(topic, event) { for (let i = 0; i < this.comm.length; i++) { this.comm[i].$emit(topic, event); } } getData(id, forceFetch = false) { const data = this.cache.data[id]; if ((!data || forceFetch) && !this.pending[id]) { console.log(' > fetch data', id, forceFetch); this.pending[id] = true; this.wsClient.getRemote().PyWebVue.trigger(`${this.namespace}Fetch`, [], { id }); } return data; } getDomains(id, forceFetch = false) { const domains = this.cache.domains[id]; if ((!domains || forceFetch) && !this.pending[`d-${id}`]) { console.log(' > fetch domain', id, forceFetch); this.pending[`d-${id}`] = true; this.wsClient.getRemote().PyWebVue.trigger(`${this.namespace}Fetch`, [], { domains: id }); } return domains; } getUI(type, forceFetch = false) { const ui = this.cache.ui[type]; if ((!ui || forceFetch) && !this.pending[type]) { console.log(' > fetch ui', type, forceFetch); this.pending[type] = true; this.wsClient.getRemote().PyWebVue.trigger(`${this.namespace}Fetch`, [], { type }); } return ui; } getUITimeStamp() { return this.nextTS; } refresh(id, name) { console.log(' > refresh', id, name); this.wsClient.getRemote().PyWebVue.trigger(`${this.namespace}Refresh`, [id, name]); } } export function getSimputManager(id, namespace, client) { if (!client) { return null; } if (MANAGERS[id]) { return MANAGERS[id]; } const manager = new DataManager(namespace, client); MANAGERS[id] = manager; return manager; }