@@ -17,49 +17,59 @@
-
diff --git a/packages/client/src/widgets/widget.ts b/packages/client/src/widgets/widget.ts
new file mode 100644
index 0000000000..81239bfb3b
--- /dev/null
+++ b/packages/client/src/widgets/widget.ts
@@ -0,0 +1,71 @@
+import { reactive, watch } from 'vue';
+import { throttle } from 'throttle-debounce';
+import { Form, GetFormResultType } from '@/scripts/form';
+import * as os from '@/os';
+
+export type Widget
> = {
+ id: string;
+ data: Partial
;
+};
+
+export type WidgetComponentProps
> = {
+ widget?: Widget
;
+};
+
+export type WidgetComponentEmits
> = {
+ (e: 'updateProps', props: P);
+};
+
+export type WidgetComponentExpose = {
+ name: string;
+ id: string | null;
+ configure: () => void;
+};
+
+export const useWidgetPropsManager = >(
+ name: string,
+ propsDef: F,
+ props: Readonly>>,
+ emit: WidgetComponentEmits>,
+): {
+ widgetProps: GetFormResultType;
+ save: () => void;
+ configure: () => void;
+} => {
+ const widgetProps = reactive(props.widget ? JSON.parse(JSON.stringify(props.widget.data)) : {});
+
+ const mergeProps = () => {
+ for (const prop of Object.keys(propsDef)) {
+ if (widgetProps.hasOwnProperty(prop)) continue;
+ widgetProps[prop] = propsDef[prop].default;
+ }
+ };
+ watch(widgetProps, () => {
+ mergeProps();
+ }, { deep: true, immediate: true, });
+
+ const save = throttle(3000, () => {
+ emit('updateProps', widgetProps)
+ });
+
+ const configure = async () => {
+ const form = JSON.parse(JSON.stringify(propsDef));
+ for (const item of Object.keys(form)) {
+ form[item].default = widgetProps[item];
+ }
+ const { canceled, result } = await os.form(name, form);
+ if (canceled) return;
+
+ for (const key of Object.keys(result)) {
+ widgetProps[key] = result[key];
+ }
+
+ save();
+ };
+
+ return {
+ widgetProps,
+ save,
+ configure,
+ };
+};