ketekny-ui-kit 1.0.128 → 1.0.130

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/README.md CHANGED
@@ -205,7 +205,7 @@ Components:
205
205
  - `kSelectButton`, `kTags`, `kSearch`, `kArrayList`, `kList`, `kTextArea`
206
206
  - `kDialog`, `kDrawer`
207
207
  - `kAppHeader`, `kAppFooter`, `kAppMain`, `kHero`
208
- - `kSingleColPage`, `kMultiColPage`, `kTwoColPage` (backwards-compatible alias), `twoColLayout`, `legacyTwoColLayout`
208
+ - `kSingleColPage`, `kMultiColPage`, `twoColLayout`, `legacyTwoColLayout`
209
209
 
210
210
  Other exports:
211
211
 
package/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import kMessage from './src/ui/kMessage.vue'
3
3
  import kButton from './src/ui/kButton.vue'
4
4
  import kCheckbox from './src/ui/kCheckbox.vue'
5
+ import kCard from './src/ui/kCard.vue'
5
6
  import kChip from './src/ui/kChip.vue'
6
7
  import kClipboard from './src/ui/kClipboard.vue'
7
8
  import kCode from './src/ui/kCode.vue'
@@ -38,6 +39,7 @@ import kTree from './src/ui/kTree.vue'
38
39
  import kAppHeader from './src/layout/kAppHeader.vue'
39
40
  import kAppFooter from './src/layout/kAppFooter.vue'
40
41
  import kAppMain from './src/layout/kAppMain.vue'
42
+ import kFormSection from './src/layout/kFormSection.vue'
41
43
  import kHero from './src/layout/kHero.vue'
42
44
  import kSingleColPage from './src/layout/kSingleColPage.vue'
43
45
  import kMultiColPage from './src/layout/kMultiColPage.vue'
@@ -58,7 +60,7 @@ import tailwindPreset from './tailwind-preset.js'
58
60
  // Named exports (tree-shaking friendly)
