2022-02-24 02:26:52 +08:00
|
|
|
|
import ko from 'ko';
|
2021-03-25 17:08:29 +08:00
|
|
|
|
import { i18nToNodes } from 'Common/Translator';
|
2021-02-17 21:40:21 +08:00
|
|
|
|
import { doc, createElement } from 'Common/Globals';
|
2022-09-26 20:02:30 +08:00
|
|
|
|
import { SaveSettingStatus } from 'Common/Enums';
|
2022-09-26 17:55:26 +08:00
|
|
|
|
import { isFunction, forEachObjectEntry } from 'Common/Utils';
|
2021-01-26 05:00:13 +08:00
|
|
|
|
|
2022-02-17 06:12:12 +08:00
|
|
|
|
export const
|
2022-02-24 18:19:19 +08:00
|
|
|
|
errorTip = (element, value) => value
|
|
|
|
|
? setTimeout(() => element.setAttribute('data-rainloopErrorTip', value), 100)
|
|
|
|
|
: element.removeAttribute('data-rainloopErrorTip'),
|
|
|
|
|
|
2022-02-17 06:12:12 +08:00
|
|
|
|
/**
|
|
|
|
|
* The value of the pureComputed observable shouldn’t vary based on the
|
|
|
|
|
* number of evaluations or other “hidden” information. Its value should be
|
|
|
|
|
* based solely on the values of other observables in the application
|
|
|
|
|
*/
|
|
|
|
|
koComputable = fn => ko.computed(fn, {'pure':true}),
|
|
|
|
|
|
2022-02-17 16:36:29 +08:00
|
|
|
|
addObservablesTo = (target, observables) =>
|
|
|
|
|
forEachObjectEntry(observables, (key, value) =>
|
2022-02-28 17:38:47 +08:00
|
|
|
|
target[key] || (target[key] = /*isArray(value) ? ko.observableArray(value) :*/ ko.observable(value)) ),
|
2022-02-17 16:36:29 +08:00
|
|
|
|
|
|
|
|
|
addComputablesTo = (target, computables) =>
|
|
|
|
|
forEachObjectEntry(computables, (key, fn) => target[key] = koComputable(fn)),
|
|
|
|
|
|
|
|
|
|
addSubscribablesTo = (target, subscribables) =>
|
|
|
|
|
forEachObjectEntry(subscribables, (key, fn) => target[key].subscribe(fn)),
|
|
|
|
|
|
2022-09-02 17:52:07 +08:00
|
|
|
|
dispose = disposable => isFunction(disposable?.dispose) && disposable.dispose(),
|
2022-02-17 19:48:57 +08:00
|
|
|
|
|
2023-02-21 22:33:55 +08:00
|
|
|
|
onKey = (key, element, fValueAccessor, fAllBindings, model) => {
|
2023-02-21 21:26:37 +08:00
|
|
|
|
let fn = event => {
|
|
|
|
|
if (key == event.key) {
|
|
|
|
|
// stopEvent(event);
|
|
|
|
|
// element.dispatchEvent(new Event('change'));
|
2023-02-21 22:33:55 +08:00
|
|
|
|
fValueAccessor().call(model);
|
2023-02-21 21:26:37 +08:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
element.addEventListener('keydown', fn);
|
|
|
|
|
ko.utils.domNodeDisposal.addDisposeCallback(element, () => element.removeEventListener('keydown', fn));
|
|
|
|
|
},
|
|
|
|
|
|
2022-02-17 19:48:57 +08:00
|
|
|
|
// With this we don't need delegateRunOnDestroy
|
|
|
|
|
koArrayWithDestroy = data => {
|
|
|
|
|
data = ko.observableArray(data);
|
|
|
|
|
data.subscribe(changes =>
|
|
|
|
|
changes.forEach(item =>
|
2022-09-02 17:52:07 +08:00
|
|
|
|
'deleted' === item.status && null == item.moved && item.value.onDestroy?.()
|
2022-02-17 19:48:57 +08:00
|
|
|
|
)
|
|
|
|
|
, data, 'arrayChange');
|
|
|
|
|
return data;
|
|
|
|
|
};
|
2021-12-31 20:30:05 +08:00
|
|
|
|
|
2022-02-24 18:19:19 +08:00
|
|
|
|
Object.assign(ko.bindingHandlers, {
|
|
|
|
|
tooltipErrorTip: {
|
|
|
|
|
init: (element, fValueAccessor) => {
|
|
|
|
|
doc.addEventListener('click', () => {
|
|
|
|
|
let value = fValueAccessor();
|
|
|
|
|
ko.isObservable(value) && !ko.isComputed(value) && value('');
|
|
|
|
|
errorTip(element);
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
update: (element, fValueAccessor) => {
|
|
|
|
|
let value = ko.unwrap(fValueAccessor());
|
|
|
|
|
value = isFunction(value) ? value() : value;
|
|
|
|
|
errorTip(element, value);
|
2014-08-20 23:03:12 +08:00
|
|
|
|
}
|
2022-02-24 18:19:19 +08:00
|
|
|
|
},
|
2016-06-30 08:02:45 +08:00
|
|
|
|
|
2022-02-24 18:19:19 +08:00
|
|
|
|
onEnter: {
|
2023-02-21 22:33:55 +08:00
|
|
|
|
init: (element, fValueAccessor, fAllBindings, model) =>
|
|
|
|
|
onKey('Enter', element, fValueAccessor, fAllBindings, model)
|
2022-02-24 18:19:19 +08:00
|
|
|
|
},
|
2016-06-30 08:02:45 +08:00
|
|
|
|
|
2022-09-26 19:54:28 +08:00
|
|
|
|
onEsc: {
|
2023-02-21 22:33:55 +08:00
|
|
|
|
init: (element, fValueAccessor, fAllBindings, model) =>
|
|
|
|
|
onKey('Escape', element, fValueAccessor, fAllBindings, model)
|
2022-09-26 19:54:28 +08:00
|
|
|
|
},
|
|
|
|
|
|
2022-02-24 18:19:19 +08:00
|
|
|
|
onSpace: {
|
2023-02-21 22:33:55 +08:00
|
|
|
|
init: (element, fValueAccessor, fAllBindings, model) =>
|
|
|
|
|
onKey(' ', element, fValueAccessor, fAllBindings, model)
|
2022-02-24 18:19:19 +08:00
|
|
|
|
},
|
2016-06-30 08:02:45 +08:00
|
|
|
|
|
2022-02-24 18:19:19 +08:00
|
|
|
|
i18nUpdate: {
|
|
|
|
|
update: (element, fValueAccessor) => {
|
|
|
|
|
ko.unwrap(fValueAccessor());
|
|
|
|
|
i18nToNodes(element);
|
2016-09-03 07:19:37 +08:00
|
|
|
|
}
|
2022-02-24 18:19:19 +08:00
|
|
|
|
},
|
2016-09-03 07:19:37 +08:00
|
|
|
|
|
2022-02-24 18:19:19 +08:00
|
|
|
|
title: {
|
|
|
|
|
update: (element, fValueAccessor) => element.title = ko.unwrap(fValueAccessor())
|
2016-06-30 08:02:45 +08:00
|
|
|
|
},
|
2014-08-20 23:03:12 +08:00
|
|
|
|
|
2022-02-24 18:19:19 +08:00
|
|
|
|
command: {
|
|
|
|
|
init: (element, fValueAccessor, fAllBindings, viewModel, bindingContext) => {
|
|
|
|
|
const command = fValueAccessor();
|
2014-08-20 23:03:12 +08:00
|
|
|
|
|
2022-03-04 00:34:45 +08:00
|
|
|
|
if (!command || !command.canExecute) {
|
2022-02-24 18:19:19 +08:00
|
|
|
|
throw new Error('Value should be a command');
|
|
|
|
|
}
|
2014-08-20 23:03:12 +08:00
|
|
|
|
|
2022-02-24 18:19:19 +08:00
|
|
|
|
ko.bindingHandlers['FORM'==element.nodeName ? 'submit' : 'click'].init(
|
|
|
|
|
element,
|
|
|
|
|
fValueAccessor,
|
|
|
|
|
fAllBindings,
|
|
|
|
|
viewModel,
|
|
|
|
|
bindingContext
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
update: (element, fValueAccessor) => {
|
2022-10-10 19:52:56 +08:00
|
|
|
|
const cl = element.classList;
|
2022-02-24 18:19:19 +08:00
|
|
|
|
|
2022-10-10 19:52:56 +08:00
|
|
|
|
let disabled = !fValueAccessor().canExecute();
|
2022-02-24 18:19:19 +08:00
|
|
|
|
cl.toggle('disabled', disabled);
|
|
|
|
|
|
|
|
|
|
if (element.matches('INPUT,TEXTAREA,BUTTON')) {
|
|
|
|
|
element.disabled = disabled;
|
|
|
|
|
}
|
2021-02-11 17:08:27 +08:00
|
|
|
|
}
|
|
|
|
|
},
|
2022-02-24 18:19:19 +08:00
|
|
|
|
|
|
|
|
|
saveTrigger: {
|
|
|
|
|
init: (element) => {
|
|
|
|
|
let icon = element;
|
|
|
|
|
if (element.matches('input,select,textarea')) {
|
2022-09-30 16:17:19 +08:00
|
|
|
|
element.classList.add('settings-save-trigger-input');
|
2022-02-24 18:19:19 +08:00
|
|
|
|
element.after(element.saveTriggerIcon = icon = createElement('span'));
|
|
|
|
|
}
|
|
|
|
|
icon.classList.add('settings-save-trigger');
|
|
|
|
|
},
|
|
|
|
|
update: (element, fValueAccessor) => {
|
|
|
|
|
const value = parseInt(ko.unwrap(fValueAccessor()),10);
|
|
|
|
|
let cl = (element.saveTriggerIcon || element).classList;
|
|
|
|
|
if (element.saveTriggerIcon) {
|
2022-09-26 20:02:30 +08:00
|
|
|
|
cl.toggle('saving', value === SaveSettingStatus.Saving);
|
|
|
|
|
cl.toggle('success', value === SaveSettingStatus.Success);
|
|
|
|
|
cl.toggle('error', value === SaveSettingStatus.Failed);
|
2022-02-24 18:19:19 +08:00
|
|
|
|
}
|
|
|
|
|
cl = element.classList;
|
2022-09-26 20:02:30 +08:00
|
|
|
|
cl.toggle('success', value === SaveSettingStatus.Success);
|
|
|
|
|
cl.toggle('error', value === SaveSettingStatus.Failed);
|
2021-02-11 17:08:27 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-02-24 18:19:19 +08:00
|
|
|
|
});
|
2021-02-11 17:08:27 +08:00
|
|
|
|
|
2016-06-30 08:02:45 +08:00
|
|
|
|
// extenders
|
2014-10-04 19:58:01 +08:00
|
|
|
|
|
2016-08-17 06:01:20 +08:00
|
|
|
|
ko.extenders.toggleSubscribeProperty = (target, options) => {
|
|
|
|
|
const prop = options[1];
|
2019-07-05 03:19:24 +08:00
|
|
|
|
if (prop) {
|
|
|
|
|
target.subscribe(
|
2022-09-02 17:52:07 +08:00
|
|
|
|
prev => prev?.[prop]?.(false),
|
2019-07-05 03:19:24 +08:00
|
|
|
|
options[0],
|
|
|
|
|
'beforeChange'
|
|
|
|
|
);
|
2014-08-20 23:03:12 +08:00
|
|
|
|
|
2022-09-02 17:52:07 +08:00
|
|
|
|
target.subscribe(next => next?.[prop]?.(true), options[0]);
|
2016-06-30 08:02:45 +08:00
|
|
|
|
}
|
2014-08-20 23:03:12 +08:00
|
|
|
|
|
2016-08-17 06:01:20 +08:00
|
|
|
|
return target;
|
2016-06-30 08:02:45 +08:00
|
|
|
|
};
|
|
|
|
|
|
2016-08-17 06:01:20 +08:00
|
|
|
|
ko.extenders.falseTimeout = (target, option) => {
|
2021-02-11 17:08:27 +08:00
|
|
|
|
target.subscribe((() => target(false)).debounce(parseInt(option, 10) || 0));
|
2016-08-17 06:01:20 +08:00
|
|
|
|
return target;
|
2016-06-30 08:02:45 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// functions
|
2014-10-04 19:58:01 +08:00
|
|
|
|
|
2022-01-28 06:07:34 +08:00
|
|
|
|
ko.observable.fn.askDeleteHelper = function() {
|
|
|
|
|
return this.extend({ falseTimeout: 3000, toggleSubscribeProperty: [this, 'askDelete'] });
|
2016-06-30 08:02:45 +08:00
|
|
|
|
};
|