@omnitend/dashboard-for-laravel 0.22.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,104 @@
1
+ <!--
2
+ @component
3
+ The circular user avatar shown in the dashboard navbar's user menu: a single
4
+ initial on a solid disc, with an optional notification dot.
5
+
6
+ It exists as its own component because `DXDashboardNavbar`'s `user-icon` slot
7
+ is one consumers routinely *decorate* rather than replace — adding an unread
8
+ badge, wrapping it in a link. The navbar's styles are scoped, so slot content
9
+ (compiled in the consumer's scope) can't reuse them; without this component
10
+ every override starts by re-implementing the avatar's CSS, which then drifts
11
+ from the theme. Rendering it here means an override is one line and stays on
12
+ theme.
13
+ -->
14
+ <template>
15
+ <div class="dx-user-avatar">
16
+ <!-- `.user-avatar` (not a `dx-`-prefixed name) because it's the documented
17
+ theming hook consumers already override — see the theming guide. -->
18
+ <span class="user-avatar">
19
+ <!-- @slot Replaces the initial inside the disc (e.g. a photo or an icon). -->
20
+ <slot>{{ resolvedInitial }}</slot>
21
+ </span>
22
+
23
+ <span
24
+ v-if="badge"
25
+ class="dx-user-avatar__badge"
26
+ :class="`bg-${badgeVariant}`"
27
+ >
28
+ <span class="visually-hidden">{{ badgeLabel }}</span>
29
+ </span>
30
+ </div>
31
+ </template>
32
+
33
+ <script setup lang="ts">
34
+ import { computed } from "vue";
35
+ import type { ColorVariant } from "bootstrap-vue-next";
36
+
37
+ const props = withDefaults(
38
+ defineProps<{
39
+ /** The user whose initial is shown. The first letter of `name` is used. */
40
+ user?: { name: string; [key: string]: any } | null;
41
+
42
+ /** Overrides the initial derived from `user.name` (e.g. two-letter initials). */
43
+ initial?: string;
44
+
45
+ /** Show a notification dot on the avatar (e.g. unread updates). */
46
+ badge?: boolean;
47
+
48
+ /** Colour of the notification dot. */
49
+ badgeVariant?: ColorVariant;
50
+
51
+ /**
52
+ * Accessible text for the notification dot, announced to screen readers
53
+ * (the dot itself is a colour-only signal). Ignored unless `badge` is set.
54
+ */
55
+ badgeLabel?: string;
56
+ }>(),
57
+ {
58
+ user: null,
59
+ initial: "",
60
+ badge: false,
61
+ badgeVariant: "danger",
62
+ badgeLabel: "Unread notifications",
63
+ },
64
+ );
65
+
66
+ const resolvedInitial = computed(() => {
67
+ if (props.initial) return props.initial;
68
+ return props.user?.name?.charAt(0).toUpperCase() ?? "";
69
+ });
70
+ </script>
71
+
72
+ <style scoped>
73
+ /* The disc, not the root, carries the size: the root is a positioning context
74
+ for the badge, and giving it the disc's dimensions would clip the dot. */
75
+ .dx-user-avatar {
76
+ position: relative;
77
+ display: inline-flex;
78
+ }
79
+
80
+ .user-avatar {
81
+ width: 32px;
82
+ height: 32px;
83
+ border-radius: 50%;
84
+ background-color: var(--bs-dark);
85
+ color: var(--bs-white);
86
+ display: flex;
87
+ align-items: center;
88
+ justify-content: center;
89
+ font-weight: 600;
90
+ font-size: 14px;
91
+ }
92
+
93
+ .dx-user-avatar__badge {
94
+ position: absolute;
95
+ top: 0;
96
+ right: 0;
97
+ width: 10px;
98
+ height: 10px;
99
+ border-radius: 50%;
100
+ /* Ring the dot in the bar's own background so it reads as a separate mark
101
+ rather than a bite out of the disc's edge. */
102
+ box-shadow: 0 0 0 2px var(--bs-body-bg);
103
+ }
104
+ </style>
@@ -24,6 +24,7 @@ export { default as DXBasicForm } from "./components/extended/DXBasicForm.vue";
24
24
  export { default as DXTable } from "./components/extended/DXTable.vue";
25
25
  export { default as DXDashboardSidebar } from "./components/extended/DXDashboardSidebar.vue";
26
26
  export { default as DXDashboardNavbar } from "./components/extended/DXDashboardNavbar.vue";
27
+ export { default as DXUserAvatar } from "./components/extended/DXUserAvatar.vue";
27
28
 
28
29
  // Base components (wrapped Bootstrap with theming)
29
30
  export { default as DAccordion } from "./components/base/DAccordion.vue";
@@ -118,6 +118,17 @@ export interface FieldDefinition {
118
118
  /** Re-run `optionsLoader` whenever the form model changes. */
119
119
  reloadOptionsOnChange?: boolean;
120
120
 
121
+ /**
122
+ * For `select` fields: render a **searchable** select — type to filter a
123
+ * long option list, while the model still holds `option.value` (an id).
124
+ *
125
+ * A plain `select` is fine for ten options and unusable for hundreds; the
126
+ * `autocomplete` type filters but models the *typed text*, so it can't back
127
+ * a foreign key (picking "Tesco" gives you `"Tesco"`, not `37`). This is the
128
+ * combination high-frequency data entry against a long list needs.
129
+ */
130
+ searchable?: boolean;
131
+
121
132
  /** Number of rows for textarea (default: 3) */
122
133
  rows?: number;
123
134
 
@@ -293,6 +304,43 @@ export interface FieldDefinition {
293
304
  */
294
305
  readonly?: MaybeFn<boolean>;
295
306
 
307
+ /**
308
+ * Display the value as static text instead of a control — no border, no
309
+ * input box (Bootstrap's `.form-control-plaintext`). For values a
310
+ * profile/settings page shows but never lets you edit, where a `readonly`
311
+ * input still reads as "you could edit this, but can't".
312
+ *
313
+ * Implies read-only, for every field type. Anything backed by a real input
314
+ * — the text family, textarea, currency, percentage, autocomplete — renders
315
+ * as static text; control types with no native readonly state
316
+ * (select/radio/checkbox/switch/file) are disabled instead, exactly as they
317
+ * are for `readonly`.
318
+ *
319
+ * Enforced over `inputProps`: unlike the field's other bindings, which
320
+ * `inputProps` may override, a plaintext field cannot be made editable
321
+ * again through it.
322
+ */
323
+ plaintext?: MaybeFn<boolean>;
324
+
325
+ /**
326
+ * Only for `type: "password"`. Show the reveal (eye) toggle that switches
327
+ * the input between masked and plain text. Defaults to `true` — set
328
+ * `false` to render a bare password input.
329
+ */
330
+ revealable?: boolean;
331
+
332
+ /**
333
+ * Include this field's value in the submitted payload. Default `true`.
334
+ *
335
+ * Set `false` for a **presentational** field — a header, an alert, an
336
+ * explanatory block rendered via `span` — that exists to lay out the form,
337
+ * not to hold data. Without it, `DXTable`'s edit modal seeds its form from
338
+ * every `editFields` key, so those decorative fields are POSTed alongside
339
+ * the real ones (an empty string, an empty array) purely because they were
340
+ * declared.
341
+ */
342
+ submit?: boolean;
343
+
296
344
  /**
297
345
  * Conditionally show or hide this field. When omitted the field is
298
346
  * always visible. Boolean or a function of the form model; evaluated