feat(CommunityPermissions): Dropdown for channels selection added

This commit is contained in:
Michał Cieślak 2022-12-20 01:19:46 +01:00 committed by Michał
parent c980b52c3b
commit d58dd3331b
4 changed files with 439 additions and 1 deletions

View File

@ -0,0 +1,13 @@
import StatusQ.Core.Theme 0.1
CommunityListItem {
implicitHeight: 34
statusListItemTitle.font.pixelSize: 12
statusListItemTitle.color: Theme.palette.baseColor1
checkBox.visible: false
asset.isLetterIdenticon: false
asset.isImage: false
}

View File

@ -0,0 +1,46 @@
import QtQuick 2.14
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
StatusListItem {
property alias checked: checkBox.checked
property alias checkState: checkBox.checkState
readonly property alias checkBox: checkBox
implicitHeight: 44
leftPadding: 8
rightPadding: 3
statusListItemTitle.font.pixelSize: 13
statusListItemTitleArea.anchors.leftMargin: 8
asset.bgWidth: 32
asset.bgHeight: 32
asset.isLetterIdenticon: true
asset.letterSize: 12
asset.isImage: model.icon.includes("data")
asset.width: 32
asset.height: 32
components: [
StatusCheckBox {
id: checkBox
size: StatusCheckBox.Size.Small
rightPadding: 0
}
]
// using MouseArea instead of build-in 'clicked' signal to avoid
// intercepting event by the StatusCheckBox
MouseArea {
anchors.fill: parent
onClicked: {
checkBox.toggle()
checkBox.toggled()
}
cursorShape: Qt.PointingHandCursor
}
}

View File

