Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 5 additions & 10 deletions docs/components/cn-color-picker.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# CnColorPicker

Themed wrapper around vue-color's `Chrome` picker. Forwards all props and events to the underlying component, so the full vue-color API stays available — this component only exists to remap the picker's hardcoded light-mode palette to Nextcloud CSS variables so it follows the active theme (light, dark, or nldesign).
A color swatch button that opens a themed `Chrome` color picker (vue-color) in a popover. Clicking the swatch square toggles the picker. The active color is shown in the swatch (with a checker pattern behind it so alpha values render correctly). Remaps the picker's hardcoded light-mode palette to Nextcloud CSS variables so it follows the active theme (light, dark, or nldesign).

## Usage

Expand Down Expand Up @@ -30,22 +30,18 @@ export default {

## Props & events

All props and listeners are forwarded to vue-color's `Chrome` component. The most commonly used:

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `value` | `string \| object` | | Current color (string like `'#abcdef'`, `'rgba(...)'`, `'hsl(...)'`, or a vue-color color object). |
| `disabled` | `Boolean` | `false` | Disables the swatch trigger and prevents the popover from opening. |
| `value` | `string \| object` | `null` | Current color (string like `'#abcdef'`, `'rgba(...)'`, `'hsl(...)'`, or a vue-color color object). `null`/empty renders a transparent swatch. |
| `disabled` | `Boolean` | `false` | Disables the swatch button and prevents the popover from opening. |
| `mode` | `'hex' \| 'rgb' \| 'hsl' \| null` | `null` | Lock the numeric-input fields to a single mode and hide the toggle. `null` lets the user switch. The shown fields include alpha when `disable-alpha` is `false` (so `'rgb'` becomes RGBA, `'hsl'` becomes HSLA). |
| `disable-alpha` | `Boolean` | `false` | Forwarded to `Chrome`. Hides the alpha bar and the alpha numeric field. |
| `disable-fields` | `Boolean` | `false` | Forwarded to `Chrome`. Hides the hex/RGB/HSL numeric fields entirely. |

Additional props (e.g. `disable-alpha`, `disable-fields`) are forwarded via `$attrs` to the underlying `Chrome` picker. See vue-color's [`Chrome` component](https://github.com/linx4200/vue-color) for the full prop surface.

| Event | Payload | Description |
|-------|---------|-------------|
| `input` | `{ hex, hex8, rgba, hsl, hsv, a, source }` | Fires whenever the user changes the color via any control. |

See vue-color's [`Chrome` component](https://github.com/linx4200/vue-color) for the full prop and event surface.

## Theming

This component remaps the following Chrome internals to Nextcloud variables:
Expand All @@ -64,7 +60,6 @@ Consumers don't need to add any CSS overrides themselves.

## Notes

- This is purely the picker UI. It does not include a swatch trigger, popover, text input, or format conversion — wire those at the call site if you need them.
- `vue-color` is a direct dependency of this library, so the picker is always available.

## Related
Expand Down
3 changes: 3 additions & 0 deletions docs/components/cn-dashboard-page.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ const { widgets, layout, loading, onLayoutChange } = useDashboardView({
| `type` | String | `'custom'` (default) or `'tile'` |
| `iconUrl` | String | Header icon image URL |
| `iconClass` | String | Header icon CSS class |
| `titleIconPosition` | String | Position of the `widget-{id}-title-icon` slot: `'left'` (before title) or `'right'` (after actions, default) |
| `titleIconColor` | String | CSS color applied to the title-icon slot container (e.g. `'#e74c3c'`) |
| `buttons` | Array | Footer buttons: `[{ text, link }]` |
| `itemApiVersions` | Number[] | NC Dashboard API versions — triggers auto-rendering |
| `reloadInterval` | Number | Auto-refresh interval in seconds (NC widgets) |
Expand Down Expand Up @@ -129,4 +131,5 @@ const { widgets, layout, loading, onLayoutChange } = useDashboardView({
| `header-actions` | — | Extra buttons in the page header (right side) |
| `widget-{widgetId}` | `{ item, widget }` | Custom content for a specific widget |
| `widget-{widgetId}-actions` | `{ item, widget }` | Header action buttons for a specific widget |
| `widget-{widgetId}-title-icon` | `{ item, widget }` | Extra icon in the widget header; position and color controlled by `titleIconPosition` / `titleIconColor` on the widget definition |
| `empty` | — | Custom empty state when no layout items exist |
3 changes: 3 additions & 0 deletions docs/components/cn-widget-wrapper.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ Container shell around a dashboard widget. Provides a header with icon and title
| `flush` | Boolean | `false` | Remove content padding — lets content extend edge-to-edge |
| `iconUrl` | String | `null` | Image URL for the header icon |
| `iconClass` | String | `null` | CSS class for the header icon (e.g. Nextcloud icon class) |
| `titleIconPosition` | String | `'right'` | Position of the `title-icon` slot in the header: `'left'` places it before the title group; `'right'` places it after the actions |
| `titleIconColor` | String | `null` | CSS color value applied to the `title-icon` slot container (e.g. `'#e74c3c'`) |
| `buttons` | Array | `[]` | Footer button links: `[{ text, link }]` |
| `styleConfig` | Object | `{}` | Runtime style overrides: `{ backgroundColor?, borderStyle?, borderWidth?, borderColor?, borderRadius?, padding?: { top, right, bottom, left } }` |

Expand All @@ -55,4 +57,5 @@ Container shell around a dashboard widget. Provides a header with icon and title
|------|-------------|
| default | Widget content rendered in the scrollable body area |
| `actions` | Buttons or controls placed in the right side of the header |
| `title-icon` | Extra icon element rendered in the header at the position controlled by `titleIconPosition` (left of title or right of actions) |
| `footer` | Custom footer content (replaces the `buttons` prop rendering) |
18 changes: 17 additions & 1 deletion src/components/CnDashboardPage/CnDashboardPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,13 @@
:borderless="item.showTitle === false"
:flush="item.flush === true"
:buttons="getWidgetButtons(item)"
:style-config="item.styleConfig || {}">
:style-config="item.styleConfig || {}"
:title-icon-position="getWidgetTitleIconPosition(item)"
:title-icon-color="getWidgetTitleIconColor(item)">
<!-- Per-widget title icon (e.g. #widget-my-work-title-icon) -->
<template v-if="$slots['widget-' + item.widgetId + '-title-icon']" #title-icon>
<slot :name="'widget-' + item.widgetId + '-title-icon'" :item="item" :widget="getWidgetDef(item.widgetId)" />
</template>
<!-- Per-widget header actions (e.g. #widget-my-work-actions) -->
<template v-if="$slots['widget-' + item.widgetId + '-actions']" #actions>
<slot :name="'widget-' + item.widgetId + '-actions'" :item="item" :widget="getWidgetDef(item.widgetId)" />
Expand Down Expand Up @@ -320,6 +326,16 @@ export default {
return def?.buttons || []
},

getWidgetTitleIconPosition(item) {
const def = this.getWidgetDef(item.widgetId)
return def?.titleIconPosition || 'right'
},

getWidgetTitleIconColor(item) {
const def = this.getWidgetDef(item.widgetId)
return def?.titleIconColor || null
},

isTile(item) {
const def = this.getWidgetDef(item.widgetId)
return def?.type === 'tile'
Expand Down
32 changes: 32 additions & 0 deletions src/components/CnWidgetWrapper/CnWidgetWrapper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
:style="wrapperStyles">
<!-- Header -->
<div v-if="showTitle" class="cn-widget-wrapper__header">
<!-- Title icon — left: rendered before the title group -->
<div v-if="$slots['title-icon'] && titleIconPosition === 'left'"
class="cn-widget-wrapper__title-icon"
:style="titleIconColor ? { color: titleIconColor } : {}">
<slot name="title-icon" />
</div>
<div class="cn-widget-wrapper__header-left">
<img
v-if="iconUrl"
Expand All @@ -32,6 +38,12 @@
<div class="cn-widget-wrapper__actions">
<slot name="actions" />
</div>
<!-- Title icon — right: rendered after actions, far right -->
<div v-if="$slots['title-icon'] && titleIconPosition === 'right'"
class="cn-widget-wrapper__title-icon"
:style="titleIconColor ? { color: titleIconColor } : {}">
<slot name="title-icon" />
</div>
</div>

<!-- Content -->
Expand Down Expand Up @@ -114,6 +126,20 @@ export default {
type: String,
default: null,
},
/**
* Position of the title-icon slot in the header.
* 'left' places it before the title; 'right' places it after the actions.
*/
titleIconPosition: {
type: String,
default: 'right',
validator: (v) => ['left', 'right'].includes(v),
},
/** CSS color value applied to the title-icon slot container */
titleIconColor: {
type: String,
default: null,
},
/** Footer action buttons: [{ text, link }] */
buttons: {
type: Array,
Expand Down Expand Up @@ -227,6 +253,12 @@ export default {
flex-shrink: 0;
}

.cn-widget-wrapper__title-icon {
display: flex;
align-items: center;
flex-shrink: 0;
}

.cn-widget-wrapper__footer {
display: flex;
justify-content: flex-end;
Expand Down
Loading