There's a new `validators` property that can be used to add validators to `StatusInput` instances, which are executed in the same order:
```qml
StatusInput {
text: "Some value"
validators: [...]
}
```
For convenience StatusQ provides some common validation methods, such as `StatusMinLengthValidator`, StatusMaxLengthValidator` and could be extended to other (e.g. email validation etc):
```qml
StatusInput {
text: "Some value"
validators: [
StatusMinLengthValidator { minLength: 3 }
]
}
```
Validators are executed every time the text of the input changes. They are executed in the same order they have been applied, which enables users to create cascading conditions like "First make sure the value is at least 3 characters long, then make sure it matches a certain pattern".
When a validation fails, it sets the validity of the input (`valid: false`) accordingly and optionally exposes additional error information on `StatusInput.errors`:
```qml
StatusInput {
text: "Fo"
onTextChanged: {
if (errors) {
/**
* errors now has the following structure:
* errors: {
* minLength: { minValue: 3, actual: ... }
* }
* Also, `StatusInput` is now `valid = false`
**/
errorMesssage = "Expected " + errors.minLenght.minValue + " but got: "+ errors.minLength.actual; // i18n'able
}
}
validators: [
StatusMinLengthValidator { minLength: 3 }
]
}
```
There can be any number of error objects on the `errors` property, depending on who many validators have been run that failed validation.
Custom validators can be implemented by introducing a new `StatusValidator` type that has to implement a `validate()` function and defines the validators name. The `validate()` function has to return either `true` or `false` depending on whether the value is valid.
Alternatively, the function can return an error object which gets exposed on the underlying input's `errors` property, at which point it's considered invalid as well.
Here's a simple custom validator:
```qml
// HelloValidator.qml
import StatusQ.Controls.Validators 0.1
StatusValidator {
property string name: "hello"
validate: function (value) { // `value` is the `text` value of the underlying control
return value === "hello"
}
}
```
Applying this validators would look like this:
```qml
StatusInput {
text: "Some value"
validators: [
HelloValidator {}
]
onTextChanged: {
if (errors.hello) {
errorMessage = "Doesn't say hello!"
}
}
}
```
Alternatively, validators can return error objects to provide more information about what went wrong. Here's the implementation of the `StatusMinLengthValidator` as an example:
```qml
StatusValidator {
property int minLength: 0
name: "minLength"
validate: function (value) {
return value.length >= minLength ? true : {
min: minLength,
actual: value.length
}
}
}
```
Because validators as components, they can hold any custom properties they need to be configured.
There has been concern that, with this API, error messages need to be potentially defined in multiple places, given that there could be multiple instances of any validator. This is easily solved by having a centralized function figure out what the error message is, given a certain error object:
```qml
StatusInput {
validators: [
StatusMinLengthValidator { minLength: 3 }
]
onTextChanged: {
if (errors) {
errorMessage = getErrorMessage(errors) // this function is provided by global or elsewhere
}
}
}
```
Closes#298
Prior to this commit, setting `charLimit` on `StatusInput` would only have a visual effect
(rendering the char limit), however it wouldn't actually enforce this limit.
This was by intended behaviour, because we wanted to leave some room
for possible validators to kick in (for example a max length validator).
If however the char limit is enforce, such a validator would never kick in.
There seems to be consensus in the team that the limit should be enforced though.
This commit enables that.
The menu has a CloseOnReleaseOutside policy and so it
was closing and immediately re-opened when the kebab icon
was clicked since it's outside the menu area and also was
calling the popup function of the menu. Added dummy bool
property to detect whether the menu is already closed and
not open it again
Closes#308
There's some usage specific color being added to the chat input (which doesn't live in
StatusQ yet). To make sure we don't lose that change, I'm adding the new
colors to StatusQ theming system and have Status Desktop use it for the time being
until `StatusChatInput` is moved to StatusQ anyways.
This extends the popup menu to accept image or icon configurations a la `StatusIconSettings`
and `StatusImageSettings` in menu items, as well as nested menus.
Usage:
```qml
StatusPopupMenu {
StatusMenuItem {
text: "Custom Image icon"
image.source: // image source
}
StatusMenuItem {
text: "Custom identicon icon"
image.source: // identicon source
image.isIdenticon: true
}
StatusMenuItem {
text: "Custom letter identicon"
iconSettings.isLetterIdenticon: true
iconSettings.background.color: "red"
}
}
```
Few things to note:
- Because `StatusMenuItem` is an `Action` type, we can't extend its `icon` property,
so we have to introduce our own (`iconSettings`) which can be of type `StatusIconSettings`
- Where possible, `StatusPopupMenu` will prefer `iconSettings.[...]` over `icon.[...]`,
which means, both would work: `icon.name` and `iconSettings.name`.
This is for consistency's sake. Consumers can switch completely to `iconSettings` if desired.
- When `isLetterIdenticon` is true, `iconSettings.background.color` must be set, similar
to how it work in any other StatusQ component that makes use of this configuration type.
Closes#263
This component can be used to group different sections within a popup menu.
Usage:
```qml
StatusPopupMenu {
StatusMenuItem {
text: "One"
icon.name: "info"
}
StatusMenuHeadline {
text: "Some text"
}
StatusMenuItem {
text: "Two"
icon.name: "info"
}
}
```
There's cases where the arrow on the tooltip needs to be repositioned, for example
when the tooltip doesn't fit into the viewport anymore but is centered below a button.
This adds a `StatusModalDivider` to the header and footer so they don't
have to be put into `content` and therefore won't scroll out of the viewport
if the content exceeds the modal height.
The footer divider is only rendered when there's indeed action buttons
provided.
Closes#265
This commit adds the members and search button which are needed for certain
features in Status Desktop. In views where these aren't needed, each button
can be set `visible: false` individually:
```qml
StatusChatToolBar {
...
membersButton.visible: false
searchButton.visible: false
}
```
Closes#243
This adds support for letter identicons by using the `icon.isLetterIdenticon`
flag:
```qml
StatusListItem {
title: "Some name"
icon.isLetterIdenticon: true
icon.background.color: "orange"
}
```
Closes#239
Adding picker button
* As an example it opens Qt's ColorDialog, as soon as
a color is selected there, the button is colored
with that. Final color picker to be implemented in
a seperate task.
Also minor improvements in main.qml and sandbox.pro
Closes#202
This just introduces a new `StatusImageSettings` property `isIdenticon`
which can be used to determine whether a `StatusRoundedImage` should
render with a background + border (which is the case for identicons).
It also updates the `StatusChatList` delegate to consider that property
and have it properly decide how to render the UI.
Closes#173
This property enables users to pass as factory function to `StatusChatList`
component that determines the profile image of a given chat id.
Usage:
```qml
import StatusQ.Components 0.1
StatusChatList {
...
profileImageFn: function (id) {
// `id` is the model id of the current chat item iterator
return ... // has to return a profile image url
}
}
```
In addition to this property, this commit also makes the component
expect an optional `model.identityImage` in case it's already provided.
That way, `profileImageFn` can be omitted.
Closes#174
Chat list items can open a context on right click as well, so `StatusChatList`
needs to provide an API for users to pass down a `StatusPopupMenu` accordingly.
This is now possible with a dedicated `popupMenu` proporty that can be
used as follows:
```qml
StatusChatList {
...
popupMenu: StatusPopupMenu {
property string chatId
openHandler: function () {
...
}
StatusMenuItem {
...
}
...
}
}
```
As will all `popupMenu` properties in StatusQ component, having this explicit API
option enables us to have control over how triggering components (in this case chat
list items) behave when they open a context menu (e.g. keeping them highlighted as long
as the menu is active).
When defining a `chatId` property, `StatusChatList` will hydrate it with the id of
the chat list item that has triggered the menu.
If there's more logic to be executed upon opening the menu, `openHandler` serves
as a hook similar to other popup menus. Inside the hook, users have access to the
specific `chatId`.
Closes#171
This is because we ran into issues where some component compositions
caused scrollviews to be nested which rendered them non-functional.
For now we're rolling back the idea of components being smart enough
to become scrollable by themselves and have StatusQ consumers handle
scroll behaviour.
This is a wrapping component that can be used to render community chat
lists and categories. It takes care of rendering categories, the top
chat list, as well as becominng scrollable in case the content outgrows
the available space.
Usage:
```qml
import StatusQ.Components 0.1
StatusChatListAndCategories {
chatList.model: ... // non-categorized chat items, pass all chat items here, the component will take care of filtering categorized items out
categoryListModel: ... // available categories (need to have `id` and `name`)
selectedChatId: ...
showCategoryActionButtons: true // default `false` - useful when only admin users can create and mutate categories/channels
onChatItemSelected: ... // `id` is available for selected chat id
categoryPopupMenu: StatusPopupMenu { // optional popup menu for category items
property string categoryId // define this property to have it hydrated with correct id and make it available inside menu items
...
}
popupMenu: StatusPopupMenu { ... } // optional popup menu for whole list, will be triggered with right-click
}
```
Closes#133
This uses the newly introduced `filterFn` in `StatusChatList` to hide chat items
that don't belong to a given category.
It also optionally exposes the `categoryId` on the provided popup menu, so it has
access to it inside menu item triggers.
Chat lists can belong to a category inside of communities, so they should
hold a property `categoryId` that represents that. In addition,
this commit introduces a `filterFn` function that can be used to conditionally
show/hide chat list items.
Closes#154
This commit introduces a `showActionButtons` flag that defaults to `false`
and can be used to render the action buttons provided in the chat list category.
This is useful for cases where only admin users should have the right to
create channels inside categories or mutate a category's state.
Settings `showActionButtons: true` will then render the buttons.
Closes#150
This turned out to be a bug. `model.count` isn't defined on `Repeater`
types.
Also, set `ScrollView` height to `0` if there are not community
tab buttons. This is needed because the columns implicit height
comes with spacing, resulting in some height.
This introduces a new `popupMenu` property that can be used to pass
down a `StatusPopupMenu` to `StatusChatToolBar`.
The reason this is done is so that we get control over its `onClosed`
handler, which is used to remove the menu button's `highlighted` state.
The `highlighted` state is activated inside the component as well when
it's clicked.
Existing signals like `menuButtonClicked` still exist and can be leveraged
for further logic.
Closes#125
This component is used to render chat list categories.
Usage:
```qml
import StatusQ.Components 0.1
StatusChatListCategoryItem {
title: "Public"
onClicked: opened = !opened
onToggleButtonClicked: opened = !opened
onMenuButtonClicked: ...
onAddButtonClicked: ...
addButton.[...]: ... // StatusChatListCategoryItemButton
menuButton.[...]: ... // StatusChatListCategoryItemButton
toggleButton.[...]: ... // StatusChatListCategoryItemButton
}
```
The button components are exposed so their tooltips can be configured
with (internationalized) app messages.
Closes#117
These properties are needed to enable more control over how a list
item implementation can look like.
Content children are exposed as follows:
```qml
StatusListItem {
statusListItemTitle.[...]: ... // StatusBaseText
sensor.[...]: ... // MouseArea
rightPadding: ... // default 16
leftPadding: .. // default 16
}
```
This commit introduce a new `StautsChatList` component that can be used
to render `StatusChatListItem`s, typically for chat and community views.
The component expects a `chatListItems.model` that has the following properties:
```qml
ListModel {
ListElement {
chatId: "0"
name: "#status"
chatType: StatusChatListItem.Type.PublicChat
muted: false
hasUnreadMessages: false
hasMention: false
unreadMessagesCount: 0
iconColor: "blue"
}
...
}
```
It also emits two possible signals:
- `onChatItemSelected(string id)`
- `onChatItemUnmuted(string id)`
Usage:
```qml
import StatusQ.Components 0.1
StatusChatList {
selectedChatId: "0"
chatListItems.model: demoChatListItems
onChatItemSelected: ...
onChatItemUnmuted: ...
}
```
Closes#100
As discussed in #109, when using the `rotation` property of `StatusIcon`
it seems to just mirror the underlying image source.
It turns out that using `rotation` on the image source is enough to have
the `ColorOverlay` rotate as well. Adding a `rotation` to the overlay will
rotate the source that's already rotated, resulting in a mirrored result.
To fix this, we're removing the additional `rotation` from the overlay.
Fixes#109
This is a component to easily render two panel layouts used in different
view of Status Desktop. Designed to be used with `StatusAppLayout`
Usage:
```qml
import StatusQ.Layout 0.1
StatusAppLayout {
...
appView: StackView {
anchors.fill: parent
initialItem: Component {
StatusAppTwoPanelLayout {
leftPanel: Component {...}
rightPanel: Component {...}
}
}
}
}
```
Closes: #78
`StatusBadge` uses a border with same color as its underlying component
to create "fake space" (see `StatusNavTabButton`).
The color depends on the parent component background and its state,
which the `StatusBadge` doesn't know about by itself, so the border color
has to be set explicity whereever it's desired.
This commit introduces (default) colors for the border based on where
badge is used.
This is a settings object to complement the existing `StatusIconSettings`.
Some components, like `StatusRoundIcon` need to be able to configure the
surroundings of the icon their rendering.
In such cases, `StatusIconBackgroundSettings` expose an API that give consumers
a way to configure things like background color, width, height and radius.
This introduces the brand new `StatusChatListItem` which can be used
to render channels, one-to-one chats and community channels in Status
applications.
The component can be used as follows:
```qml
import Status.Components 0.1
StatusChatListItem {
name: "channel-name" // name of channel or user
type: StatusChatListItem.Type.PublicChat // GroupChat | CommunityChat | OneToOneChat
image.source: "profile/image/source" // uses letter identicon instead of not supplied
hasUnreadMessages: true // default `false`
badge.value: 1 // default `0`
muted: true // `default `false`
selected: true // default `false`
onUnmute: ... // signal when unmute icon is clicked
}
```
Closes#65
This is needed to have the underlying SVG's color bleed through and
make use of the image's opacity at the same time.
Using `ColorOverlay` together with (half) transparent colors doesn't
work very well as it makes the underlying SVG color come through,
result in wrong colors.
Applying `opacity` on the image itself cause the `ColorOverlay` to
reduce in opacity as well. So in order to work with transparent
colors, we need a way to turn of the ColorOverlay and work with the
Image's opacity, which is what this change enables.
Very similar to what we're doing with `StatusIconSettings` in https://github.com/status-im/StatusQ/pull/61,
this commit introduces `StatusImageSettings` to expose a consisten image
API across components.
This commit introduces a `StatusRoundIcon` component which renders
a `StatusIcon` inside a rounded rectangle.
This is similar to `StatusRoundButton`, just that it isn't a clickable
component.
With these changes, we also introduce a new `StatusIconSettings` config
object that can be used across component that need icon configuration.
The configuration includes the following properties:
- `name` - Name of the icon and used to locate the asset
- `width`
- `height`
Closes#53
This commit introduces a loading indicator for cases where `icon.source`
is set (a custom image/icon) to improve UX of the component.
It also falls back to a letter identicon in case loading the image source
wasn't successful.
Usage:
```
StatusIconTabButton {
icon.source: "SOME URL THAT CAN'T BE RESOLVED"
icon.color: "red" // in case fallback is used, icon.color will be gray by default
name: "Some channel" // used to generated letter identicon as fallback
}
```
Closes#37
This commit introduces a new `StatusAppNavBar` component that can be used
to create a Status application's tab bar for dedicated tab sections such as
chat, profile, wallet etc.
The component is build in a way that it support declarative and imperative usage
where necessary.
In its most simple form, a `StatusAppNavBar` comes with a single tab button
for the chat section. Such button has to be of type `StatusNavBarTabButton`:
```qml
import StatusQ.Layout 0.1
StatusAppNavBar {
navBarChatButton: StatusNavBarTabButton {
icon.name: "chat"
badge.value: 33
badge.visible: true
tooltip.text: "Chat"
}
}
```
In addition, it's possible to specify a list of `StatusNavBarTabButton` for
other sections of the application using the `navBarTabButtons` property:
```qml
StatusAppNavBar {
...
navBarTabButtons: [
StatusNavBarTabButton {
icon.name: "wallet"
tooltip.text: "Wallet"
},
StatusNavBarTabButton {
icon.name: "browser"
tooltip.text: "Browser"
},
StatusNavBarTabButton {
icon.name: "status-update"
tooltip.text: "Timeline"
}
]
}
```
Lastly, when desired to render tabs for Status community, which can grow
in size, `StatusAppNavBar` exposes a list via the `navBarCommunityTabButtons`
property that can have a `model` and a `delegate`. The `delegate` should also
be a `StatusNavBarTabButton`:
```qml
StatusAppNavBar {
...
navBarCommunityTabButtons.model: someModel.communities
navBarCommunityTabButtons.delegate: StatusNavBarTabButton {
name: model.name
tooltip.text: model.name
anchors.horizontalCenter: parent.horizontalCenter
}
}
```
The amount of community tab buttons can grow as they need until their dedicated
area becomes scrollable, at which point all `navBarTabButtons` will stick to the
bottom of `StatusAppNavBar`.
Closes#18
This component is used to render application tabs in the application
nav bar that is yet to be implemented.
`StatusNavBarTabButton` is a composition of `StatusIconTabButton`, `StatusToolTip`
and `StatusBadge` and exposes each of these to enable customization.
Usage:
```
StatusNavBarTabButton {
checked: true/false // whether or not it's 'active'
name: "string" // used to render a `StatusLetterIdenticon`
badge.value: 30 // `StatusBadge` is exposed as `badge`
tooltip.text: "Some tooltip" // `StatusTooltip` is exposed as `tooltip`
icon.name: "message" // Behaves exactly like `StatusIconTabButton.icon`
}
```
Closes#17
This moves the `StatusToolTip` component into `StatusQ` and applies some of
the changes done by @jrainville in https://github.com/status-im/status-desktop/pull/2447.
Usage:
```
import StatusQ.Controls 0.1
Button {
text: "Hover me!"
StatusToolTip {
visible: parent.hovered
text: "Top"
orientation: StatusToolTip.Orientation.Top // default: Top
}
}
```
Closes#14
This adds the `StatusIconTabButton` componoent to `StatusQ` with some slight
adjustments:
- removes `iconColor` in favour of `icon.color`
- removes `disabledColor` (main reason being that we don't show disabled buttons of this type)
This button handles various cases:
1. Icon tab buttons - An icon button used in Status Desktop for different sections
2. Letter identicon button - Used for community sections that don't have a profile picture
3. Image icon button - Used for community sections that do have a profile picture
Which type is rendered depends on the configuration of the component as shown
in the usage.
Usage:
```
import StatusQ.Controls 0.1
// When `icon.name` is set, it renders a `StatusIcon` button
StatusIconTabButton {
icon.name: "chat"
}
// When `icon.source` is set, it renders a `StatusRoundedImage` button
StatusIconTabButton {
icon.source: "https://pbs.twimg.com/profile_images/1369221718338895873/T_5fny6o_400x400.jpg"
}
// When `name` is set, it renders a `StatusLetterIdenticon` button
StatusIconTabButton {
name: "#status"
}
```
Closes#16
This component can be used to render badges with additional information
as seen in the navbar tab buttons and menu items.
Here's how it can be used:
```
StatusBadge {
value: 2
}
```
By default and based on value, StatusBadge will change its width.
If no value is provided, it renders as badge indicator as seen in the profile
tab button.
Closes#15
Status color palettes don't specify a solid black and white that stay
black and white in both themes.
This commit introduces `black` and `white` as solid colors to `ThemePalette`
so they are automatically inherited in all built-in and custom themes.
Turns out, even though we only use one and the same `font.family` name
throughout our components (namely `Inter` and/or `InterStatus`),
`font.weight` properties set to anything other than `Font.Normal` (which is the default),
will not apply unless all font weights have actually been loaded.
Unfortunately we can put a `FontLoader` as direct child in a `ThemePalette`
so we have to make it actual properties.
These properties aren't really used anywhere and merely serve as a place
to load font assets.
Fixes#30
A `StatusIcon` that rotates infinitely and can be used for indicating
pending states.
Usage:
```
StatusLoadingIndicator {
width: 24 // default: 17
height: 24 // default: 17
color: "red" // default: loading asset color
}
```
Closes#7
Abstract text component that defaults to Status' `Inter` font.
Also known as `StyledText` in Status Desktop.
This will close#20 as it doesn't make sense to implement multiple
components for a single text type. Size and weight of text can be configured
on a per usage basis.
Closes#20
This commit introduces the theming story discussed in #3.
Once this lands, we'll have a new namespace `StatusQ.Core.Theme` which
can be used as follows:
```qml
import StatusQ.Core.Theme
StatusLightTheme {}
StatusDarkTheme {}
Theme.palette.[SOME_THEME_PROP] // as spec'ed in #3
```
Closes#3