@ -0,0 +1,376 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Popups 0.1
import shared.controls 1.0
import SortFilterProxyModel 0.2
StatusDropdown {
id: root
property string communityName
property string communityImage
property color communityColor
property var model
property int acceptMode: InDropdown.AcceptMode.Add
enum AcceptMode {
Add, Update
}
width: 289
padding: 8
// force keeping within the bounds of the enclosing window
margins: 0
signal addChannelClicked
signal communitySelected
signal channelsSelected(var channels)
function setSelectedChannels(channels) {
d.setSelectedChannels(channels)
}
onAboutToHide: searcher.text = ""
onAboutToShow: scrollView.Layout.preferredHeight = Math.min(
scrollView.implicitHeight, 420)
QtObject {
id: d
readonly property var selectedChannels: new Map()
signal setSelectedChannels(var channels)
function search(text, searcherText) {
return text.toLowerCase().includes(searcherText.toLowerCase())
}
function resolveEmoji(emoji) {
return !!emoji ? emoji : ""
}
function resolveColor(color, colorId) {
return !!color ? color : Theme.palette.userCustomizationColors[colorId]
}
function addToSelectedChannels(model) {
selectedChannels.set(model.itemId, {
itemId: model.itemId,
name: model.name,
color: d.resolveColor(model.color, model.colorId),
emoji: d.resolveEmoji(model.emoji)
})
}
function removeFromSelectedChannels(model) {
selectedChannels.delete(model.itemId)
}
}
ColumnLayout {
anchors.top: parent.top
anchors.right: parent.right
anchors.left: parent.left
spacing: 0
SearchBox {
id: searcher
Layout.fillWidth: true
topPadding: 0
bottomPadding: 0
minimumHeight: 36
maximumHeight: 36
}
StatusListItem {
Layout.fillWidth: true
Layout.topMargin: 9
Layout.preferredHeight: 44
title: root.communityName
subTitle: qsTr("Community")
asset.name: root.communityImage
asset.color: root.communityColor
asset.isImage: true
asset.width: 32
asset.height: 32
asset.isLetterIdenticon: !asset.name
asset.charactersLen: 2
asset.letterSize: 15
leftPadding: 8
rightPadding: 6
statusListItemTitleArea.anchors.leftMargin: 8
statusListItemTitle.font.pixelSize: 13
statusListItemTitle.font.weight: Font.Medium
statusListItemSubTitle.font.pixelSize: 12
components: [
StatusRadioButton {
id: radioButton
size: StatusRadioButton.Size.Small
rightPadding: 0
}
]
// using MouseArea instead of build-in 'clicked' signal to avoid
// intercepting event by the StatusRadioButton
MouseArea {
anchors.fill: parent
onClicked: {
radioButton.toggle()
radioButton.toggled()
}
cursorShape: Qt.PointingHandCursor
}
}
StatusMenuSeparator {
Layout.fillWidth: true
Layout.topMargin: 4 - implicitHeight / 2
Layout.bottomMargin: 4 - implicitHeight / 2
}
StatusScrollView {
id: scrollView
Layout.fillWidth: true
Layout.bottomMargin: 9
padding: 0
ColumnLayout {
id: scollableColumn
spacing: 0
width: scrollView.width
StatusIconTextButton {
Layout.preferredHeight: 36
leftPadding: 8
spacing: 8
statusIcon: "add"
icon.width: 16
icon.height: 16
iconRotation: 0
text: qsTr("Add channel")
onClicked: root.addChannelClicked()
}
Repeater {
id: topRepeater
model: SortFilterProxyModel {
id: topLevelModel
sourceModel: root.model
sorters: [
RoleSorter { roleName: "isCategory" },
RoleSorter { roleName: "position" }
]
}
ColumnLayout {
id: column
Layout.fillWidth: true
spacing: 0
readonly property var topModel: model
readonly property alias checkBox: loader.item
property int checkedCount: 0
visible: {
if (!model.isCategory)
return d.search(model.name, searcher.text)
|| checkBox.checked
if (checkedCount > 0)
return true
const subItemsCount = subItemsRepeater.count
for (let i = 0; i < subItemsCount; i++)
if (subItemsRepeater.itemAt(i).show)
return true
return false
}
Loader {
id: loader
Layout.fillWidth: true
Layout.topMargin: model.isCategory ? 9 : 0
sourceComponent: model.isCategory
? communityCategoryDelegate
: communityDelegate
Connections {
target: radioButton
function onToggled() {
const checkBox = loader.item.checkBox
checkBox.checked = false
checkBox.onToggled()
}
}
Component {
id: communityDelegate
CommunityListItem {
id: communityItem
title: "#" + model.name
asset.name: model.icon
asset.emoji: d.resolveEmoji(model.emoji)
asset.color: d.resolveColor(model.color,
model.colorId)
checkBox.onToggled: {
if (checked)
radioButton.checked = false
}
checkBox.onCheckedChanged: {
if (checkBox.checked)
d.addToSelectedChannels(model)
else
d.removeFromSelectedChannels(model)
}
Connections {
target: d
function onSetSelectedChannels(channels) {
communityItem.checked = channels.includes(
model.itemId)
}
}
}
}
Component {
id: communityCategoryDelegate
CommunityCategoryListItem {
title: model.name
checkState: {
if (checkedCount === model.subItems.count)
return Qt.Checked
else if (checkedCount === 0)
return Qt.Unchecked
return Qt.PartiallyChecked
}
checkBox.onToggled: {
if (checked)
radioButton.checked = false
subItemsRepeater.setAll(checkState)
}
}
}
}
Repeater {
id: subItemsRepeater
model: SortFilterProxyModel {
sourceModel: topModel.isCategory ? topModel.subItems : null
sorters: RoleSorter { roleName: "position" }
}
function setAll(checkState) {
const subItemsCount = count
for (let i = 0; i < subItemsCount; i++) {
itemAt(i).checkState = checkState
}
}
CommunityListItem {
id: communitySubItem
Layout.fillWidth: true
readonly property bool show: d.search(model.name, searcher.text)
|| checked
visible: show
title: "#" + model.name
asset.name: model.icon
asset.emoji: d.resolveEmoji(model.emoji)
asset.color: d.resolveColor(model.color,
model.colorId)
onCheckedChanged: {
if (checked) {
radioButton.checked = false
d.addToSelectedChannels(model)
} else {
d.removeFromSelectedChannels(model)
}
Qt.callLater(() => checkedCount += checked ? 1 : -1)
}
Connections {
target: d
function onSetSelectedChannels(channels) {
communitySubItem.checked = channels.includes(
model.itemId)
}
}
}
}
}
}
}
}
StatusButton {
Layout.fillWidth: true
text: root.acceptMode === InDropdown.AcceptMode.Add
? qsTr("Add") : qsTr("Update")
onClicked: {
if (radioButton.checked) {
root.communitySelected()
return
}
root.channelsSelected(Array.from(d.selectedChannels.values()))
}
}
}
}

View File

@ -1,3 +1,6 @@
HoldingsDropdown 1.0 HoldingsDropdown.qml
CommunityCategoryListItem 1.0 CommunityCategoryListItem.qml
CommunityListItem 1.0 CommunityListItem.qml
HoldingTypes 1.0 HoldingTypes.qml
HoldingsDropdown 1.0 HoldingsDropdown.qml
InDropdown 1.0 InDropdown.qml
PermissionItem 1.0 PermissionItem.qml