470 lines
16 KiB
QML
470 lines
16 KiB
QML
import QtQuick 2.13
|
||
import QtQuick.Layouts 1.14
|
||
import QtGraphicalEffects 1.0
|
||
|
||
import StatusQ.Core 0.1
|
||
import StatusQ.Core.Theme 0.1
|
||
|
||
/*!
|
||
\qmltype StatusCommunityCard
|
||
\inherits Rectangle
|
||
\inqmlmodule StatusQ.Components
|
||
\since StatusQ.Components 0.1
|
||
\brief It is a community card item that provides relevant information about a community model. Inherits \l{https://doc.qt.io/qt-5/qml-qtquick-rectangle.html}{Rectangle}.
|
||
|
||
The \c StatusCommunityCard is a community card clickable item that represents a data model. The data model is commonly a JavaScript array or a ListModel object.
|
||
|
||
Example of how the component looks like:
|
||
\image status_community_card.png
|
||
Example of how to use it:
|
||
\qml
|
||
StatusCommunityCard {
|
||
locale: "en"
|
||
communityId: model.communityId
|
||
loaded: model.available
|
||
logo: model.icon
|
||
name: model.name
|
||
description: model.description
|
||
members: model.members
|
||
popularity: model.popularity
|
||
categories: model.categories
|
||
|
||
onClicked: { d.navigateToCommunity(communityId) }
|
||
}
|
||
\endqml
|
||
For a list of components available see StatusQ.
|
||
*/
|
||
Rectangle {
|
||
id: root
|
||
|
||
/*!
|
||
\qmlproperty string StatusCommunityCard::communityId
|
||
This property holds the community identifier value.
|
||
*/
|
||
property string communityId: ""
|
||
/*!
|
||
\qmlproperty bool StatusCommunityCard::loaded
|
||
This property holds a boolean value that represents if the community information is loaded or not.
|
||
*/
|
||
property bool loaded: true
|
||
/*!
|
||
\qmlproperty url StatusCommunityCard::logo
|
||
This property holds the community logo source.
|
||
*/
|
||
property url logo: ""
|
||
/*!
|
||
\qmlproperty string StatusCommunityCard::name
|
||
This property holds the community name.
|
||
*/
|
||
property string name: ""
|
||
/*!
|
||
\qmlproperty string StatusCommunityCard::description
|
||
This property holds the community description.
|
||
*/
|
||
property string description: ""
|
||
/*!
|
||
\qmlproperty int StatusCommunityCard::members
|
||
This property holds the community members value.
|
||
*/
|
||
property int members: 0
|
||
/*!
|
||
\qmlproperty int StatusCommunityCard::activeUsers
|
||
This property holds the community active users value.
|
||
*/
|
||
property int activeUsers: 0
|
||
/*!
|
||
\qmlproperty int StatusCommunityCard::popularity
|
||
This property holds the community popularity (community rate).
|
||
*/
|
||
property int popularity: 0
|
||
/*!
|
||
\qmlproperty string StatusCommunityCard::categories
|
||
This property holds the data that will be populated as the tags row of the community.
|
||
|
||
Here an example of the model roles expected:
|
||
\qml
|
||
categories: ListModel {
|
||
ListElement { name: "gaming"; emoji: "🎮"; selected: false}
|
||
ListElement { name: "art"; emoji: "🖼️️"; selected: false}
|
||
ListElement { name: "crypto"; emoji: "💸"; selected: false}
|
||
ListElement { name: "nsfw"; emoji: "🍆"; selected: false}
|
||
ListElement { name: "markets"; emoji: "💎"; selected: false}
|
||
}
|
||
\endqml
|
||
*/
|
||
property ListModel categories: ListModel {}
|
||
/*!
|
||
\qmlproperty string StatusCommunityCard::locale
|
||
This property holds the application locale used to give format to members number representation.
|
||
If not provided, default value is "en".
|
||
*/
|
||
property string locale: "en"
|
||
/*!
|
||
\qmlproperty url StatusCommunityCard::banner
|
||
This property holds the community banner image url.
|
||
*/
|
||
property url banner: ""
|
||
/*!
|
||
\qmlproperty color StatusCommunityCard::communityColor
|
||
This property holds the community color.
|
||
If not provided, default value in Light Theme is "#4360DF" and in Dark Theme is "#88B0FF".
|
||
*/
|
||
property color communityColor: Theme.palette.primaryColor1
|
||
/*!
|
||
\qmlproperty bool StatusCommunityCard::isPrivate
|
||
This property holds if the community is private.
|
||
If not provided, default value is false.
|
||
*/
|
||
property bool isPrivate: false
|
||
/*!
|
||
\qmlproperty url StatusCommunityCard::tokenLogo
|
||
This property holds the image of the token needed if the community is private.
|
||
*/
|
||
property url tokenLogo: ""
|
||
|
||
/*!
|
||
\qmlsignal StatusCommunityCard::clicked(string communityId)
|
||
This signal is emitted when the card item is clicked.
|
||
*/
|
||
signal clicked(string communityId)
|
||
|
||
QtObject {
|
||
id: d
|
||
property int cardWidth: Theme.dp(335)
|
||
property int bannerHeigth: Theme.dp(64)
|
||
property int cardHeigth: Theme.dp(190)
|
||
property int totalHeigth: Theme.dp(230)
|
||
property int margins: Theme.dp(12)
|
||
property int bannerRadius: Theme.dp(20)
|
||
property int cardRadius: Theme.dp(16)
|
||
property color cardColor: Theme.palette.indirectColor1
|
||
property color fontColor: Theme.palette.directColor1
|
||
property color loadingColor1: Theme.palette.baseColor5
|
||
property color loadingColor2: Theme.palette.baseColor4
|
||
|
||
function numberFormat(number) {
|
||
var res = number
|
||
const million = 1000000
|
||
const ks = 1000
|
||
if(number > million) {
|
||
res = number / million
|
||
res = Number(number / million).toLocaleString(Qt.locale(root.locale), 'f', 1) + 'M'
|
||
}
|
||
else if(number > ks) {
|
||
res = number / ks
|
||
res = Number(number / ks).toLocaleString(Qt.locale(root.locale), 'f', 1) + 'K'
|
||
}
|
||
else
|
||
res = Number(number).toLocaleString(Qt.locale(root.locale), 'f', 0)
|
||
return res
|
||
}
|
||
}
|
||
|
||
implicitWidth: d.cardWidth
|
||
implicitHeight: d.totalHeigth
|
||
radius: d.bannerRadius
|
||
color: "transparent"
|
||
layer.enabled: true
|
||
layer.effect: DropShadow {
|
||
source: root
|
||
horizontalOffset: 0
|
||
verticalOffset: Theme.dp(2)
|
||
radius: sensor.containsMouse ? Theme.dp(30) : d.bannerRadius
|
||
samples: 25
|
||
spread: 0
|
||
color: sensor.containsMouse ? Theme.palette.backdropColor : Theme.palette.dropShadow
|
||
}
|
||
|
||
// Community banner:
|
||
Item {
|
||
id: banner
|
||
anchors.top: parent.top
|
||
width: d.cardWidth
|
||
height: d.bannerHeigth
|
||
Rectangle {
|
||
id: mask
|
||
anchors.fill: parent
|
||
radius: d.bannerRadius
|
||
color: root.loaded ? root.communityColor : d.loadingColor2
|
||
}
|
||
Image {
|
||
id: image
|
||
source: root.banner
|
||
anchors.fill: parent
|
||
anchors.centerIn: parent
|
||
fillMode: Image.PreserveAspectFit
|
||
smooth: true
|
||
visible: !root.loaded || !root.banner
|
||
}
|
||
OpacityMask {
|
||
anchors.fill: image
|
||
source: image
|
||
maskSource: mask
|
||
}
|
||
}
|
||
|
||
// Community logo:
|
||
Rectangle {
|
||
z: content.z + 1
|
||
anchors.top: parent.top
|
||
anchors.topMargin: Theme.dp(16)
|
||
anchors.left: parent.left
|
||
anchors.leftMargin: Theme.dp(12)
|
||
width: Theme.dp(48)
|
||
height: width
|
||
radius: width / 2
|
||
color: root.loaded ? d.cardColor : d.loadingColor1
|
||
|
||
StatusRoundedImage {
|
||
visible: root.loaded
|
||
anchors.centerIn: parent
|
||
width: parent.width - Theme.dp(4)
|
||
height: width
|
||
image.source: root.logo
|
||
color: "transparent"
|
||
}
|
||
} // End of community logo
|
||
|
||
// Content card
|
||
Rectangle {
|
||
id: content
|
||
z: banner.z + 1
|
||
visible: root.loaded
|
||
anchors.top: parent.top
|
||
anchors.topMargin: Theme.dp(40)
|
||
width: d.cardWidth
|
||
height: d.cardHeigth
|
||
color: d.cardColor
|
||
radius: d.cardRadius
|
||
clip: true
|
||
// Community restriccions
|
||
Rectangle {
|
||
visible: root.isPrivate
|
||
anchors.top: parent.top
|
||
anchors.topMargin: Theme.dp(8)
|
||
anchors.right: parent.right
|
||
anchors.rightMargin: anchors.topMargin
|
||
width: Theme.dp(48)
|
||
height: Theme.dp(24)
|
||
color: d.loadingColor2
|
||
radius: Theme.dp(200)
|
||
StatusIcon {
|
||
anchors.verticalCenter: parent.verticalCenter
|
||
anchors.leftMargin: 6
|
||
anchors.left: parent.left
|
||
icon: "tiny/unlocked"
|
||
width: Theme.dp(16)
|
||
height: Theme.dp(16)
|
||
color: Theme.palette.baseColor1
|
||
}
|
||
StatusRoundedImage {
|
||
anchors.rightMargin: Theme.dp(2)
|
||
anchors.right: parent.right
|
||
anchors.verticalCenter: parent.verticalCenter
|
||
width: Theme.dp(20)
|
||
height: width
|
||
image.source: root.tokenLogo
|
||
color: "transparent"
|
||
}
|
||
}
|
||
// Community info
|
||
ColumnLayout {
|
||
anchors.fill: parent
|
||
anchors.topMargin: Theme.dp(32)
|
||
anchors.leftMargin: d.margins
|
||
anchors.rightMargin: d.margins
|
||
anchors.bottomMargin: d.margins
|
||
clip: true
|
||
spacing: Theme.dp(6)
|
||
StatusBaseText {
|
||
Layout.alignment: Qt.AlignVCenter
|
||
text: root.name
|
||
font.weight: Font.Bold
|
||
font.pixelSize: Theme.dp(19)
|
||
color: d.fontColor
|
||
}
|
||
StatusBaseText {
|
||
Layout.fillWidth: true
|
||
Layout.fillHeight: true
|
||
text: root.description
|
||
font.pixelSize: Theme.dp(15)
|
||
lineHeight: 1.2
|
||
color: d.fontColor
|
||
maximumLineCount: 2
|
||
wrapMode: Text.WordWrap
|
||
elide: Text.ElideRight
|
||
clip: true
|
||
}
|
||
}
|
||
ColumnLayout {
|
||
anchors.fill: parent
|
||
anchors.topMargin: Theme.dp(116)
|
||
anchors.leftMargin: d.margins
|
||
anchors.rightMargin: d.margins
|
||
anchors.bottomMargin: d.margins
|
||
clip: true
|
||
spacing: Theme.dp(18)
|
||
Row {
|
||
Layout.alignment: Qt.AlignVCenter
|
||
spacing: Theme.dp(20)
|
||
// Members
|
||
Row {
|
||
height: membersTxt.height
|
||
spacing: Theme.dp(4)
|
||
StatusIcon {
|
||
anchors.verticalCenter: parent.verticalCenter
|
||
icon: "tiny/members"
|
||
width: Theme.dp(16)
|
||
height: Theme.dp(16)
|
||
}
|
||
StatusBaseText {
|
||
id: membersTxt
|
||
Layout.alignment: Qt.AlignVCenter
|
||
text: d.numberFormat(root.members)
|
||
font.pixelSize: Theme.dp(15)
|
||
color: d.fontColor
|
||
}
|
||
}
|
||
// Active users:
|
||
Row {
|
||
height: activeUsersTxt.height
|
||
spacing: Theme.dp(4)
|
||
StatusIcon {
|
||
anchors.verticalCenter: parent.verticalCenter
|
||
icon: "tiny/flash"
|
||
width: Theme.dp(14)
|
||
height: Theme.dp(14)
|
||
}
|
||
StatusBaseText {
|
||
id: activeUsersTxt
|
||
Layout.alignment: Qt.AlignVCenter
|
||
text: d.numberFormat(root.activeUsers)
|
||
font.pixelSize: Theme.dp(15)
|
||
color: d.fontColor
|
||
}
|
||
}
|
||
}
|
||
// TODO: Replace by `StatusListItemTagRow` - To be done!
|
||
Row {
|
||
visible: root.categories.count > 0
|
||
Layout.alignment: Qt.AlignVCenter
|
||
Layout.fillWidth: true
|
||
spacing: Theme.dp(8)
|
||
clip: true
|
||
|
||
Repeater {
|
||
model: root.categories
|
||
delegate: StatusListItemTag {
|
||
border.color: Theme.palette.baseColor2
|
||
color: "transparent"
|
||
height: Theme.dp(24)
|
||
radius: Theme.dp(20)
|
||
closeButtonVisible: false
|
||
icon.emoji: model.emoji
|
||
icon.height: Theme.dp(24)
|
||
icon.width: icon.height
|
||
icon.color: "transparent"
|
||
icon.isLetterIdenticon: true
|
||
title: model.name
|
||
titleText.font.pixelSize: Theme.dp(13)
|
||
titleText.color: d.fontColor
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} // End of content card
|
||
|
||
// Loading card
|
||
Rectangle {
|
||
visible: !root.loaded
|
||
anchors.top: parent.top
|
||
anchors.topMargin: Theme.dp(40)
|
||
width: d.cardWidth
|
||
height: d.cardHeigth
|
||
color: d.cardColor
|
||
radius: d.cardRadius
|
||
clip: true
|
||
Rectangle {
|
||
anchors.top: parent.top
|
||
anchors.topMargin: Theme.dp(8)
|
||
anchors.right: parent.right
|
||
anchors.rightMargin: anchors.topMargin
|
||
width: Theme.dp(48)
|
||
height: Theme.dp(24)
|
||
color: d.loadingColor2
|
||
radius: Theme.dp(200)
|
||
}
|
||
ColumnLayout {
|
||
anchors.fill: parent
|
||
anchors.topMargin: Theme.dp(32)
|
||
anchors.margins: d.margins
|
||
clip: true
|
||
spacing: Theme.dp(9)
|
||
Rectangle {
|
||
width: Theme.dp(84)
|
||
height: Theme.dp(16)
|
||
color: d.loadingColor1
|
||
radius: Theme.dp(5)
|
||
}
|
||
Rectangle {
|
||
width: Theme.dp(311)
|
||
height: Theme.dp(16)
|
||
color: d.loadingColor1
|
||
radius: Theme.dp(5)
|
||
}
|
||
Rectangle {
|
||
width: Theme.dp(271)
|
||
height: Theme.dp(16)
|
||
color: d.loadingColor1
|
||
radius: Theme.dp(5)
|
||
}
|
||
Row {
|
||
Layout.topMargin: Theme.dp(13)
|
||
spacing: Theme.dp(16)
|
||
Repeater {
|
||
model: 2
|
||
delegate: Row {
|
||
spacing: Theme.dp(4)
|
||
Rectangle {
|
||
width: Theme.dp(14)
|
||
height: Theme.dp(14)
|
||
color: d.loadingColor1
|
||
radius: width / 2
|
||
}
|
||
Rectangle {
|
||
width: 50
|
||
height: 12
|
||
color: d.loadingColor2
|
||
radius: 5
|
||
}
|
||
}
|
||
}
|
||
}
|
||
Row {
|
||
Layout.topMargin: Theme.dp(12)
|
||
spacing: Theme.dp(8)
|
||
Repeater {
|
||
model: 3
|
||
delegate:
|
||
Rectangle {
|
||
width: Theme.dp(76)
|
||
height: Theme.dp(24)
|
||
color: d.loadingColor2
|
||
radius: Theme.dp(20)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} // End of loading card
|
||
|
||
MouseArea {
|
||
id: sensor
|
||
enabled: root.loaded
|
||
anchors.fill: parent
|
||
cursorShape: root.loaded ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||
hoverEnabled: true
|
||
|
||
onClicked: root.clicked(root.communityId)
|
||
}
|
||
}
|