59
61
  export {
60
62
  // UI Components
61
- kMessage, kCode, kToolbar, kTooltip, kTable, kTabs, kChip, kClipboard, kSpinner, kDatatable, kIcon, kMenu, kSkeleton, kProgressBar, kPagination, kTree,
63
+ kMessage, kCode, kToolbar, kTooltip, kTable, kTabs, kCard, kChip, kClipboard, kSpinner, kDatatable, kIcon, kMenu, kSkeleton, kProgressBar, kPagination, kTree,
62
64
 
63
65
  // Form Components
64
66
  kButton, kCheckbox, kSelect, kUploader, kToggle, kInput, kDateSelector, kEditor, kSelectButton, kTags, kSearch, kArrayList, kList, kTextArea, kForm, kOptionGroup,
@@ -67,7 +69,7 @@ export {
67
69
  kDialog, kDrawer,
68
70
 
69
71
  // Layout Components
70
- kAppHeader, kAppFooter, kAppMain, kHero, kSingleColPage, kMultiColPage, kTwoColPage, twoColLayout, legacyTwoColLayout,
72
+ kAppHeader, kAppFooter, kAppMain, kFormSection, kHero, kSingleColPage, kMultiColPage, kTwoColPage, twoColLayout, legacyTwoColLayout,
71
73
 
72
74
  // Plugins
73
75
  toastPlugin, confirmPlugin, alertPlugin, tooltipPlugin, inputDialogPlugin,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ketekny-ui-kit",
3
3
  "type": "module",
4
- "version": "1.0.128",
4
+ "version": "1.0.130",
5
5
  "description": "A Vue 3 UI component library with Tailwind CSS styling",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -0,0 +1,55 @@
1
+ <template>
2
+ <section :class="sectionClasses">
3
+ <div class="grid gap-6 lg:grid-cols-[240px_minmax(0,1fr)] lg:items-start">
4
+ <div>
5
+ <h3 class="text-lg font-semibold">
6
+ {{ title }}
7
+ </h3>
8
+ <p v-if="help" class="mt-1 text-sm text-slate-500">
9
+ {{ help }}
10
+ </p>
11
+ <div v-if="$slots.left" class="mt-4">
12
+ <slot name="left" />
13
+ </div>
14
+ </div>
15
+
16
+ <div class="w-full" :style="contentStyles">
17
+ <slot />
18
+ </div>
19
+ </div>
20
+ </section>
21
+ </template>
22
+
23
+ <script>
24
+ export default {
25
+ name: 'kFormSection',
26
+ props: {
27
+ title: {
28
+ type: String,
29
+ required: true,
30
+ },
31
+ help: {
32
+ type: String,
33
+ default: '',
34
+ },
35
+ borderTop: {
36
+ type: Boolean,
37
+ default: false,
38
+ },
39
+ contentMaxWidth: {
40
+ type: String,
41
+ default: '56rem',
42
+ },
43
+ },
44
+ computed: {
45
+ sectionClasses() {
46
+ return this.borderTop ? 'border-t border-slate-200 pt-8' : ''
47
+ },
48
+ contentStyles() {
49
+ return {
50
+ maxWidth: this.contentMaxWidth,
51
+ }
52
+ },
53
+ },
54
+ }
55
+ </script>
@@ -5,8 +5,16 @@
5
5
  class="sticky z-30 border-b border-t border-slate-200 bg-slate-50 px-4 backdrop-blur sm:px-6 lg:px-8"
6
6
  :style="{ top: resolvedHeaderExtraTop }"
7
7
  >
8
- <div class="mx-auto flex w-full max-w-none items-center">
9
- <slot name="header-extra" />
8
+ <div class="mx-auto w-full max-w-none py-1">
9
+ <kToolbar
10
+ v-if="hasActions"
11
+ :actions="actions"
12
+ dense
13
+ variant="plain"
14
+ />
15
+ <div v-if="$slots['header-extra']" class="flex items-center">
16
+ <slot name="header-extra" />
17
+ </div>
10
18
  </div>
11
19
  </div>
12
20
 
@@ -49,8 +57,16 @@
49
57
  class="sticky z-30 border-b border-t border-slate-200 bg-slate-50 px-4 backdrop-blur sm:px-6 lg:px-8"
50
58
  :style="{ top: resolvedHeaderExtraTop }"
51
59
  >
52
- <div class="mx-auto flex w-full max-w-none items-center">
53
- <slot name="header-extra" />
60
+ <div class="mx-auto w-full max-w-none py-1">
61
+ <kToolbar
62
+ v-if="hasActions"
63
+ :actions="actions"
64
+ dense
65
+ variant="plain"
66
+ />
67
+ <div v-if="$slots['header-extra']" class="flex items-center">
68
+ <slot name="header-extra" />
69
+ </div>
54
70
  </div>
55
71
  </div>
56
72
 
@@ -65,10 +81,11 @@
65
81
  <script>
66
82
  import packageJson from '../../package.json'
67
83
  import kButton from '../ui/kButton.vue'
84
+ import kToolbar from '../ui/kToolbar.vue'
68
85
 
69
86
  export default {
70
87
  name: 'PageShell',
71
- components: { kButton },
88
+ components: { kButton, kToolbar },
72
89
  props: {
73
90
  appTitle: {
74
91
  type: String,
@@ -86,6 +103,10 @@ export default {
86
103
  type: String,
87
104
  default: '',
88
105
  },
106
+ actions: {
107
+ type: Array,
108
+ default: () => [],
109
+ },
89
110
  headerExtraTop: {
90
111
  type: String,
91
112
  default: '0px',
@@ -104,11 +125,14 @@ export default {
104
125
  this.$slots['header-actions']
105
126
  )
106
127
  },
128
+ hasActions() {
129
+ return Array.isArray(this.actions) && this.actions.length > 0
130
+ },
107
131
  showHeaderExtraBeforeTitle() {
108
- return this.headerExtraBeforeTitle && Boolean(this.$slots['header-extra'])
132
+ return this.headerExtraBeforeTitle && (Boolean(this.$slots['header-extra']) || this.hasActions)
109
133
  },
110
134
  showHeaderExtraAfterTitle() {
111
- return !this.headerExtraBeforeTitle && Boolean(this.$slots['header-extra'])
135
+ return !this.headerExtraBeforeTitle && (Boolean(this.$slots['header-extra']) || this.hasActions)
112
136
  },
113
137
  resolvedHeaderExtraTop() {
114
138
  return this.hasTopSection ? this.headerExtraTop : '0px'
@@ -48,11 +48,19 @@
48
48
  </section>
49
49
 
50
50
  <div
51
- v-if="$slots['header-extra']"
51
+ v-if="$slots['header-extra'] || hasActions"
52
52
  class="shrink-0 border-b border-t border-slate-200 bg-slate-50 px-4 backdrop-blur"
53
53
  >
54
- <div class="mx-auto flex w-full max-w-none items-center">
55
- <slot name="header-extra" />
54
+ <div class="mx-auto w-full max-w-none py-1">
55
+ <kToolbar
56
+ v-if="hasActions"
57
+ :actions="actions"
58
+ dense
59
+ variant="plain"
60
+ />
61
+ <div v-if="$slots['header-extra']" class="flex items-center">
62
+ <slot name="header-extra" />
63
+ </div>
56
64
  </div>
57
65
  </div>
58
66
 
@@ -97,11 +105,11 @@
97
105
  <div class="mx-auto flex min-h-0 w-full max-w-none flex-1 gap-2">
98
106
  <aside
99
107
  v-if="showLeftSidebar"
100
- class="min-h-0 shrink-0 overflow-y-auto bg-slate-100 px-4 sm:px-6 lg:ml-8 lg:pr-4 lg:pl-0"
101
- :class="mobileSidebarClass('left')"
108
+ class="min-h-0 shrink-0 overflow-y-auto bg-slate-100"
109
+ :class="[mobileSidebarClass('left'), sidebarSurfaceClass('left')]"
102
110
  :style="mobileSidebarStyle('left')"
103
111
  >
104
- <div v-if="effectiveSidebarLeftTitle" class="pb-3">
112
+ <div v-if="effectiveSidebarLeftTitle" :class="sidebarTitleWrapClass('left')">
105
113
  <p class="text-xs font-semibold uppercase tracking-[0.22em] text-slate-500">
106
114
  {{ effectiveSidebarLeftTitle }}
107
115
  </p>
@@ -115,11 +123,11 @@
115
123
  </section>
116
124
  <aside
117
125
  v-if="showRightSidebar"
118
- class="min-h-0 shrink-0 overflow-y-auto bg-slate-100 px-4 sm:px-6 lg:mr-8 lg:pl-4 lg:pr-0"
119
- :class="mobileSidebarClass('right')"
126
+ class="min-h-0 shrink-0 overflow-y-auto bg-slate-100"
127
+ :class="[mobileSidebarClass('right'), sidebarSurfaceClass('right')]"
120
128
  :style="mobileSidebarStyle('right')"
121
129
  >
122
- <div v-if="sidebarRightTitle" class="pb-3">
130
+ <div v-if="sidebarRightTitle" :class="sidebarTitleWrapClass('right')">
123
131
  <p class="text-xs font-semibold uppercase tracking-[0.22em] text-slate-500">
124
132
  {{ sidebarRightTitle }}
125
133
  </p>
@@ -135,6 +143,7 @@
135
143
  import packageJson from '../../package.json'
136
144
  import kButton from '../ui/kButton.vue'
137
145
  import kIcon from '../ui/kIcon.vue'
146
+ import kToolbar from '../ui/kToolbar.vue'
138
147
 
139
148
  const LG_VIEWPORT_QUERY = '(min-width: 1024px)'
140
149
  const MAIN_MENU_TOGGLE_EVENT = 'ketekny:two-col-layout-mobile-menu'
@@ -144,7 +153,7 @@ const MOBILE_SWIPE_VERTICAL_TOLERANCE = 48
144
153
 
145
154
  export default {
146
155
  name: 'MultiColPage',
147
- components: { kButton, kIcon },
156
+ components: { kButton, kIcon, kToolbar },
148
157
  props: {
149
158
  appTitle: {
150
159
  type: String,
@@ -162,6 +171,10 @@ export default {
162
171
  type: String,
163
172
  default: '',
164
173
  },
174
+ actions: {
175
+ type: Array,
176
+ default: () => [],
177
+ },
165
178
  sidebarWidth: {
166
179
  type: String,
167
180
  default: '300px',
@@ -170,6 +183,14 @@ export default {
170
183
  type: String,
171
184
  default: '',
172
185
  },
186
+ sidebarDense: {
187
+ type: Boolean,
188
+ default: false,
189
+ },
190
+ sidebarLeftDense: {
191
+ type: Boolean,
192
+ default: false,
193
+ },
173
194
  sidebarTitle: {
174
195
  type: String,
175
196
  default: '',
@@ -182,6 +203,10 @@ export default {
182
203
  type: String,
183
204
  default: '',
184
205
  },
206
+ sidebarRightDense: {
207
+ type: Boolean,
208
+ default: false,
209
+ },
185
210
  sidebarRightTitle: {
186
211
  type: String,
187
212
  default: '',
@@ -222,12 +247,18 @@ export default {
222
247
  hasRightSidebar() {
223
248
  return Boolean(this.$slots['sidebar-right'])
224
249
  },
250
+ hasActions() {
251
+ return Array.isArray(this.actions) && this.actions.length > 0
252
+ },
225
253
  effectiveSidebarLeftWidth() {
226
254
  return this.sidebarLeftWidth || this.sidebarWidth || '300px'
227
255
  },
228
256
  effectiveSidebarLeftTitle() {
229
257
  return this.sidebarLeftTitle || this.sidebarTitle
230
258
  },
259
+ effectiveSidebarLeftDense() {
260
+ return this.sidebarLeftDense || this.sidebarDense
261
+ },
231
262
  effectiveSidebarRightWidth() {
232
263
  return this.sidebarRightWidth || this.effectiveSidebarLeftWidth
233
264
  },
@@ -286,6 +317,7 @@ export default {
286
317
  return {
287
318
  side,
288
319
  title: side === 'right' ? this.sidebarRightTitle : this.effectiveSidebarLeftTitle,
320
+ dense: side === 'right' ? this.sidebarRightDense : this.effectiveSidebarLeftDense,
289
321
  isDesktopViewport: this.isDesktopViewport,
290
322
  mobileSidebarOpen: this.mobileSidebarOpen,
291
323
  mobileSidebarSide: this.mobileSidebarSide,
@@ -349,6 +381,20 @@ export default {
349
381
  sidebarTitleForSide(side) {
350
382
  return side === 'right' ? this.sidebarRightTitle : this.effectiveSidebarLeftTitle
351
383
  },
384
+ sidebarDenseForSide(side) {
385
+ return side === 'right' ? this.sidebarRightDense : this.effectiveSidebarLeftDense
386
+ },
387
+ sidebarSurfaceClass(side) {
388
+ if (this.sidebarDenseForSide(side)) {
389
+ return ''
390
+ }
391
+ return side === 'right'
392
+ ? 'px-4 sm:px-6 lg:mr-8 lg:pl-4 lg:pr-0'
393
+ : 'px-4 sm:px-6 lg:ml-8 lg:pr-4 lg:pl-0'
394
+ },
395
+ sidebarTitleWrapClass(side) {
396
+ return this.sidebarDenseForSide(side) ? '' : 'pb-3'
397
+ },
352
398
  mobileSidebarButtonLabel(side) {
353
399
  if (this.mobileSidebarOpen && this.mobileSidebarSide === side) {
354
400
  return 'Hide sidebar'
@@ -0,0 +1,51 @@
1
+ <template>
2
+ <article class="overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm">
3
+ <header
4
+ v-if="hasHeader"
5
+ class="border-b border-slate-200 px-5 py-4"
6
+ >
7
+ <slot name="header">
8
+ <div>
9
+ <p v-if="title" class="text-sm font-semibold text-slate-900">
10
+ {{ title }}
11
+ </p>
12
+ <p v-if="subtitle" :class="title ? 'mt-1 text-sm text-slate-500' : 'text-sm text-slate-500'">
13
+ {{ subtitle }}
14
+ </p>
15
+ </div>
16
+ </slot>
17
+ </header>
18
+
19
+ <div class="px-5 py-4">
20
+ <slot />
21
+ </div>
22
+
23
+ <footer
24
+ v-if="$slots.footer"
25
+ class="border-t border-slate-200 px-5 py-4"
26
+ >
27
+ <slot name="footer" />
28
+ </footer>
29
+ </article>
30
+ </template>
31
+
32
+ <script>
33
+ export default {
34
+ name: 'kCard',
35
+ props: {
36
+ title: {
37
+ type: String,
38
+ default: '',
39
+ },
40
+ subtitle: {
41
+ type: String,
42
+ default: '',
43
+ },
44
+ },
45
+ computed: {
46
+ hasHeader() {
47
+ return Boolean(this.$slots.header || this.title || this.subtitle)
48
+ },
49
+ },
50
+ }
51
+ </script>