2022-04-10 13:07:36 +02:00
|
|
|
<template>
|
|
|
|
<div class="row plugin entities-container">
|
|
|
|
<Loading v-if="loading" />
|
|
|
|
|
2022-04-12 00:43:22 +02:00
|
|
|
<header>
|
2023-05-03 03:14:46 +02:00
|
|
|
<Selector
|
|
|
|
:entity-groups="entityGroups"
|
|
|
|
:value="selector"
|
|
|
|
@input="selector = $event"
|
|
|
|
@refresh="refresh"
|
|
|
|
@show-variable-modal="variableModalVisible = true"
|
|
|
|
/>
|
2022-04-12 00:43:22 +02:00
|
|
|
</header>
|
2022-04-10 13:07:36 +02:00
|
|
|
|
|
|
|
<div class="groups-canvas">
|
2023-01-03 23:16:14 +01:00
|
|
|
<EntityModal
|
|
|
|
:entity="entities[modalEntityId]"
|
2023-03-22 16:20:29 +01:00
|
|
|
:parent="entities[entities[modalEntityId].parent_id]"
|
|
|
|
:children="childrenByParentId(modalEntityId)"
|
2023-01-03 23:16:14 +01:00
|
|
|
:visible="modalVisible"
|
|
|
|
:config-values="configValuesByParentId(modalEntityId)"
|
|
|
|
@close="onEntityModal"
|
2023-03-21 16:02:02 +01:00
|
|
|
@entity-update="modalEntityId = $event"
|
2023-01-03 23:16:14 +01:00
|
|
|
v-if="modalEntityId && entities[modalEntityId]"
|
2022-04-23 01:01:14 +02:00
|
|
|
/>
|
|
|
|
|
2023-04-29 18:20:41 +02:00
|
|
|
<VariableModal :visible="variableModalVisible" @close="variableModalVisible = false" />
|
2022-04-10 14:27:32 +02:00
|
|
|
<NoItems v-if="!Object.keys(displayGroups || {})?.length">No entities found</NoItems>
|
2022-04-23 01:01:14 +02:00
|
|
|
|
2022-04-10 14:27:32 +02:00
|
|
|
<div class="groups-container" v-else>
|
2022-04-10 13:07:36 +02:00
|
|
|
<div class="group fade-in" v-for="group in displayGroups" :key="group.name">
|
|
|
|
<div class="frame">
|
|
|
|
<div class="header">
|
|
|
|
<span class="section left">
|
2022-11-02 16:33:12 +01:00
|
|
|
<Icon v-bind="entitiesMeta[typesByCategory[group.name]].icon || {}"
|
|
|
|
v-if="selector.grouping === 'category' && entitiesMeta[typesByCategory[group.name]]" />
|
2022-04-10 13:07:36 +02:00
|
|
|
<Icon :class="pluginIcons[group.name]?.class" :url="pluginIcons[group.name]?.imgUrl"
|
|
|
|
v-else-if="selector.grouping === 'plugin' && pluginIcons[group.name]" />
|
|
|
|
</span>
|
|
|
|
|
|
|
|
<span class="section center">
|
2022-11-02 16:33:12 +01:00
|
|
|
<div class="title" v-text="group.name" />
|
2022-04-10 13:07:36 +02:00
|
|
|
</span>
|
|
|
|
|
2022-04-19 23:56:49 +02:00
|
|
|
<span class="section right">
|
|
|
|
<button title="Refresh" @click="refresh(group)">
|
|
|
|
<i class="fa fa-sync-alt" />
|
|
|
|
</button>
|
|
|
|
</span>
|
2022-04-10 13:07:36 +02:00
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="body">
|
2023-03-21 16:02:02 +01:00
|
|
|
<div class="entity-frame"
|
2023-05-02 10:14:03 +02:00
|
|
|
v-for="entity in Object.values(group.entities).sort((a, b) => a.name.localeCompare(b.name))"
|
|
|
|
:key="entity.id">
|
2022-04-12 00:43:22 +02:00
|
|
|
<Entity
|
|
|
|
:value="entity"
|
2023-01-01 23:06:40 +01:00
|
|
|
:children="childrenByParentId(entity.id)"
|
2023-04-17 02:11:13 +02:00
|
|
|
:all-entities="entities"
|
2023-03-21 16:02:02 +01:00
|
|
|
@show-modal="onEntityModal($event)"
|
2023-01-01 23:06:40 +01:00
|
|
|
@input="onEntityInput(entity)"
|
2022-04-12 15:58:19 +02:00
|
|
|
:error="!!errorEntities[entity.id]"
|
2023-05-02 10:14:03 +02:00
|
|
|
:key="entity.id"
|
2022-04-12 00:43:22 +02:00
|
|
|
:loading="!!loadingEntities[entity.id]"
|
2022-04-12 01:10:09 +02:00
|
|
|
@loading="loadingEntities[entity.id] = $event"
|
2023-01-01 23:06:40 +01:00
|
|
|
v-if="!entity.parent_id"
|
2022-04-12 00:43:22 +02:00
|
|
|
/>
|
2022-04-10 13:07:36 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
import Utils from "@/Utils"
|
|
|
|
import Loading from "@/components/Loading";
|
|
|
|
import Icon from "@/components/elements/Icon";
|
2022-04-10 14:27:32 +02:00
|
|
|
import NoItems from "@/components/elements/NoItems";
|
2022-04-10 13:07:36 +02:00
|
|
|
import Entity from "./Entity.vue";
|
|
|
|
import Selector from "./Selector.vue";
|
2022-04-23 01:01:14 +02:00
|
|
|
import EntityModal from "./Modal"
|
2023-04-29 18:20:41 +02:00
|
|
|
import VariableModal from "./VariableModal"
|
2023-01-21 16:58:28 +01:00
|
|
|
import { bus } from "@/bus";
|
2022-04-10 13:07:36 +02:00
|
|
|
import icons from '@/assets/icons.json'
|
|
|
|
import meta from './meta.json'
|
|
|
|
|
|
|
|
export default {
|
|
|
|
name: "Entities",
|
|
|
|
mixins: [Utils],
|
2023-04-29 18:20:41 +02:00
|
|
|
components: {
|
|
|
|
Entity,
|
|
|
|
EntityModal,
|
|
|
|
Icon,
|
|
|
|
Loading,
|
|
|
|
NoItems,
|
|
|
|
Selector,
|
|
|
|
VariableModal,
|
|
|
|
},
|
2022-04-10 13:07:36 +02:00
|
|
|
|
2022-04-12 15:58:19 +02:00
|
|
|
props: {
|
|
|
|
// Entity scan timeout in seconds
|
|
|
|
entityScanTimeout: {
|
|
|
|
type: Number,
|
|
|
|
default: 30,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2022-04-10 13:07:36 +02:00
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
loading: false,
|
2022-04-12 00:43:22 +02:00
|
|
|
loadingEntities: {},
|
2022-04-12 15:58:19 +02:00
|
|
|
errorEntities: {},
|
|
|
|
entityTimeouts: {},
|
2022-04-10 13:07:36 +02:00
|
|
|
entities: {},
|
2023-05-02 10:14:03 +02:00
|
|
|
entityGroups: {
|
|
|
|
id: {},
|
|
|
|
category: {},
|
|
|
|
plugin: {},
|
|
|
|
type: {},
|
|
|
|
},
|
2022-04-23 01:01:14 +02:00
|
|
|
modalEntityId: null,
|
|
|
|
modalVisible: false,
|
2023-04-29 18:20:41 +02:00
|
|
|
variableModalVisible: false,
|
2022-04-10 13:07:36 +02:00
|
|
|
selector: {
|
2023-05-01 10:06:11 +02:00
|
|
|
grouping: 'plugin',
|
2022-04-10 13:07:36 +02:00
|
|
|
selectedEntities: {},
|
2023-05-05 02:33:34 +02:00
|
|
|
selectedGroups: {},
|
2022-04-10 13:07:36 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
computed: {
|
|
|
|
entitiesMeta() {
|
|
|
|
return meta
|
|
|
|
},
|
|
|
|
|
|
|
|
pluginIcons() {
|
|
|
|
return icons
|
|
|
|
},
|
|
|
|
|
2022-11-02 16:33:12 +01:00
|
|
|
typesByCategory() {
|
|
|
|
return Object.entries(meta).reduce((obj, [type, meta]) => {
|
|
|
|
obj[meta.name_plural] = type
|
|
|
|
return obj
|
|
|
|
}, {})
|
|
|
|
},
|
|
|
|
|
2022-04-10 13:07:36 +02:00
|
|
|
displayGroups() {
|
2023-05-05 02:33:34 +02:00
|
|
|
return Object.entries(this.entityGroups[this.selector.grouping])
|
|
|
|
.filter((entry) => this.selector.selectedGroups[entry[0]])
|
|
|
|
.map(
|
2023-01-01 23:06:40 +01:00
|
|
|
([grouping, entities]) => {
|
|
|
|
return {
|
|
|
|
name: grouping,
|
2023-05-02 10:14:03 +02:00
|
|
|
entities: Object.values(entities).filter(
|
2023-01-01 23:06:40 +01:00
|
|
|
(e) => e.id in this.selector.selectedEntities
|
|
|
|
),
|
|
|
|
}
|
2022-04-10 13:07:36 +02:00
|
|
|
}
|
2023-05-05 02:33:34 +02:00
|
|
|
)
|
|
|
|
.filter((group) => group.entities?.length > 0)
|
|
|
|
.sort((a, b) => a.name.localeCompare(b.name))
|
2022-04-10 13:07:36 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
methods: {
|
2023-05-02 10:14:03 +02:00
|
|
|
addEntity(entity) {
|
|
|
|
if (entity.parent_id != null)
|
|
|
|
return // Only group entities that have no parent
|
|
|
|
|
|
|
|
this.entities[entity.id] = entity;
|
|
|
|
['id', 'type', 'category', 'plugin'].forEach((attr) => {
|
|
|
|
if (entity[attr] == null)
|
|
|
|
return
|
|
|
|
|
2023-05-03 02:09:51 +02:00
|
|
|
if (attr == 'id')
|
|
|
|
this.entityGroups[attr][entity[attr]] = entity
|
|
|
|
else {
|
|
|
|
if (!this.entityGroups[attr][entity[attr]])
|
|
|
|
this.entityGroups[attr][entity[attr]] = {}
|
|
|
|
this.entityGroups[attr][entity[attr]][entity.id] = entity
|
|
|
|
}
|
2023-05-02 10:14:03 +02:00
|
|
|
})
|
|
|
|
},
|
2022-11-02 16:33:12 +01:00
|
|
|
|
2023-05-02 10:14:03 +02:00
|
|
|
removeEntity(entity) {
|
|
|
|
if (entity.parent_id != null)
|
|
|
|
return // Only group entities that have no parent
|
2022-04-10 13:07:36 +02:00
|
|
|
|
2023-05-02 10:14:03 +02:00
|
|
|
['id', 'type', 'category', 'plugin'].forEach((attr) => {
|
|
|
|
if (this.entityGroups[attr][entity[attr]][entity.id])
|
|
|
|
delete this.entityGroups[attr][entity[attr]][entity.id]
|
|
|
|
})
|
|
|
|
|
|
|
|
if (this.entities[entity.id])
|
|
|
|
delete this.entities[entity.id]
|
2022-04-10 13:07:36 +02:00
|
|
|
},
|
|
|
|
|
2023-01-14 22:33:53 +01:00
|
|
|
_shouldSkipLoading(entity) {
|
|
|
|
const children = Object.values(this.childrenByParentId(entity.id))
|
|
|
|
const hasReadableChildren = children.filter((child) => {
|
|
|
|
return (
|
|
|
|
!child.is_configuration &&
|
|
|
|
!child.is_write_only &&
|
|
|
|
!child.is_query_disabled
|
|
|
|
)
|
|
|
|
}).length > 0
|
|
|
|
|
|
|
|
return (
|
|
|
|
entity.is_query_disabled ||
|
|
|
|
entity.is_write_only ||
|
|
|
|
(children.length && !hasReadableChildren)
|
|
|
|
)
|
|
|
|
},
|
|
|
|
|
2022-04-19 23:56:49 +02:00
|
|
|
async refresh(group) {
|
2022-04-23 01:01:14 +02:00
|
|
|
const entities = (group ? group.entities : this.entities) || {}
|
2022-04-19 23:56:49 +02:00
|
|
|
const args = {}
|
|
|
|
if (group)
|
2023-04-29 18:20:41 +02:00
|
|
|
args.plugins = Object.values(entities).reduce((obj, entity) => {
|
2022-04-19 23:56:49 +02:00
|
|
|
obj[entity.plugin] = true
|
|
|
|
return obj
|
2023-04-29 18:20:41 +02:00
|
|
|
}, {})
|
2022-04-19 23:56:49 +02:00
|
|
|
|
2022-04-23 01:01:14 +02:00
|
|
|
this.loadingEntities = Object.values(entities).reduce((obj, entity) => {
|
2023-01-14 22:33:53 +01:00
|
|
|
if (this._shouldSkipLoading(entity))
|
2022-11-13 00:54:37 +01:00
|
|
|
return obj
|
|
|
|
|
2022-04-13 11:25:14 +02:00
|
|
|
const self = this
|
2022-04-23 01:01:14 +02:00
|
|
|
const id = entity.id
|
2022-04-13 11:25:14 +02:00
|
|
|
if (this.entityTimeouts[id])
|
|
|
|
clearTimeout(this.entityTimeouts[id])
|
|
|
|
|
2023-05-02 10:14:03 +02:00
|
|
|
this.addEntity(entity)
|
2022-04-13 11:25:14 +02:00
|
|
|
this.entityTimeouts[id] = setTimeout(() => {
|
|
|
|
if (self.loadingEntities[id])
|
|
|
|
delete self.loadingEntities[id]
|
|
|
|
if (self.entityTimeouts[id])
|
|
|
|
delete self.entityTimeouts[id]
|
|
|
|
|
|
|
|
self.errorEntities[id] = entity
|
2022-11-02 22:24:06 +01:00
|
|
|
console.warn(`Scan timeout for ${entity.name}`)
|
2022-04-13 11:25:14 +02:00
|
|
|
}, this.entityScanTimeout * 1000)
|
|
|
|
|
|
|
|
obj[id] = true
|
|
|
|
return obj
|
|
|
|
}, {})
|
2022-04-12 00:43:22 +02:00
|
|
|
|
2022-04-19 23:56:49 +02:00
|
|
|
await this.request('entities.scan', args)
|
2022-04-12 00:43:22 +02:00
|
|
|
},
|
|
|
|
|
2023-05-10 02:26:06 +02:00
|
|
|
async sync(setLoading=true) {
|
|
|
|
if (setLoading)
|
|
|
|
this.loading = true
|
2022-04-10 13:07:36 +02:00
|
|
|
|
|
|
|
try {
|
|
|
|
this.entities = (await this.request('entities.get')).reduce((obj, entity) => {
|
2022-04-23 01:01:14 +02:00
|
|
|
entity.name = entity?.meta?.name_override || entity.name
|
2022-11-02 16:33:12 +01:00
|
|
|
entity.category = meta[entity.type].name_plural
|
2022-04-10 13:07:36 +02:00
|
|
|
entity.meta = {
|
|
|
|
...(meta[entity.type] || {}),
|
|
|
|
...(entity.meta || {}),
|
|
|
|
}
|
|
|
|
|
|
|
|
obj[entity.id] = entity
|
2023-05-02 10:14:03 +02:00
|
|
|
this.addEntity(entity)
|
2022-04-10 13:07:36 +02:00
|
|
|
return obj
|
|
|
|
}, {})
|
|
|
|
|
|
|
|
this.selector.selectedEntities = this.entityGroups.id
|
2023-05-10 02:26:06 +02:00
|
|
|
this.refreshEntitiesCache()
|
2022-04-10 13:07:36 +02:00
|
|
|
} finally {
|
2023-05-10 02:26:06 +02:00
|
|
|
if (setLoading)
|
|
|
|
this.loading = false
|
2022-04-10 13:07:36 +02:00
|
|
|
}
|
|
|
|
},
|
2022-04-11 23:16:29 +02:00
|
|
|
|
2023-05-02 10:14:03 +02:00
|
|
|
childrenByParentId(parentId, selectConfig) {
|
|
|
|
const entity = this.entities?.[parentId]
|
|
|
|
if (!entity?.children_ids?.length)
|
|
|
|
return {}
|
|
|
|
|
|
|
|
return entity.children_ids.reduce((obj, id) => {
|
|
|
|
const child = this.entities[id]
|
|
|
|
if (
|
|
|
|
child && (
|
|
|
|
(!selectConfig && !child.is_configuration) ||
|
|
|
|
(selectConfig && child.is_configuration)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
obj[id] = this.entities[id]
|
|
|
|
return obj
|
|
|
|
}, {})
|
2023-01-03 23:16:14 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
configValuesByParentId(parentId) {
|
2023-05-02 10:14:03 +02:00
|
|
|
return this.childrenByParentId(parentId, true)
|
2023-01-01 23:06:40 +01:00
|
|
|
},
|
|
|
|
|
2022-04-12 15:58:19 +02:00
|
|
|
clearEntityTimeouts(entityId) {
|
|
|
|
if (this.errorEntities[entityId])
|
|
|
|
delete this.errorEntities[entityId]
|
|
|
|
if (this.loadingEntities[entityId])
|
|
|
|
delete this.loadingEntities[entityId]
|
|
|
|
if (this.entityTimeouts[entityId]) {
|
|
|
|
clearTimeout(this.entityTimeouts[entityId])
|
|
|
|
delete this.entityTimeouts[entityId]
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2022-04-12 00:43:22 +02:00
|
|
|
onEntityInput(entity) {
|
2022-11-02 16:33:12 +01:00
|
|
|
entity.category = meta[entity.type].name_plural
|
2022-04-12 15:58:19 +02:00
|
|
|
this.entities[entity.id] = entity
|
|
|
|
this.clearEntityTimeouts(entity.id)
|
2022-04-12 00:43:22 +02:00
|
|
|
if (this.loadingEntities[entity.id])
|
|
|
|
delete this.loadingEntities[entity.id]
|
|
|
|
},
|
|
|
|
|
2022-04-11 23:16:29 +02:00
|
|
|
onEntityUpdate(event) {
|
|
|
|
const entityId = event.entity.id
|
|
|
|
if (entityId == null)
|
|
|
|
return
|
|
|
|
|
2022-04-12 15:58:19 +02:00
|
|
|
this.clearEntityTimeouts(entityId)
|
2022-04-23 01:01:14 +02:00
|
|
|
const entity = {...event.entity}
|
2022-04-24 01:41:45 +02:00
|
|
|
if (event.entity?.state == null)
|
|
|
|
entity.state = this.entities[entityId]?.state
|
2022-04-23 01:01:14 +02:00
|
|
|
if (entity.meta?.name_override?.length)
|
|
|
|
entity.name = entity.meta.name_override
|
|
|
|
else if (this.entities[entityId]?.meta?.name_override?.length)
|
|
|
|
entity.name = this.entities[entityId].meta.name_override
|
|
|
|
else
|
|
|
|
entity.name = event.entity?.name || this.entities[entityId]?.name
|
|
|
|
|
2022-11-02 16:33:12 +01:00
|
|
|
entity.category = meta[entity.type].name_plural
|
2022-04-23 01:01:14 +02:00
|
|
|
entity.meta = {
|
|
|
|
...(meta[event.entity.type] || {}),
|
2022-04-23 17:52:21 +02:00
|
|
|
...(this.entities[entityId]?.meta || {}),
|
2022-04-23 01:01:14 +02:00
|
|
|
...(event.entity?.meta || {}),
|
|
|
|
}
|
|
|
|
|
2023-05-02 10:14:03 +02:00
|
|
|
this.addEntity(entity)
|
2023-01-21 16:58:28 +01:00
|
|
|
bus.publishEntity(entity)
|
2022-04-23 01:01:14 +02:00
|
|
|
},
|
|
|
|
|
2022-04-24 21:40:10 +02:00
|
|
|
onEntityDelete(event) {
|
|
|
|
const entityId = event.entity?.id
|
|
|
|
if (entityId == null)
|
|
|
|
return
|
|
|
|
if (entityId === this.modalEntityId)
|
|
|
|
this.modalEntityId = null
|
|
|
|
if (this.entities[entityId])
|
2023-05-02 10:14:03 +02:00
|
|
|
this.removeEntity(this.entities[entityId])
|
2022-04-24 21:40:10 +02:00
|
|
|
},
|
|
|
|
|
2022-04-23 01:01:14 +02:00
|
|
|
onEntityModal(entityId) {
|
|
|
|
if (entityId) {
|
|
|
|
this.modalEntityId = entityId
|
|
|
|
this.modalVisible = true
|
|
|
|
} else {
|
|
|
|
this.modalEntityId = null
|
|
|
|
this.modalVisible = false
|
2022-04-11 23:16:29 +02:00
|
|
|
}
|
|
|
|
},
|
2023-05-10 02:26:06 +02:00
|
|
|
|
|
|
|
loadCachedEntities() {
|
|
|
|
const cachedEntities = localStorage.getItem('entities')
|
|
|
|
if (cachedEntities) {
|
|
|
|
this.entities = JSON.parse(cachedEntities)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
},
|
|
|
|
|
|
|
|
refreshEntitiesCache() {
|
|
|
|
if (this.loading)
|
|
|
|
return
|
|
|
|
|
|
|
|
window.localStorage.setItem('entities', JSON.stringify(this.entities))
|
|
|
|
},
|
2022-04-10 13:07:36 +02:00
|
|
|
},
|
|
|
|
|
2022-05-01 15:34:45 +02:00
|
|
|
async mounted() {
|
2022-04-11 23:16:29 +02:00
|
|
|
this.subscribe(
|
|
|
|
this.onEntityUpdate,
|
|
|
|
'on-entity-update',
|
|
|
|
'platypush.message.event.entities.EntityUpdateEvent'
|
|
|
|
)
|
|
|
|
|
2022-04-24 21:40:10 +02:00
|
|
|
this.subscribe(
|
|
|
|
this.onEntityDelete,
|
|
|
|
'on-entity-delete',
|
|
|
|
'platypush.message.event.entities.EntityDeleteEvent'
|
|
|
|
)
|
|
|
|
|
2023-05-10 02:26:06 +02:00
|
|
|
if (!this.loadCachedEntities()) {
|
|
|
|
await this.sync()
|
|
|
|
await this.refresh()
|
|
|
|
} else {
|
|
|
|
this.refresh()
|
|
|
|
this.sync(false).then(() => this.refresh())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Refresh the entities cache every 10 seconds
|
|
|
|
setInterval(() => this.refreshEntitiesCache(), 10000)
|
2022-04-10 13:07:36 +02:00
|
|
|
},
|
2023-01-21 16:58:28 +01:00
|
|
|
|
|
|
|
unmounted() {
|
|
|
|
this.unsubscribe('on-entity-update')
|
|
|
|
},
|
2022-04-10 13:07:36 +02:00
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
2022-04-12 16:00:31 +02:00
|
|
|
<style lang="scss" scoped>
|
2022-04-10 13:07:36 +02:00
|
|
|
@import "vars";
|
|
|
|
@import "~@/style/items";
|
|
|
|
|
|
|
|
.entities-container {
|
|
|
|
--groups-per-row: 1;
|
|
|
|
|
|
|
|
@include from($desktop) {
|
|
|
|
--groups-per-row: 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
@include from($fullhd) {
|
|
|
|
--groups-per-row: 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
overflow: auto;
|
|
|
|
color: $default-fg-2;
|
|
|
|
font-weight: 400;
|
|
|
|
|
2022-04-12 00:43:22 +02:00
|
|
|
button {
|
2023-05-06 12:34:27 +02:00
|
|
|
background: transparent;
|
2022-04-12 00:43:22 +02:00
|
|
|
border: 0;
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
color: $default-hover-fg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
header {
|
2022-11-27 12:56:39 +01:00
|
|
|
width: calc(100% - 2px);
|
2022-04-10 13:07:36 +02:00
|
|
|
height: $selector-height;
|
2022-04-12 00:43:22 +02:00
|
|
|
display: flex;
|
|
|
|
background: $default-bg-2;
|
2022-11-27 12:56:39 +01:00
|
|
|
margin-left: 2px;
|
2022-04-12 00:43:22 +02:00
|
|
|
box-shadow: $border-shadow-bottom;
|
2022-04-12 22:24:19 +02:00
|
|
|
position: relative;
|
2022-11-26 01:16:07 +01:00
|
|
|
z-index: 1;
|
2022-04-12 22:24:19 +02:00
|
|
|
|
|
|
|
.right {
|
|
|
|
position: absolute;
|
|
|
|
right: 0;
|
|
|
|
text-align: right;
|
|
|
|
margin-right: 0.5em;
|
2023-04-29 18:20:41 +02:00
|
|
|
padding-right: 0;
|
2022-04-12 22:24:19 +02:00
|
|
|
|
|
|
|
button {
|
|
|
|
padding: 0.5em 0;
|
|
|
|
}
|
|
|
|
}
|
2022-04-10 13:07:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
.groups-canvas {
|
|
|
|
width: 100%;
|
|
|
|
height: calc(100% - #{$selector-height});
|
2022-04-12 22:24:19 +02:00
|
|
|
overflow: auto;
|
2022-04-10 13:07:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
.groups-container {
|
2023-05-06 12:34:27 +02:00
|
|
|
@include until(#{$tablet - 1}) {
|
|
|
|
background: $default-bg-2;
|
|
|
|
}
|
|
|
|
|
2023-05-01 01:22:02 +02:00
|
|
|
@include until(#{$desktop - 1}) {
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
align-items: center;
|
|
|
|
}
|
|
|
|
|
2022-04-10 13:07:36 +02:00
|
|
|
@include from($desktop) {
|
|
|
|
column-count: var(--groups-per-row);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.group {
|
|
|
|
width: 100%;
|
2023-05-01 01:22:02 +02:00
|
|
|
max-width: 600px;
|
2022-04-10 13:07:36 +02:00
|
|
|
max-height: 100%;
|
|
|
|
position: relative;
|
|
|
|
display: flex;
|
|
|
|
break-inside: avoid;
|
2022-04-12 22:24:19 +02:00
|
|
|
|
2023-05-06 12:34:27 +02:00
|
|
|
@include until(#{$tablet - 1}) {
|
|
|
|
padding: 0;
|
|
|
|
}
|
|
|
|
|
2023-05-01 01:22:02 +02:00
|
|
|
@include from($tablet) {
|
2022-04-12 22:24:19 +02:00
|
|
|
padding: $main-margin;
|
|
|
|
}
|
2022-04-10 13:07:36 +02:00
|
|
|
|
|
|
|
.frame {
|
2022-04-12 22:24:19 +02:00
|
|
|
@include from($desktop) {
|
|
|
|
max-height: calc(100vh - #{$header-height} - #{$main-margin});
|
|
|
|
}
|
|
|
|
|
2022-04-10 13:07:36 +02:00
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
flex-grow: 1;
|
|
|
|
position: relative;
|
2023-05-06 12:34:27 +02:00
|
|
|
|
|
|
|
@include from($tablet) {
|
|
|
|
border-radius: 1em;
|
|
|
|
box-shadow: $group-shadow;
|
|
|
|
}
|
2022-04-10 13:07:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
.header {
|
|
|
|
width: 100%;
|
|
|
|
height: $header-height;
|
|
|
|
display: table;
|
|
|
|
background: $header-bg;
|
|
|
|
box-shadow: $header-shadow;
|
2023-05-06 12:34:27 +02:00
|
|
|
|
|
|
|
@include until(#{$tablet - 1}) {
|
|
|
|
border-bottom: 1px solid $border-color-2;
|
|
|
|
}
|
|
|
|
|
|
|
|
@include from($tablet) {
|
|
|
|
border-radius: 1em 1em 0 0;
|
|
|
|
}
|
2022-04-10 13:07:36 +02:00
|
|
|
|
|
|
|
.section {
|
|
|
|
height: 100%;
|
|
|
|
display: table-cell;
|
|
|
|
vertical-align: middle;
|
|
|
|
|
|
|
|
&.left, &.right {
|
|
|
|
width: 10%;
|
|
|
|
}
|
|
|
|
|
2022-04-24 11:30:52 +02:00
|
|
|
&.right {
|
|
|
|
text-align: right;
|
|
|
|
}
|
|
|
|
|
2022-04-10 13:07:36 +02:00
|
|
|
&.center {
|
|
|
|
width: 80%;
|
|
|
|
text-align: center;
|
|
|
|
}
|
|
|
|
}
|
2023-05-06 12:34:27 +02:00
|
|
|
|
|
|
|
.title {
|
|
|
|
text-transform: capitalize;
|
|
|
|
|
|
|
|
@include until(#{$tablet - 1}) {
|
|
|
|
font-weight: bold;
|
|
|
|
}
|
|
|
|
}
|
2022-04-10 13:07:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
.body {
|
|
|
|
max-height: calc(100% - #{$header-height});
|
|
|
|
overflow: auto;
|
|
|
|
flex-grow: 1;
|
|
|
|
|
2023-05-06 12:34:27 +02:00
|
|
|
@include until(#{$tablet - 1}) {
|
|
|
|
background: $default-bg-4;
|
|
|
|
}
|
|
|
|
|
|
|
|
@include from($tablet) {
|
|
|
|
background: $default-bg-2;
|
|
|
|
}
|
|
|
|
|
|
|
|
.entity-frame {
|
|
|
|
background: $background-color;
|
|
|
|
|
|
|
|
@include until(#{$tablet - 1}) {
|
|
|
|
margin: 0.75em 0.25em;
|
|
|
|
border: $default-border-2;
|
|
|
|
border-radius: 1em;
|
|
|
|
}
|
|
|
|
|
|
|
|
@include from($tablet) {
|
|
|
|
&:last-child {
|
|
|
|
border-radius: 0 0 1em 1em;
|
|
|
|
}
|
|
|
|
}
|
2022-04-10 13:07:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-04-23 01:01:14 +02:00
|
|
|
|
|
|
|
:deep(.modal) {
|
2023-05-06 12:34:27 +02:00
|
|
|
@include until(#{$tablet - 1}) {
|
|
|
|
width: calc(100% - 1em);
|
|
|
|
|
|
|
|
.table-row {
|
|
|
|
border-bottom: 1px solid $border-color-2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.table-row {
|
|
|
|
.value {
|
|
|
|
overflow: auto;
|
|
|
|
}
|
2022-04-23 01:01:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
.content {
|
|
|
|
@include until($tablet) {
|
|
|
|
width: 100%;
|
|
|
|
}
|
|
|
|
|
|
|
|
@include from($tablet) {
|
|
|
|
min-width: 30em;
|
|
|
|
}
|
|
|
|
|
|
|
|
.body {
|
|
|
|
padding: 0;
|
|
|
|
|
|
|
|
.table-row {
|
|
|
|
padding: 0.5em;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-04-10 13:07:36 +02:00
|
|
|
}
|
|
|
|
</style>
|