diff --git a/.gitignore b/.gitignore index ecc3046..ff01332 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ pnpm-debug.log* __pycache__ *egg-info *pyc + +*build/* diff --git a/examples/01_Widgets/app.py b/examples/01_Widgets/app.py index 8e4ed1e..7924f60 100644 --- a/examples/01_Widgets/app.py +++ b/examples/01_Widgets/app.py @@ -1,15 +1,24 @@ from pathlib import Path from trame.app import get_server -from trame.ui.vuetify3 import SinglePageLayout -from trame.widgets import vuetify3 as vuetify, simput, html +from trame.widgets import simput, html from trame_simput import get_simput_manager +client_type = "vue3" +use_client2 = client_type == "vue2" + +if use_client2: + from trame.ui.vuetify2 import SinglePageLayout + from trame.widgets import vuetify2 as vuetify +else: + from trame.ui.vuetify3 import SinglePageLayout + from trame.widgets import vuetify3 as vuetify + # ----------------------------------------------------------------------------- # Trame setup # ----------------------------------------------------------------------------- -server = get_server() +server = get_server(client_type=client_type) state, ctrl = server.state, server.controller # ----------------------------------------------------------------------------- @@ -19,17 +28,27 @@ DEF_DIR = Path(__file__).with_name("definitions") simput_manager = get_simput_manager() -simput_manager.load_model(yaml_file=DEF_DIR / "model.yaml") # ----------------------------------------------------------------------------- # Application state # ----------------------------------------------------------------------------- pxm = simput_manager.proxymanager + + +def load_model(): + simput_manager.load_model(yaml_file=DEF_DIR / "model.yaml") + + +load_model() + CHOICES = [] for obj_type in pxm.types(): item = pxm.create(obj_type) - CHOICES.append({"text": obj_type, "value": item.id}) + if use_client2: + CHOICES.append({"text": obj_type, "value": item.id}) + else: + CHOICES.append({"title": obj_type, "value": item.id}) # ----------------------------------------------------------------------------- @@ -40,9 +59,7 @@ def update_ui(use_xml_ui, **kwargs): simput_manager.load_ui(xml_file=DEF_DIR / "ui.xml") else: simput_manager.clear_ui() - simput_manager.load_model( - yaml_file=DEF_DIR / "model.yaml" - ) # Needed to generate UI + load_model() # Needed to generate UI # ----------------------------------------------------------------------------- @@ -115,7 +132,7 @@ def update_ui(use_xml_ui, **kwargs): vuetify.VSelect( v_model=("active", CHOICES[0].get("value")), items=("choices", CHOICES), - dense=True, + variant="underlined", hide_details=True, style="max-width: 120px;", ) diff --git a/examples/01_Widgets/definitions/model.yaml b/examples/01_Widgets/definitions/model.yaml index a579416..763648a 100644 --- a/examples/01_Widgets/definitions/model.yaml +++ b/examples/01_Widgets/definitions/model.yaml @@ -205,6 +205,29 @@ Select: - text: Case 4 value: 4 + Section: + _label: Drop Down with sub-sections + _help: >- + Example of a drop down with sub-sections + type: string + size: -1 + initial: + - A + - '1' + domains: + - type: LabelList + values: + - header: Letter cases + - text: Case A + value: A + - text: Case B + value: B + - header: Number cases + - text: Case 1 + value: '1' + - text: Case 2 + value: '2' + SelectDynamicList: _label: Dynamic available _help: Just to try remote domain list @@ -361,7 +384,6 @@ Slider: value_range: [-10, 10] level: 2 - ManyDoubleFloat: _label: Many Double floats _help: >- @@ -383,4 +405,4 @@ Slider: level: 2 - type: UI properties: - sizeControl: true \ No newline at end of file + sizeControl: true diff --git a/examples/07_ProxyList/app.py b/examples/07_ProxyList/app.py index bcc79d7..db4d3d5 100644 --- a/examples/07_ProxyList/app.py +++ b/examples/07_ProxyList/app.py @@ -1,16 +1,23 @@ from pathlib import Path from trame.app import get_server -from trame.ui.vuetify2 import SinglePageLayout -from trame.widgets import vuetify2 as vuetify, simput +from trame.widgets import simput from trame_simput import get_simput_manager +client_type = "vue3" +use_client2 = client_type == "vue2" +if use_client2: + from trame.ui.vuetify2 import SinglePageLayout + from trame.widgets import vuetify2 as vuetify +else: + from trame.ui.vuetify3 import SinglePageLayout + from trame.widgets import vuetify3 as vuetify # ----------------------------------------------------------------------------- # Trame setup # ----------------------------------------------------------------------------- -server = get_server(client_type="vue2") +server = get_server(client_type=client_type) state, ctrl = server.state, server.controller # ----------------------------------------------------------------------------- @@ -18,7 +25,6 @@ # ----------------------------------------------------------------------------- DEF_DIR = Path(__file__).with_name("definitions") - simput_manager = get_simput_manager() simput_manager.load_model(yaml_file=DEF_DIR / "model.yaml") simput_manager.load_ui(xml_file=DEF_DIR / "ui.xml") @@ -26,12 +32,10 @@ address_book = simput_manager.proxymanager.create("AddressBook") - with SinglePageLayout(server) as layout: simput_widget.register_layout(layout) - with layout.content: - with vuetify.VContainer(fluid=True): - simput.SimputItem(item_id=f"{address_book.id}") + with layout.content, vuetify.VContainer(fluid=True): + simput.SimputItem(item_id=f"{address_book.id}") if __name__ == "__main__": diff --git a/examples/07_ProxyList/definitions/model.yaml b/examples/07_ProxyList/definitions/model.yaml index b1476f8..ea25c6f 100644 --- a/examples/07_ProxyList/definitions/model.yaml +++ b/examples/07_ProxyList/definitions/model.yaml @@ -1,5 +1,5 @@ Person: - _label: Personal Informations + _label: Personal Information FirstName: _label: First Name type: string diff --git a/trame_simput/module/core.py b/trame_simput/module/core.py index b4b9b50..b0d6af5 100644 --- a/trame_simput/module/core.py +++ b/trame_simput/module/core.py @@ -142,6 +142,8 @@ def push(self, id=None, type=None, domains=None): ) def emit(self, topic, **kwargs): + if not self._server.protocol: + return logger.info("emit: %s", topic) self._server.protocol_call("simput.push.event", topic, **kwargs) diff --git a/vue3-components/src/components/Simput/script.js b/vue3-components/src/components/Simput/script.js index 41ad31b..758d546 100644 --- a/vue3-components/src/components/Simput/script.js +++ b/vue3-components/src/components/Simput/script.js @@ -38,6 +38,7 @@ export default { () => emit("query", props.query?.toLowerCase() || ""), 250 ), + managerId, }; const updateManager = function updateManager() { diff --git a/vue3-components/src/components/SimputItem/template.html b/vue3-components/src/components/SimputItem/template.html index 362783a..7b398b6 100644 --- a/vue3-components/src/components/SimputItem/template.html +++ b/vue3-components/src/components/SimputItem/template.html @@ -1,6 +1,6 @@
- {{ data }} +
\ No newline at end of file diff --git a/vue3-components/src/widgets/Proxy/script.js b/vue3-components/src/widgets/Proxy/script.js index 6205831..0ef442e 100644 --- a/vue3-components/src/widgets/Proxy/script.js +++ b/vue3-components/src/widgets/Proxy/script.js @@ -1,7 +1,7 @@ import SimputInput from "../../components/SimputItem/index.vue"; import { useDecorator } from "../../core/utils"; -const { computed, inject, toRef } = window.Vue; +const { computed, inject, toRef, ref } = window.Vue; export default { name: "swProxy", @@ -12,6 +12,17 @@ export default { mtime: { type: Number, }, + size: { + type: Number, + default: 1, + }, + sizeControl: { + type: Boolean, + default: false, + }, + proxyType: { + type: String, + }, }, components: { SimputInput, @@ -28,15 +39,91 @@ export default { }); const properties = inject("properties"); + + const model = computed({ + get() { + /* eslint-disable no-unused-expressions */ + props.mtime; // force refresh + const value = properties() && properties()[props.name]; + if (!value && props.size > 1) { + const emptyArray = []; + emptyArray.length = props.size; + return emptyArray; + } + return value; + }, + set(v) { + properties()[props.name] = v; + }, + }); + const itemId = computed(() => { /* eslint-disable no-unused-expressions */ props.mtime; // force refresh return properties()[props.name]; }); + const computedLayout = computed(() => { + /* eslint-disable no-unused-expressions */ + props.mtime; // force refresh + return props.layout || domains()[props.name]?.UI?.layout || "vertical"; + }); + + const computedSize = computed(() => { + if (Number(props.size) !== 1) { + return Math.max(props.size || 1, model.value.length || 0); + } + return Number(props.size); + }); + + const computedSizeControl = computed(() => { + /* eslint-disable no-unused-expressions */ + props.mtime; // force refresh + return props.sizeControl || domains()[props.name]?.UI?.sizeControl; + }); + + const deleteEntry = function deleteEntry(index) { + if (computedSize.value > Number(props.size)) { + model.value.splice(index, 1); + dirty(props.name); + } + }; + + const getComponentProps = function getComponentProps(index) { + if (computedLayout.value === "vertical") { + return { cols: 12 }; + } + if (computedLayout.value === "l2") { + return { cols: 6 }; + } + if (computedLayout.value === "l3") { + return { cols: 4 }; + } + if (computedLayout.value === "l4") { + return { cols: 3 }; + } + if (computedLayout.value === "m3-half") { + const attrs = { cols: 4 }; + if (index === 3) { + attrs.offset = 4; + } + if (index === 5) { + attrs.offset = 8; + } + return attrs; + } + return {}; + }; + return { itemId, decorator, + model, + computedLayout, + computedSize, + computedSizeControl, + deleteEntry, + getComponentProps, }; }, }; diff --git a/vue3-components/src/widgets/Proxy/template.html b/vue3-components/src/widgets/Proxy/template.html index a2fa372..2860eea 100644 --- a/vue3-components/src/widgets/Proxy/template.html +++ b/vue3-components/src/widgets/Proxy/template.html @@ -1 +1,26 @@ - + + \ No newline at end of file diff --git a/vue3-components/src/widgets/Select/script.js b/vue3-components/src/widgets/Select/script.js index 477a91f..4cc062f 100644 --- a/vue3-components/src/widgets/Select/script.js +++ b/vue3-components/src/widgets/Select/script.js @@ -203,4 +203,9 @@ export default { shouldShow, }; }, + methods: { + resolveItemTitle(item) { + return item.title ?? item.text ?? undefined; + } + }, }; diff --git a/vue3-components/src/widgets/Select/template.html b/vue3-components/src/widgets/Select/template.html index 0f97ad4..3f0d3ce 100644 --- a/vue3-components/src/widgets/Select/template.html +++ b/vue3-components/src/widgets/Select/template.html @@ -2,14 +2,24 @@ - + > + + + diff --git a/vue3-components/src/widgets/Slider/script.js b/vue3-components/src/widgets/Slider/script.js index 6181e8a..c0a30c9 100644 --- a/vue3-components/src/widgets/Slider/script.js +++ b/vue3-components/src/widgets/Slider/script.js @@ -107,7 +107,7 @@ export default { get() { /* eslint-disable no-unused-expressions */ props.mtime; // force refresh - props.dynamicSize; + dynamicSize.value; return properties() && properties()[props.name]; }, set(v) { @@ -122,7 +122,7 @@ export default { }); const computedSize = computed(() => { - if (Number(props.size.value) !== 1) { + if (Number(props.size) !== 1) { return Math.max(props.size, model.value.length); } return Number(props.size); @@ -217,8 +217,8 @@ export default { dirty(props.name); }; const addEntry = function addEntry() { - props.dynamicSize = model.value.length + 1; - model.value.length = props.dynamicSize; + dynamicSize.value = model.value.length + 1; + model.value.length = dynamicSize.value; validate(dynamicSize.value); }; diff --git a/vue3-components/src/widgets/Slider/template.html b/vue3-components/src/widgets/Slider/template.html index bce75f5..10a34db 100644 --- a/vue3-components/src/widgets/Slider/template.html +++ b/vue3-components/src/widgets/Slider/template.html @@ -1,7 +1,7 @@
- - + +
@@ -22,14 +22,13 @@ {{ model }} - + @@ -63,7 +61,6 @@ v-model="model[i - 1]" @update:modelValue="validate(i)" hide-details - density="compact" :rules="[rule]" :min="computedMin" :max="computedMax" @@ -74,21 +71,18 @@
{{ model[i - 1] }}
- mdi-minus-circle-outline - + /> diff --git a/vue3-components/src/widgets/Switch/template.html b/vue3-components/src/widgets/Switch/template.html index c9615e0..b7a5b3f 100644 --- a/vue3-components/src/widgets/Switch/template.html +++ b/vue3-components/src/widgets/Switch/template.html @@ -6,7 +6,6 @@ :hint="help" v-model="model" @update:modelValue="validate" - density="compact" hide-details :disabled="disabled || !decorator.enable" :readonly="readonly" @@ -14,6 +13,7 @@ { + if (proxy_id != undefined) { + model.value.push(proxy_id); + dirty(props.name); + } + dynamicSize.value = model.value.length; + validate(dynamicSize.value); + }); + } else { + if (props.newValue === "null") { + model.value.push(null); + } else if (props.newValue === "same") { + model.value.push(model.value[model.value.length - 2]); + } + dynamicSize.value = model.value.length; + validate(dynamicSize.value); } - - validate(dynamicSize.value); }; const deleteEntry = function deletEntry(index) { diff --git a/vue3-components/src/widgets/TextField/template.html b/vue3-components/src/widgets/TextField/template.html index 53d0c9b..0f3a53d 100644 --- a/vue3-components/src/widgets/TextField/template.html +++ b/vue3-components/src/widgets/TextField/template.html @@ -1,14 +1,8 @@ - +
- - mdi-sync - - - mdi-lifebuoy - - - mdi-plus-circle-outline - + + +
@@ -20,7 +14,7 @@
- + + variant="underlined" + /> - mdi-minus-circle-outline - + />
@@ -87,7 +80,6 @@ class="mb-1" :type="levelToType(hint.level)" border="start" - density="compact" > {{ hint.message }}