@vmz/ui 0.2.1 → 0.2.3

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vmz/ui",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "type": "module",
5
5
  "description": "VMZ UI — official UI framework (component contracts + semantic token requirements). Not a theme. Not a plugin.",
6
6
  "license": "MIT",
@@ -68,7 +68,8 @@
68
68
  "./Header": "./src/components/Header.vmz",
69
69
  "./Sider": "./src/components/Sider.vmz",
70
70
  "./Content": "./src/components/Content.vmz",
71
- "./Footer": "./src/components/Footer.vmz"
71
+ "./Footer": "./src/components/Footer.vmz",
72
+ "./Icon": "./src/components/Icon.vmz"
72
73
  },
73
74
  "files": [
74
75
  "src",
@@ -3,14 +3,30 @@
3
3
  <aside class="vmz-ui-console-shell__sidebar" data-vmz-shell="sidebar" :aria-label="navLabel">
4
4
  <a class="vmz-ui-console-shell__brand" :href="homeHref">{{ brand }}</a>
5
5
  <nav class="vmz-ui-console-shell__nav">
6
- <a v-for="item in nav" :key="item.id" :href="item.href" :data-vmz-nav="item.id">
6
+ <a
7
+ v-for="item in nav"
8
+ :key="item.id"
9
+ :href="item.href"
10
+ :data-vmz-nav="item.id"
11
+ :data-active="item.id === activeNavId ? 'true' : null"
12
+ :aria-current="item.id === activeNavId ? 'page' : null"
13
+ >
7
14
  {{ item.title }}
8
15
  </a>
9
16
  </nav>
10
17
  </aside>
11
18
  <div class="vmz-ui-console-shell__body">
12
19
  <header class="vmz-ui-console-shell__header" data-vmz-shell="header">
13
- <h1 class="vmz-ui-console-shell__title">{{ title }}</h1>
20
+ <div class="vmz-ui-console-shell__header-copy">
21
+ <p v-if="breadcrumbLabel" class="vmz-ui-console-shell__breadcrumb" data-vmz-shell="breadcrumb">{{ breadcrumbLabel }}</p>
22
+ <h1 class="vmz-ui-console-shell__title">{{ title }}</h1>
23
+ <p v-if="subtitle" class="vmz-ui-console-shell__subtitle">{{ subtitle }}</p>
24
+ </div>
25
+ <a
26
+ v-if="primaryActionLabel && primaryActionHref"
27
+ class="vmz-ui-console-shell__action"
28
+ :href="primaryActionHref"
29
+ >{{ primaryActionLabel }}</a>
14
30
  </header>
15
31
  <main class="vmz-ui-console-shell__main" data-vmz-shell="main">
16
32
  <slot />
@@ -25,32 +41,33 @@
25
41
  <style>
26
42
  .vmz-ui-console-shell {
27
43
  display: grid;
28
- grid-template-columns: minmax(12rem, 15rem) minmax(0, 1fr);
44
+ grid-template-columns: 13rem minmax(0, 1fr);
29
45
  grid-template-rows: minmax(100%, 100%);
30
46
  min-height: 100vh;
31
47
  min-height: 100dvh;
32
48
  height: 100dvh;
33
49
  color: inherit;
34
- background: var(--vmz-surface-page, #f5f5f5);
50
+ background: var(--vmz-surface-page, #f5f6f8);
35
51
  }
36
52
 
37
53
  .vmz-ui-console-shell__sidebar {
38
54
  display: flex;
39
55
  flex-direction: column;
40
56
  gap: 1rem;
41
- padding: 1.15rem 0.95rem 1.5rem;
42
- border-right: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
57
+ padding: 1rem 0.75rem 1.5rem;
58
+ border-right: 1px solid var(--vmz-line-subtle, #e5e6eb);
43
59
  min-height: 100%;
44
60
  align-self: stretch;
61
+ background: #fff;
45
62
  }
46
63
 
47
64
  .vmz-ui-console-shell__brand {
48
65
  font: inherit;
49
- font-weight: 700;
50
- font-size: 1.05rem;
51
- letter-spacing: 0.04em;
66
+ font-weight: 600;
67
+ font-size: 1rem;
68
+ letter-spacing: 0;
52
69
  text-decoration: none;
53
- color: var(--vmz-action-primary-background);
70
+ color: #1f2329;
54
71
  }
55
72
 
56
73
  .vmz-ui-console-shell__brand:focus-visible {
@@ -61,16 +78,25 @@
61
78
  .vmz-ui-console-shell__nav {
62
79
  display: flex;
63
80
  flex-direction: column;
64
- gap: 0.35rem;
81
+ gap: 0.25rem;
65
82
  flex: 1;
66
83
  }
67
84
 
68
85
  .vmz-ui-console-shell__nav a {
69
86
  font: inherit;
70
- font-weight: 600;
87
+ font-weight: 400;
88
+ font-size: 0.875rem;
89
+ line-height: 1.375rem;
71
90
  text-decoration: none;
72
- color: var(--vmz-action-primary-active);
91
+ color: #51565d;
73
92
  padding: 0.45rem 0.55rem;
93
+ border-radius: 6px;
94
+ }
95
+
96
+ .vmz-ui-console-shell__nav a[data-active='true'] {
97
+ color: #0958d9;
98
+ font-weight: 500;
99
+ background: #e6f4ff;
74
100
  }
75
101
 
76
102
  .vmz-ui-console-shell__nav a:focus-visible {
@@ -91,33 +117,84 @@
91
117
  position: sticky;
92
118
  top: 0;
93
119
  z-index: 20;
94
- padding: 0.9rem 1.25rem;
95
- border-bottom: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
96
- background: var(--vmz-surface-paper, #fff);
120
+ display: flex;
121
+ align-items: center;
122
+ justify-content: space-between;
123
+ gap: 1rem;
124
+ min-height: 3.5rem;
125
+ padding: 0.75rem 1.5rem;
126
+ border-bottom: 1px solid var(--vmz-line-subtle, #e5e6eb);
127
+ background: #fff;
128
+ }
129
+
130
+ .vmz-ui-console-shell__header-copy {
131
+ display: grid;
132
+ gap: 0.15rem;
133
+ min-width: 0;
134
+ }
135
+
136
+ .vmz-ui-console-shell__breadcrumb {
137
+ margin: 0;
138
+ color: #646a73;
139
+ font-size: 0.8125rem;
140
+ line-height: 1.25rem;
97
141
  }
98
142
 
99
143
  .vmz-ui-console-shell__title {
100
144
  margin: 0;
101
- font-size: 1.15rem;
102
- font-weight: 700;
103
- color: var(--vmz-action-primary-active);
145
+ font-size: 1.5rem;
146
+ line-height: 2rem;
147
+ font-weight: 600;
148
+ color: #1f2329;
149
+ }
150
+
151
+ .vmz-ui-console-shell__subtitle {
152
+ margin: 0;
153
+ color: #51565d;
154
+ font-size: 0.875rem;
155
+ line-height: 1.375rem;
156
+ }
157
+
158
+ .vmz-ui-console-shell__action {
159
+ display: inline-flex;
160
+ align-items: center;
161
+ justify-content: center;
162
+ min-height: 2rem;
163
+ padding: 0.25rem 0.75rem;
164
+ border-radius: 6px;
165
+ background: #1677ff;
166
+ color: #fff;
167
+ font-size: 0.875rem;
168
+ font-weight: 500;
169
+ text-decoration: none;
170
+ white-space: nowrap;
171
+ }
172
+
173
+ .vmz-ui-console-shell__action:hover {
174
+ color: #fff;
175
+ background: #0958d9;
176
+ }
177
+
178
+ .vmz-ui-console-shell__action:focus-visible {
179
+ outline: none;
180
+ box-shadow: 0 0 0 2px var(--vmz-focus-ring);
104
181
  }
105
182
 
106
183
  .vmz-ui-console-shell__main {
107
184
  flex: 1 1 auto;
108
- padding: 1.15rem 1.25rem 1.5rem;
185
+ padding: 1.5rem;
109
186
  display: flex;
110
187
  flex-direction: column;
111
- gap: 0.85rem;
188
+ gap: 1rem;
112
189
  }
113
190
 
114
191
  .vmz-ui-console-shell__footer {
115
192
  margin-top: auto;
116
- padding: 0.85rem 1.25rem 1.1rem;
117
- border-top: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
118
- color: var(--vmz-text-steel, #6b7466);
193
+ padding: 0.85rem 1.5rem 1.1rem;
194
+ border-top: 1px solid var(--vmz-line-subtle, #e5e6eb);
195
+ color: var(--vmz-text-steel, #646a73);
119
196
  font-size: 0.82rem;
120
- background: var(--vmz-surface-paper, #fff);
197
+ background: #fff;
121
198
  }
122
199
 
123
200
  @media (max-width: 40rem) {
@@ -130,7 +207,7 @@
130
207
 
131
208
  .vmz-ui-console-shell__sidebar {
132
209
  border-right: 0;
133
- border-bottom: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
210
+ border-bottom: 1px solid var(--vmz-line-subtle, #e5e6eb);
134
211
  }
135
212
 
136
213
  .vmz-ui-console-shell__nav {
@@ -138,6 +215,16 @@
138
215
  flex-wrap: wrap;
139
216
  flex: 0;
140
217
  }
218
+
219
+ .vmz-ui-console-shell__header {
220
+ flex-direction: column;
221
+ align-items: flex-start;
222
+ padding-inline: 1rem;
223
+ }
224
+
225
+ .vmz-ui-console-shell__main {
226
+ padding: 1rem;
227
+ }
141
228
  }
142
229
  </style>
143
230
 
@@ -150,6 +237,11 @@ export default class ConsoleShell {
150
237
  public brand: string = "VMZ";
151
238
  public homeHref: string = "/";
152
239
  public title: string = "Console";
240
+ public subtitle: string = "";
241
+ public breadcrumbLabel: string = "";
242
+ public primaryActionLabel: string = "";
243
+ public primaryActionHref: string = "";
244
+ public activeNavId: string = "";
153
245
  public navLabel: string = "Console";
154
246
  public footer: string = "";
155
247
  /** @type {{ id: string, title: string, href: string }[]} */
@@ -1,87 +1,78 @@
1
1
  <template>
2
- <span
3
- class="vmz-ui-icon"
4
- data-vmz-ui="icon"
5
- :data-name="name"
6
- :data-size="size"
7
- :role="decorative ? 'presentation' : 'img'"
8
- :aria-hidden="decorative ? 'true' : 'false'"
9
- :aria-label="decorative ? '' : label"
10
- >
11
- <svg class="vmz-ui-icon__svg" viewBox="0 0 24 24" focusable="false" aria-hidden="true">
12
- <path v-if="isCheck" fill="currentColor" d="M9.2 16.6 4.8 12.2l1.4-1.4 3 3 8-8 1.4 1.4z" />
13
- <path v-if="isClose" fill="currentColor" d="M6.4 7.8 7.8 6.4 12 10.6 16.2 6.4 17.6 7.8 13.4 12 17.6 16.2 16.2 17.6 12 13.4 7.8 17.6 6.4 16.2 10.6 12z" />
14
- <path v-if="isSearch" fill="currentColor" d="M10.5 3a7.5 7.5 0 0 1 5.9 12.1l4 4-1.4 1.4-4-4A7.5 7.5 0 1 1 10.5 3zm0 2a5.5 5.5 0 1 0 0 11 5.5 5.5 0 0 0 0-11z" />
15
- <path v-if="isChevronDown" fill="currentColor" d="M7.4 9.1 12 13.7l4.6-4.6 1.4 1.4L12 16.5 6 10.5z" />
16
- <path v-if="isChevronRight" fill="currentColor" d="M9.1 6 15.1 12l-6 6-1.4-1.4L12.3 12 7.7 7.4z" />
17
- <path v-if="isInfo" fill="currentColor" d="M12 2a10 10 0 1 1 0 20 10 10 0 0 1 0-20zm0 8.2a1 1 0 0 0-1 1V17a1 1 0 1 0 2 0v-5.8a1 1 0 0 0-1-1zm0-3.7a1.2 1.2 0 1 0 0 2.4 1.2 1.2 0 0 0 0-2.4z" />
18
- <path v-if="isWarning" fill="currentColor" d="M12 3.2 21 19H3L12 3.2zm0 5.3a1 1 0 0 0-1 1v4.2a1 1 0 1 0 2 0V9.5a1 1 0 0 0-1-1zm0 8.1a1.2 1.2 0 1 0 0 2.4 1.2 1.2 0 0 0 0-2.4z" />
19
- <path v-if="isSuccess" fill="currentColor" d="M12 2a10 10 0 1 1 0 20 10 10 0 0 1 0-20zm-2.5 10.3-2-2 1.4-1.4 2 2 5-5 1.4 1.4z" />
20
- <path v-if="isDanger" fill="currentColor" d="M12 2a10 10 0 1 1 0 20 10 10 0 0 1 0-20zm-1 5h2v6h-2zm0 8h2v2h-2z" />
21
- <path v-if="isMenu" fill="currentColor" d="M4 7h16v2H4zm0 4h16v2H4zm0 4h16v2H4z" />
22
- <path v-if="isPlus" fill="currentColor" d="M11 5h2v6h6v2h-6v6h-2v-6H5v-2h6z" />
23
- <path v-if="isFallback" fill="currentColor" d="M12 2a10 10 0 1 1 0 20 10 10 0 0 1 0-20zm1 5h-2v2h2zm0 4h-2v7h2z" />
24
- </svg>
2
+ <span data-vmz-ui="icon" :data-name="name" :data-size="size" :role='decorative ? "presentation" : "img"' :aria-hidden='decorative ? "true" : "false"' :aria-label='decorative ? "" : label' class="vmz-ui-icon">
3
+ <svg viewBox="0 0 24 24" focusable="false" aria-hidden="true" class="vmz-ui-icon__svg">
4
+ <path fill="currentColor" :d="iconPath()" />
5
+ </svg>
25
6
  </span>
26
7
  </template>
27
-
28
8
  <style>
29
- .vmz-ui-icon {
30
- display: inline-flex;
31
- align-items: center;
32
- justify-content: center;
33
- width: 1em;
34
- height: 1em;
35
- vertical-align: -0.125em;
36
- color: inherit;
37
- line-height: 1;
38
- }
39
- .vmz-ui-icon[data-size='sm'] { font-size: 0.875rem; }
40
- .vmz-ui-icon[data-size='md'] { font-size: 1rem; }
41
- .vmz-ui-icon[data-size='lg'] { font-size: 1.25rem; }
42
- .vmz-ui-icon__svg { width: 1em; height: 1em; display: block; }
9
+ .vmz-ui-icon {
10
+ display: inline-flex;
11
+ align-items: center;
12
+ justify-content: center;
13
+ width: 1em;
14
+ height: 1em;
15
+ vertical-align: -0.125em;
16
+ color: inherit;
17
+ line-height: 1;
18
+ }
19
+ .vmz-ui-icon[data-size="sm"] {
20
+ font-size: 0.875rem;
21
+ }
22
+ .vmz-ui-icon[data-size="md"] {
23
+ font-size: 1rem;
24
+ }
25
+ .vmz-ui-icon[data-size="lg"] {
26
+ font-size: 1.25rem;
27
+ }
28
+ .vmz-ui-icon__svg {
29
+ width: 1em;
30
+ height: 1em;
31
+ display: block;
32
+ }
43
33
  </style>
44
-
45
34
  <script client>
46
35
  /**
47
- * VMZ UI — Icon (minimal official set).
48
- * Parent picks `name`; no useIcon/createIcon. Unknown names render fallback glyph.
49
- */
36
+ * VMZ UI Icon — shell for UI chrome and author-supplied glyphs.
37
+ * Pass `pathD` for custom SVG paths, or `name` for the built-in chrome set.
38
+ * Full semantic registries live in optional `@vmz/ui-icons`.
39
+ */
40
+ const FALLBACK_PATH =
41
+ 'M12 2a10 10 0 1 1 0 20 10 10 0 0 1 0-20zm1 5h-2v2h2zm0 4h-2v7h2z';
42
+
43
+ const CHROME_PATHS = {
44
+ check: 'M9.2 16.6 4.8 12.2l1.4-1.4 3 3 8-8 1.4 1.4z',
45
+ close: 'M6.4 7.8 7.8 6.4 12 10.6 16.2 6.4 17.6 7.8 13.4 12 17.6 16.2 16.2 17.6 12 13.4 7.8 17.6 6.4 16.2 10.6 12z',
46
+ search: 'M10.5 3a7.5 7.5 0 0 1 5.9 12.1l4 4-1.4 1.4-4-4A7.5 7.5 0 1 1 10.5 3zm0 2a5.5 5.5 0 1 0 0 11 5.5 5.5 0 0 0 0-11z',
47
+ 'chevron-down': 'M7.4 9.1 12 13.7l4.6-4.6 1.4 1.4L12 16.5 6 10.5z',
48
+ 'chevron-right': 'M9.1 6 15.1 12l-6 6-1.4-1.4L12.3 12 7.7 7.4z',
49
+ info: 'M12 2a10 10 0 1 1 0 20 10 10 0 0 1 0-20zm0 8.2a1 1 0 0 0-1 1V17a1 1 0 1 0 2 0v-5.8a1 1 0 0 0-1-1zm0-3.7a1.2 1.2 0 1 0 0 2.4 1.2 1.2 0 0 0 0-2.4z',
50
+ warning:
51
+ 'M12 3.2 21 19H3L12 3.2zm0 5.3a1 1 0 0 0-1 1v4.2a1 1 0 1 0 2 0V9.5a1 1 0 0 0-1-1zm0 8.1a1.2 1.2 0 1 0 0 2.4 1.2 1.2 0 0 0 0-2.4z',
52
+ success: 'M12 2a10 10 0 1 1 0 20 10 10 0 0 1 0-20zm-2.5 10.3-2-2 1.4-1.4 2 2 5-5 1.4 1.4z',
53
+ danger: 'M12 2a10 10 0 1 1 0 20 10 10 0 0 1 0-20zm-1 5h2v6h-2zm0 8h2v2h-2z',
54
+ menu: 'M4 7h16v2H4zm0 4h16v2H4zm0 4h16v2H4z',
55
+ plus: 'M11 5h2v6h6v2h-6v6h-2v-6H5v-2h6z',
56
+ };
57
+
58
+ function resolveChromePath(name) {
59
+ const key = String(name || '').trim();
60
+ if (!key) return FALLBACK_PATH;
61
+ return CHROME_PATHS[key] || FALLBACK_PATH;
62
+ }
63
+
50
64
  export default class Icon {
51
- static __vmzHostBox = 'contents';
52
- public name: string = "info";
53
- public size: string = "md";
54
- public label: string = "";
55
- public decorative: boolean = true;
56
- isCheck = false;
57
- isClose = false;
58
- isSearch = false;
59
- isChevronDown = false;
60
- isChevronRight = false;
61
- isInfo = false;
62
- isWarning = false;
63
- isSuccess = false;
64
- isDanger = false;
65
- isMenu = false;
66
- isPlus = false;
67
- isFallback = false;
68
- onMount() {
69
- this._sync();
70
- }
71
- _sync() {
72
- const n = String(this.name || "");
73
- this.isCheck = n === "check";
74
- this.isClose = n === "close";
75
- this.isSearch = n === "search";
76
- this.isChevronDown = n === "chevron-down";
77
- this.isChevronRight = n === "chevron-right";
78
- this.isInfo = n === "info";
79
- this.isWarning = n === "warning";
80
- this.isSuccess = n === "success";
81
- this.isDanger = n === "danger";
82
- this.isMenu = n === "menu";
83
- this.isPlus = n === "plus";
84
- this.isFallback = !(this.isCheck || this.isClose || this.isSearch || this.isChevronDown || this.isChevronRight || this.isInfo || this.isWarning || this.isSuccess || this.isDanger || this.isMenu || this.isPlus);
85
- }
65
+ static __vmzHostBox = 'contents';
66
+ public name: string = 'info';
67
+ public size: string = 'md';
68
+ public label: string = '';
69
+ public decorative: boolean = true;
70
+ /** Optional author SVG path override. When set, `name` is ignored for rendering. */
71
+ public pathD: string = '';
72
+
73
+ iconPath() {
74
+ const explicit = String(this.pathD || '').trim();
75
+ return explicit || resolveChromePath(this.name);
76
+ }
86
77
  }
87
78
  </script>