@omnitend/dashboard-for-laravel 0.22.0 → 0.23.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";
@@ -293,6 +293,31 @@ export interface FieldDefinition {
293
293
  */
294
294
  readonly?: MaybeFn<boolean>;
295
295
 
296
+ /**
297
+ * Display the value as static text instead of a control — no border, no
298
+ * input box (Bootstrap's `.form-control-plaintext`). For values a
299
+ * profile/settings page shows but never lets you edit, where a `readonly`
300
+ * input still reads as "you could edit this, but can't".
301
+ *
302
+ * Implies read-only, for every field type. Anything backed by a real input
303
+ * — the text family, textarea, currency, percentage, autocomplete — renders
304
+ * as static text; control types with no native readonly state
305
+ * (select/radio/checkbox/switch/file) are disabled instead, exactly as they
306
+ * are for `readonly`.
307
+ *
308
+ * Enforced over `inputProps`: unlike the field's other bindings, which
309
+ * `inputProps` may override, a plaintext field cannot be made editable
310
+ * again through it.
311
+ */
312
+ plaintext?: MaybeFn<boolean>;
313
+
314
+ /**
315
+ * Only for `type: "password"`. Show the reveal (eye) toggle that switches
316
+ * the input between masked and plain text. Defaults to `true` — set
317
+ * `false` to render a bare password input.
318
+ */
319
+ revealable?: boolean;
320
+
296
321
  /**
297
322
  * Conditionally show or hide this field. When omitted the field is
298
323
  * always visible. Boolean or a function of the form model; evaluated