Thing details: Add dialog view for properties with overflow key/values (#3162)

This adds an icon to properties who have values longer then can be
displayed on the thing details page. Clicking this icon brings up the
key and value of the property in a dialog box. This icon only shows if
the value overflows.

---------

Signed-off-by: Dan Cunningham <dan@digitaldan.com>
pull/3170/head
Dan Cunningham 2025-04-28 10:53:07 -07:00 committed by GitHub
parent 7c0e759b04
commit afe96e1c4a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 114 additions and 3 deletions

View File

@ -72,7 +72,15 @@
<f7-accordion-content>
<f7-list>
<f7-list-item class="thing-property" v-for="(value, key) in thing.properties" :key="key"
:title="key" :after="value" />
@click="showFullPropertyIfTruncated(key, value)">
<div slot="title" class="item-title-content">
<span :ref="'titleSpan-' + key">{{ key }}</span>
</div>
<div slot="after" class="item-after-content">
<span :ref="'valueSpan-' + key">{{ value }}</span>
<f7-icon v-if="isTruncated(key, 'title') || isTruncated(key, 'value')" f7="info_circle" size="16" class="truncation-icon" />
</div>
</f7-list-item>
</f7-list>
</f7-accordion-content>
</f7-list-item>
@ -234,7 +242,7 @@ p.action-description
.thing-property
.item-after
max-width 75%
max-width 50%
span
max-width 100%
@ -246,6 +254,35 @@ p.action-description
top calc(var(--f7-navbar-height) + var(--f7-tabbar-height))
height calc(100% - 2*var(--f7-navbar-height))
width 100%
.item-title-content, .item-after-content
display flex
align-items center
overflow hidden
width 100%
span
overflow hidden
text-overflow ellipsis
white-space nowrap
flex-shrink 1
min-width 0
.truncation-icon
margin-left 4px
flex-shrink 0
color var(--f7-text-color-secondary)
.dialog.wide-property-dialog
--f7-dialog-width: 560px
@media (max-width: 599px) {
--f7-dialog-width: 95vw
}
@media (min-width: 768px) {
--f7-dialog-width: 700px
}
</style>
<script>
@ -303,7 +340,8 @@ export default {
thingEnabled: true,
eventSource: null,
thingYaml: null,
notEditableMsg: 'This Thing is not editable because it has been provisioned from a file.'
notEditableMsg: 'This Thing is not editable because it has been provisioned from a file.',
propertyTruncation: {}
}
},
computed: {
@ -356,6 +394,12 @@ export default {
}
},
deep: true
},
'thing.properties': {
handler () {
this.checkPropertyTruncation()
},
deep: true
}
},
methods: {
@ -424,6 +468,7 @@ export default {
this.savedThing = cloneDeep(this.thing)
this.ready = true
this.loading = false
this.checkPropertyTruncation()
})
}
@ -916,7 +961,73 @@ export default {
this.$f7.dialog.alert(e).open()
return false
}
},
checkPropertyTruncation () {
this.$nextTick(() => {
const newTruncationStatus = {}
if (!this.thing || !this.thing.properties || !this.ready) {
if (Object.keys(this.propertyTruncation).length > 0) {
this.propertyTruncation = {}
}
return
}
for (const key in this.thing.properties) {
const getElement = (refName) => {
const ref = this.$refs[refName]
return Array.isArray(ref) ? ref[0] : ref
}
const titleSpan = getElement(`titleSpan-${key}`)
const valueSpan = getElement(`valueSpan-${key}`)
const titleTruncated = titleSpan ? titleSpan.scrollWidth > titleSpan.offsetWidth : false
const valueTruncated = valueSpan ? valueSpan.scrollWidth > valueSpan.offsetWidth : false
if (titleTruncated || valueTruncated) {
newTruncationStatus[key] = { title: titleTruncated, value: valueTruncated }
}
}
if (!fastDeepEqual(this.propertyTruncation, newTruncationStatus)) {
this.propertyTruncation = newTruncationStatus
}
})
},
isTruncated (key, type) {
return !!(this.propertyTruncation[key] && this.propertyTruncation[key][type])
},
showFullProperty (key, value) {
const dialogContent = `
<div class="dialog-title" style="margin-bottom: 8px;">${key}</div>
<pre class="dialog-text" style="font-size: var(--f7-font-size); margin: 0; white-space: pre-wrap; word-wrap: break-word;">${value}</pre>
`
const dialog = this.$f7.dialog.create({
title: 'Property Details',
content: dialogContent,
cssClass: 'wide-property-dialog',
buttons: [
{ text: 'OK' }
]
})
dialog.open()
},
showFullPropertyIfTruncated (key, value) {
const isTitleTruncated = this.isTruncated(key, 'title')
const isValueTruncated = this.isTruncated(key, 'value')
if (isTitleTruncated || isValueTruncated) {
this.showFullProperty(key, value)
}
}
},
mounted () {
this.checkPropertyTruncation()
},
updated () {
this.checkPropertyTruncation()
}
}
</script>