diff --git a/src/plugins/inspectorViews/elements/ElementItem.vue b/src/plugins/inspectorViews/elements/ElementItem.vue
index f668f6b8b2..e2770b1646 100644
--- a/src/plugins/inspectorViews/elements/ElementItem.vue
+++ b/src/plugins/inspectorViews/elements/ElementItem.vue
@@ -45,6 +45,7 @@
:object-path="[elementObject, domainObject]"
@context-click-active="setContextClickState"
/>
+
diff --git a/src/plugins/inspectorViews/elements/ElementsPool.vue b/src/plugins/inspectorViews/elements/ElementsPool.vue
index 06ee6de757..b465c9cbf0 100644
--- a/src/plugins/inspectorViews/elements/ElementsPool.vue
+++ b/src/plugins/inspectorViews/elements/ElementsPool.vue
@@ -42,7 +42,11 @@
:allow-drop="allowDrop"
@dragstart-custom="moveFrom(index)"
@drop-custom="moveTo(index)"
- />
+ >
+
+
+
+
No contained elements
diff --git a/src/plugins/inspectorViews/elements/ElementsViewProvider.js b/src/plugins/inspectorViews/elements/ElementsViewProvider.js
index ce9748861a..dc66f3a3b1 100644
--- a/src/plugins/inspectorViews/elements/ElementsViewProvider.js
+++ b/src/plugins/inspectorViews/elements/ElementsViewProvider.js
@@ -29,6 +29,7 @@ export default function ElementsViewProvider(openmct) {
key: 'elementsView',
name: 'Elements',
canView: function (selection) {
+ // TODO - only use this view provider if another custom provider has not been applied
const hasValidSelection = selection?.length;
const isOverlayPlot = selection?.[0]?.[0]?.context?.item?.type === 'telemetry.plot.overlay';
diff --git a/src/plugins/timeline/Container.js b/src/plugins/timeline/Container.js
index 3f729d56da..8832556bdc 100644
--- a/src/plugins/timeline/Container.js
+++ b/src/plugins/timeline/Container.js
@@ -1,8 +1,52 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2024, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/**
+ * a sizing container for objects in a layout
+ */
class Container {
constructor(domainObject, size) {
+ /**
+ * the identifier of the associated domain object
+ * @type {import('@/api/objects/ObjectAPI.js').Identifier}
+ */
this.domainObjectIdentifier = domainObject.identifier;
+ /**
+ * the size in percentage or pixels
+ * @type {number}
+ */
this.size = size;
+ /**
+ * the default percentage scale of an object
+ * @type {number}
+ */
this.scale = getContainerScale(domainObject);
+ /**
+ * true if the container should be a fixed pixel size
+ * false if the container should be a flexible percentage size
+ * containers are added as flex
+ * @type {boolean}
+ */
+ this.fixed = false;
}
}
diff --git a/src/plugins/timeline/TimelineElementsContent.vue b/src/plugins/timeline/TimelineElementsContent.vue
new file mode 100644
index 0000000000..4c38750e1b
--- /dev/null
+++ b/src/plugins/timeline/TimelineElementsContent.vue
@@ -0,0 +1,82 @@
+
+
+
+ px
+
+
+
+
diff --git a/src/plugins/timeline/TimelineElementsPool.vue b/src/plugins/timeline/TimelineElementsPool.vue
new file mode 100644
index 0000000000..43c9e3df2a
--- /dev/null
+++ b/src/plugins/timeline/TimelineElementsPool.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
diff --git a/src/plugins/timeline/TimelineElementsViewProvider.js b/src/plugins/timeline/TimelineElementsViewProvider.js
new file mode 100644
index 0000000000..73b5023915
--- /dev/null
+++ b/src/plugins/timeline/TimelineElementsViewProvider.js
@@ -0,0 +1,76 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2024, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import mount from 'utils/mount';
+
+import TimelineElementsPool from './TimelineElementsPool.vue';
+
+export default function TimelineElementsViewProvider(openmct) {
+ return {
+ key: 'timelineElementsView',
+ name: 'Elements',
+ canView: function (selection) {
+ return selection?.[0]?.[0]?.context?.item?.type === 'time-strip';
+ },
+ view: function (selection) {
+ let _destroy = null;
+
+ const domainObject = selection?.[0]?.[0]?.context?.item;
+
+ return {
+ show: function (element) {
+ const { destroy } = mount(
+ {
+ el: element,
+ components: {
+ TimelineElementsPool
+ },
+ provide: {
+ openmct,
+ domainObject
+ },
+ template: ``
+ },
+ {
+ app: openmct.app,
+ element
+ }
+ );
+ _destroy = destroy;
+ },
+ showTab: function (isEditing) {
+ const hasComposition = Boolean(domainObject && openmct.composition.get(domainObject));
+
+ return hasComposition && isEditing;
+ },
+ priority: function () {
+ return openmct.priority.HIGH - 1;
+ },
+ destroy: function () {
+ if (_destroy) {
+ _destroy();
+ }
+ }
+ };
+ }
+ };
+}
diff --git a/src/plugins/timeline/TimelineObjectView.vue b/src/plugins/timeline/TimelineObjectView.vue
index 7d3db098d5..e1afbc3035 100644
--- a/src/plugins/timeline/TimelineObjectView.vue
+++ b/src/plugins/timeline/TimelineObjectView.vue
@@ -32,7 +32,8 @@
:hide-button="!item.isEventTelemetry"
:button-click-on="enableExtendEventLines"
:button-click-off="disableExtendEventLines"
- :style="[{ 'flex-basis': sizeString }]"
+ :class="sizeClass"
+ :style="sizeStyle"
>
{{ item.domainObject.name }}
@@ -64,12 +65,12 @@ export default {
type: Object,
required: true
},
- extendedLinesBus: {
+ container: {
type: Object,
required: true
},
- size: {
- type: Number,
+ extendedLinesBus: {
+ type: Object,
required: true
}
},
@@ -81,8 +82,17 @@ export default {
};
},
computed: {
- sizeString() {
- return `${this.size}%`;
+ size() {
+ return this.container.size;
+ },
+ fixed() {
+ return this.container.fixed;
+ },
+ sizeClass() {
+ return `--${this.fixed ? 'fixed' : 'scales'}`;
+ },
+ sizeStyle() {
+ return `flex-basis: ${this.size}${this.fixed ? 'px' : '%'}`;
}
},
watch: {
diff --git a/src/plugins/timeline/TimelineViewLayout.vue b/src/plugins/timeline/TimelineViewLayout.vue
index b1ccf20de4..1b8382bb32 100644
--- a/src/plugins/timeline/TimelineViewLayout.vue
+++ b/src/plugins/timeline/TimelineViewLayout.vue
@@ -47,7 +47,7 @@
{
+ composition.value = loadedComposition;
+ isCompositionLoaded = true;
+
+ // check if containers configuration matches composition
+ // in case composition has been modified outside of view
+ // if so, rebuild containers to match composition
+ // sync containers to composition,
+ // in case composition modified outside of view
+ // but do not mutate until user makes a change
+ let isConfigurationChanged = false;
+ composition.value.forEach((object, index) => {
+ const containerIndex = domainObject.configuration.containers.findIndex((container) =>
+ openmct.objects.areIdsEqual(container.domainObjectIdentifier, object.identifier)
+ );
+
+ if (containerIndex !== index) {
+ isConfigurationChanged = true;
+ }
+
+ if (containerIndex > -1) {
+ existingContainers.push(domainObject.configuration.containers[containerIndex]);
+ } else {
+ const container = new Container(object);
+ existingContainers.push(container);
+ }
+ });
+
+ // add check for total size not equal to 100? if comp and containers same, probably safe
+
+ if (isConfigurationChanged) {
+ console.log('yo');
+ setContainers(existingContainers);
+ mutateContainers();
+ }
+ });
+
function addItem(_domainObject) {
let rowCount = 0;
@@ -187,14 +233,7 @@ export default {
items.value.push(item);
- if (
- !containers.value.some((container) =>
- openmct.objects.areIdsEqual(
- container.domainObjectIdentifier,
- item.domainObject.identifier
- )
- )
- ) {
+ if (isCompositionLoaded) {
const container = new Container(domainObject);
addContainer(container);
}
@@ -245,30 +284,20 @@ export default {
openmct.objects.mutate(domainObject, 'configuration.containers', containers.value);
}
- onMounted(async () => {
- if (composition) {
- composition.on('add', addItem);
- composition.on('remove', removeItem);
- composition.on('reorder', reorder);
+ // context action called from outside component
+ function toggleFixedContextAction(index, fixed) {
+ toggleFixed(index, fixed);
+ }
- loadedComposition.value = await composition.load();
-
- const containersToRemove = containers.value.filter(
- (container) =>
- !items.value.some((item) =>
- openmct.objects.areIdsEqual(
- container.domainObjectIdentifier,
- item.domainObject.identifier
- )
- )
- );
- }
- });
+ // context action called from outside component
+ function changeSizeContextAction(index, size) {
+ sizeFixedContainer(index, size);
+ }
onBeforeUnmount(() => {
- composition.off('add', addItem);
- composition.off('remove', removeItem);
- composition.off('reorder', reorder);
+ compositionCollection.off('add', addItem);
+ compositionCollection.off('remove', removeItem);
+ compositionCollection.off('reorder', reorder);
});
return {
@@ -281,7 +310,6 @@ export default {
containers,
getContainerSize,
timelineHolder,
- loadedComposition,
items,
addContainer,
removeContainer,
@@ -291,7 +319,9 @@ export default {
startContainerResizing,
containerResizing,
endContainerResizing,
- mutateContainers
+ mutateContainers,
+ toggleFixedContextAction,
+ changeSizeContextAction
};
},
data() {
diff --git a/src/plugins/timeline/TimelineViewProvider.js b/src/plugins/timeline/TimelineViewProvider.js
index b76409057f..3bb43875d3 100644
--- a/src/plugins/timeline/TimelineViewProvider.js
+++ b/src/plugins/timeline/TimelineViewProvider.js
@@ -61,7 +61,8 @@ export default function TimelineViewProvider(openmct, extendedLinesBus) {
isEditing
};
},
- template: ''
+ template:
+ ''
},
{
app: openmct.app,
@@ -71,6 +72,11 @@ export default function TimelineViewProvider(openmct, extendedLinesBus) {
component = vNode.componentInstance;
_destroy = destroy;
},
+ contextAction(action, ...args) {
+ if (component?.$refs?.timeline?.[action]) {
+ component.$refs.timeline[action](...args);
+ }
+ },
onEditModeChange(isEditing) {
component.isEditing = isEditing;
},
diff --git a/src/plugins/timeline/configuration.js b/src/plugins/timeline/configuration.js
index 0128f990dd..cb31653da7 100644
--- a/src/plugins/timeline/configuration.js
+++ b/src/plugins/timeline/configuration.js
@@ -1,3 +1,31 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2024, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/**
+ * @typedef {Object} TimeStripConfig configuration for Time Strip views
+ * @property {boolean} useIndependentTime true for independent time, false for global time
+ * @property {Array} containers
+ * @property {number} swimLaneLabelWidth
+ */
export const configuration = {
useIndependentTime: false,
containers: [],
diff --git a/src/plugins/timeline/plugin.js b/src/plugins/timeline/plugin.js
index f0fd2294f7..9704fd7234 100644
--- a/src/plugins/timeline/plugin.js
+++ b/src/plugins/timeline/plugin.js
@@ -23,9 +23,9 @@
import { configuration } from './configuration.js';
import ExtendedLinesBus from './ExtendedLinesBus.js';
import TimelineCompositionPolicy from './TimelineCompositionPolicy.js';
+import TimelineElementsViewProvider from './TimelineElementsViewProvider.js';
import timelineInterceptor from './timelineInterceptor.js';
import TimelineViewProvider from './TimelineViewProvider.js';
-
const extendedLinesBus = new ExtendedLinesBus();
export { extendedLinesBus };
@@ -48,6 +48,7 @@ export default function () {
openmct.composition.addPolicy(new TimelineCompositionPolicy(openmct).allow);
openmct.objectViews.addProvider(new TimelineViewProvider(openmct, extendedLinesBus));
+ openmct.inspectorViews.addProvider(new TimelineElementsViewProvider(openmct));
}
install.extendedLinesBus = extendedLinesBus;
diff --git a/src/plugins/timeline/timeline.scss b/src/plugins/timeline/timeline.scss
index dc81b19676..b83c64ea32 100644
--- a/src/plugins/timeline/timeline.scss
+++ b/src/plugins/timeline/timeline.scss
@@ -45,6 +45,18 @@
}
}
+ &__content {
+ &.--scales {
+ flex-grow: 1;
+ flex-shrink: 1;
+ }
+
+ &.--fixed {
+ flex-grow: 0;
+ flex-shrink: 0;
+ }
+ }
+
&__overlay-lines {
@include abs();
opacity: 0.5;
diff --git a/src/utils/vue/useFlexContainers.js b/src/utils/vue/useFlexContainers.js
index 4a949b606d..f2d68c59f2 100644
--- a/src/utils/vue/useFlexContainers.js
+++ b/src/utils/vue/useFlexContainers.js
@@ -1,5 +1,26 @@
-import _ from 'lodash';
-import { ref } from 'vue';
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2024, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import { computed, ref } from 'vue';
/**
* @typedef {Object} configuration
@@ -22,25 +43,29 @@ export function useFlexContainers(
) {
const containers = ref(existingContainers);
const maxMoveSize = ref(null);
- let sizingContainers = 0;
+
+ const fixedContainersSize = computed(() => {
+ return containers.value
+ .filter((container) => container.fixed === true)
+ .reduce((size, currentContainer) => size + currentContainer.size, 0);
+ });
function addContainer(container) {
containers.value.push(container);
- if (container.scale) {
- sizingContainers += container.scale;
- } else {
- sizingContainers++;
- }
-
- sizeItems(containers.value, container);
+ sizeItems(containers.value);
callback?.();
}
function removeContainer(index) {
+ const isFlexContainer = !containers.value[index].fixed;
+
containers.value.splice(index, 1);
- sizeToFill(containers.value);
+
+ if (isFlexContainer) {
+ sizeItems(containers.value);
+ }
callback?.();
}
@@ -55,20 +80,44 @@ export function useFlexContainers(
callback?.();
}
- function startContainerResizing(index) {
- const beforeContainer = containers.value[index];
- const afterContainer = containers.value[index + 1];
+ function setContainers(_containers) {
+ containers.value = _containers;
+ sizeItems(containers.value);
+ }
- maxMoveSize.value = beforeContainer.size + afterContainer.size;
+ function startContainerResizing(index) {
+ const beforeContainer = getBeforeContainer(index);
+ const afterContainer = getAfterContainer(index);
+
+ if (beforeContainer && afterContainer && !beforeContainer.fixed && !afterContainer.fixed) {
+ maxMoveSize.value = beforeContainer.size + afterContainer.size;
+ }
+ }
+
+ function getBeforeContainer(index) {
+ return containers.value
+ .slice(0, index + 1)
+ .filter((container) => !container.fixed === true)
+ .at(-1);
+ }
+
+ function getAfterContainer(index) {
+ return containers.value.slice(index + 1).filter((container) => !container.fixed === true)[0];
}
function containerResizing(index, delta, event) {
- let percentageMoved = Math.round((delta / getElSize()) * 100);
- let beforeContainer = containers.value[index];
- let afterContainer = containers.value[index + 1];
+ const beforeContainer = getBeforeContainer(index);
+ const afterContainer = getAfterContainer(index);
+ const percentageMoved = Math.round((delta / getElSize()) * 100);
- beforeContainer.size = getContainerSize(beforeContainer.size + percentageMoved);
- afterContainer.size = getContainerSize(afterContainer.size - percentageMoved);
+ if (beforeContainer && afterContainer && !beforeContainer.fixed && !afterContainer.fixed) {
+ beforeContainer.size = getContainerSize(beforeContainer.size + percentageMoved);
+ afterContainer.size = getContainerSize(afterContainer.size - percentageMoved);
+ } else {
+ console.warn(
+ 'Drag requires two flexible containers. Use Elements Tab in Inspector to resize.'
+ );
+ }
}
function endContainerResizing() {
@@ -76,11 +125,9 @@ export function useFlexContainers(
}
function getElSize() {
- if (rowsLayout) {
- return element.value.offsetHeight;
- } else {
- return element.value.offsetWidth;
- }
+ const elSize = rowsLayout === true ? element.value.offsetHeight : element.value.offsetWidth;
+
+ return elSize - fixedContainersSize.value;
}
function getContainerSize(size) {
@@ -94,69 +141,118 @@ export function useFlexContainers(
}
/**
- * Resize items so that newItem fits proportionally (newItem must be an element of items).
- * If newItem does not have a size or is sized at 100%,
- * newItem will have size set to (1 * newItemScale) / n * 100,
- * where n is the total number of items and newItemScale is the scale of newItem.
- * All other items will be resized to fit inside the remaining space.
+ * Resize flexible sized items so they fit proportionally within a viewport
+ * 1. add size to 0 sized items based on scale proportional to total scale
+ * 2. resize item sizes to equal 100
+ * if total size < 100, resize all items
+ * if total size > 100, resize only items not resized in step 1 (newly added)
+ * 3. round excess and apply to last item
+ *
+ * Items may have a scale (ie. items with composition)
+ *
+ * Handles single add or removal, as well as atypical use cases,
+ * such as composition out of sync with containers config
+ * due to composition edits outside of view
+ *
* @param {*} items
- * @param {*} newItem
+ * @param {Number} (optional) index of the item to apply excess to in the event of rounding errors
*/
- function sizeItems(items, newItem) {
- const newItemScale = newItem.scale || 1;
+ function sizeItems(items, index) {
+ let totalSize;
+ const flexItems = items.filter((item) => !item.fixed);
+ const flexItemsWithSize = flexItems.filter((item) => item.size);
+ const flexItemsWithoutSize = flexItems.filter((item) => !item.size);
+ // total number of flexible items, adjusted by each item scale
+ const totalScale = flexItems.reduce((total, item) => {
+ const scale = item.scale ?? 1;
+ return total + scale;
+ }, 0);
- if (!newItem.size && items.length === 1) {
- newItem.size = 100;
- } else {
- if (!newItem.size || newItem.size === 100) {
- newItem.size = Math.round((100 * newItemScale) / sizingContainers);
+ flexItemsWithoutSize.forEach((item) => {
+ const scale = item.scale ?? 1;
+ item.size = Math.round((100 * scale) / totalScale);
+ });
- // Resize oldItems to fit inside remaining space;
- const oldItems = items.filter((item) => !_.isEqual(item, newItem));
- const remainingSize = 100 - newItem.size;
+ totalSize = flexItems.reduce((total, item) => total + item.size, 0);
- oldItems.forEach((item) => {
- const itemScale = item.scale || 1;
- item.size = Math.round((item.size * itemScale * remainingSize) / 100);
- });
+ if (totalSize > 100) {
+ const addedSize = flexItemsWithoutSize.reduce((total, item) => total + item.size, 0);
+ const remainingSize = 100 - addedSize;
- // Ensure items add up to 100 in case of rounding error.
- const total = items.reduce((t, item) => t + item.size, 0);
- const excess = Math.round(100 - total);
- oldItems[oldItems.length - 1].size += excess;
- }
+ flexItemsWithSize.forEach((item) => {
+ const scale = item.scale ?? 1;
+ item.size = Math.round((item.size * scale * remainingSize) / 100);
+ });
+ } else if (totalSize < 100) {
+ const sizeToFill = 100 - totalSize;
+
+ flexItems.forEach((item) => {
+ const scale = item.scale ?? 1;
+ item.size = Math.round((item.size * scale * 100) / sizeToFill);
+ });
+ }
+
+ // Ensure items add up to 100 in case of rounding error.
+ totalSize = flexItems.reduce((total, item) => total + item.size, 0);
+ const excess = Math.round(100 - totalSize);
+
+ if (excess) {
+ const _index = index !== undefined && !items[index].fixed ? index : items.length - 1;
+ items[_index].size += excess;
}
}
- /**
- * Scales items proportionally so total is equal to 100.
- * Assumes that an item was removed from array.
- * @param {*} items
- */
- function sizeToFill(items) {
- if (items.length === 0) {
- return;
+ function toggleFixed(index, fixed) {
+ let addExcessToContainer;
+ const remainingItems = containers.value.slice();
+ const container = remainingItems.splice(index, 1)[0];
+
+ if (container.fixed !== fixed) {
+ if (fixed) {
+ const sizeToFill = 100 - container.size;
+ container.size = Math.round((container.size / 100) * getElSize());
+ remainingItems.forEach((item) => {
+ const scale = item.scale ?? 1;
+ item.size = Math.round((item.size * scale * 100) / sizeToFill);
+ });
+ } else {
+ container.size = Math.round((container.size * 100) / (getElSize() + container.size));
+ addExcessToContainer = index;
+ const remainingSize = 100 - container.size;
+ remainingItems.forEach((item) => {
+ const scale = item.scale ?? 1;
+ item.size = Math.round((item.size * scale * remainingSize) / 100);
+ });
+ }
+
+ container.fixed = fixed;
+ sizeItems(containers.value, addExcessToContainer);
+ callback?.();
}
+ }
- const oldTotal = items.reduce((total, item) => total + item.size, 0);
- items.forEach((item) => {
- const itemScale = item.scale || 1;
- item.size = Math.round((item.size * itemScale * 100) / oldTotal);
- });
+ function sizeFixedContainer(index, size) {
+ const container = containers.value[index];
- // Ensure items add up to 100 in case of rounding error.
- const total = items.reduce((t, item) => t + item.size, 0);
- const excess = Math.round(100 - total);
- items[items.length - 1].size += excess;
+ if (container.fixed) {
+ container.size = size;
+
+ callback?.();
+ } else {
+ console.warn('Use view drag resizing to resize flexible containers.');
+ }
}
return {
addContainer,
removeContainer,
reorderContainers,
+ setContainers,
containers,
startContainerResizing,
containerResizing,
- endContainerResizing
+ endContainerResizing,
+ toggleFixed,
+ sizeFixedContainer
};
}