picocrank 1.5.0 → 1.6.1

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": "picocrank",
3
- "version": "1.5.0",
3
+ "version": "1.6.1",
4
4
  "description": "A set of zero dependency Vue components built on the Femtocrank global CSS theme.",
5
5
  "main": "vite.config.js",
6
6
  "scripts": {
@@ -628,5 +628,69 @@ function handleContextMenu(event: CalendarEvent, mouseEvent: MouseEvent) {
628
628
  padding: 0.25rem 0.5rem;
629
629
  }
630
630
  }
631
- </style>
632
631
 
632
+ @media (prefers-color-scheme: dark) {
633
+ .calendar-container {
634
+ background: #565656;
635
+ border-color: #374151;
636
+ border: 1px solid #565656;
637
+ }
638
+
639
+ .calendar-day.today {
640
+ background: #646c70;
641
+ }
642
+
643
+ .calendar-day.weekend {
644
+ background: #444;
645
+ }
646
+
647
+ .calendar-day.weekend:hover {
648
+ background: #1a1a1a;
649
+ }
650
+
651
+ .calendar-day.next-month,
652
+ .calendar-day.prev-month {
653
+ background: #1a1a1a !important;
654
+ }
655
+
656
+ .calendar-day.next-month:hover,
657
+ .calendar-day.prev-month:hover {
658
+ background: #374151;
659
+ }
660
+
661
+ .calendar-day.today:hover {
662
+ background: #374151;
663
+ }
664
+
665
+ .calendar-day {
666
+ border: 1px solid #3b3b3b;
667
+ }
668
+
669
+ .day-number {
670
+ color: #f9fafb;
671
+ }
672
+
673
+ .day-header {
674
+ color: #f9fafb;
675
+ background: #444 !important;
676
+ border: 1px solid #374151;
677
+ border-color: #374151;
678
+ }
679
+
680
+ .day-content:hover .day-number.clickable {
681
+ color: #f9fafb;
682
+ }
683
+
684
+ .calendar-day:hover {
685
+ background-color: #374151;
686
+ }
687
+
688
+ .calendar-day.today:hover {
689
+ background: #374151;
690
+ }
691
+
692
+ .calendar-day.weekend:hover {
693
+ background: #374151;
694
+ }
695
+ }
696
+ </style>
@@ -7,7 +7,7 @@
7
7
  <div class = "fg1" />
8
8
 
9
9
  <button id = "sidebar-toggler-button" aria-label = "Open sidebar navigation" aria-pressed = "false" aria-haspopup = "menu" class = "neutral">
10
- <HugeiconsIcon :icon = "Menu01Icon" width = "1em" height = "1em" />
10
+ <HugeiconsIcon :icon = "Menu01Icon" width = "1em" height = "1em" :strokeWidth = 3 />
11
11
  </button>
12
12
  </div>
13
13
 
@@ -55,6 +55,11 @@
55
55
 
56
56
  <style scoped>
57
57
  button {
58
- color: #bbb;
58
+ border: 0;
59
+ color: #fff;
59
60
  }
60
- </style>
61
+
62
+ button:hover {
63
+ background-color: transparent;
64
+ }
65
+ </style>
@@ -2,15 +2,28 @@
2
2
  <div class="pagination">
3
3
  <div class="pagination-info">
4
4
  <span class="pagination-text">
5
- Showing {{ startItem + 1 }}-{{ endItem }} of {{ total }} {{ itemTitle }}
5
+ Page size:
6
+ <select
7
+ id="page-size"
8
+ v-model="localPageSize"
9
+ @change="handlePageSizeChange"
10
+ class="page-size-select"
11
+ >
12
+ <option value="10">10</option>
13
+ <option value="25">25</option>
14
+ <option value="50">50</option>
15
+ <option value="100">100</option>
16
+ </select>
17
+
18
+ Showing {{ startItem + 1 }}-{{ endItem }} of {{ total }} {{ itemTitle }}
6
19
  </span>
7
20
  </div>
8
21
 
9
22
  <div class="pagination-controls">
10
23
  <button
11
- class="pagination-btn"
12
- :disabled="currentPage === 1"
13
- @click="goToPage(currentPage - 1)"
24
+ class="button"
25
+ :disabled="currentPageValue === 1"
26
+ @click="goToPage(currentPageValue - 1)"
14
27
  title="Previous page"
15
28
  >
16
29
  <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
@@ -19,11 +32,10 @@
19
32
  </button>
20
33
 
21
34
 
22
- <div class="pagination-pages">
23
35
  <button
24
36
  v-if="showFirstPage"
25
- class="pagination-btn"
26
- :class="{ active: currentPage === 1 }"
37
+ class="button"
38
+ :class="{ active: currentPageValue === 1 }"
27
39
  @click="goToPage(1)"
28
40
  >
29
41
  1
@@ -34,8 +46,8 @@
34
46
  <button
35
47
  v-for="page in visiblePages"
36
48
  :key="page"
37
- class="pagination-btn"
38
- :class="{ active: currentPage === page }"
49
+ class="button"
50
+ :class="{ active: currentPageValue === page }"
39
51
  @click="goToPage(page)"
40
52
  >
41
53
  {{ page }}
@@ -45,18 +57,17 @@
45
57
 
46
58
  <button
47
59
  v-if="showLastPage"
48
- class="pagination-btn"
49
- :class="{ active: currentPage === totalPages }"
60
+ class="button"
61
+ :class="{ active: currentPageValue === totalPages }"
50
62
  @click="goToPage(totalPages)"
51
63
  >
52
- {{ totalPages }}
53
- </button>
54
- </div>
64
+ {{ totalPages }}
65
+ </button>
55
66
 
56
67
  <button
57
- class="pagination-btn"
58
- :disabled="currentPage === totalPages"
59
- @click="goToPage(currentPage + 1)"
68
+ class="button"
69
+ :disabled="currentPageValue === totalPages"
70
+ @click="goToPage(currentPageValue + 1)"
60
71
  title="Next page"
61
72
  >
62
73
  <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
@@ -64,21 +75,6 @@
64
75
  </svg>
65
76
  </button>
66
77
  </div>
67
-
68
- <div class="pagination-size" v-if="canChangePageSize">
69
- <label for="page-size">Items per page:</label>
70
- <select
71
- id="page-size"
72
- v-model="localPageSize"
73
- @change="handlePageSizeChange"
74
- class="page-size-select"
75
- >
76
- <option value="10">10</option>
77
- <option value="25">25</option>
78
- <option value="50">50</option>
79
- <option value="100">100</option>
80
- </select>
81
- </div>
82
78
  </div>
83
79
  </template>
84
80
 
@@ -86,10 +82,6 @@
86
82
  import { ref, computed, watch } from 'vue'
87
83
 
88
84
  const props = defineProps({
89
- pageSize: {
90
- type: Number,
91
- default: 25
92
- },
93
85
  total: {
94
86
  type: Number,
95
87
  required: true
@@ -100,30 +92,46 @@ const props = defineProps({
100
92
  },
101
93
  canChangePageSize: {
102
94
  type: Boolean,
103
- default: false
95
+ default: true,
104
96
  },
105
97
  itemTitle: {
106
98
  type: String,
107
99
  default: 'items'
100
+ },
101
+ // Support for v-model binding
102
+ page: {
103
+ type: Number,
104
+ default: 1
105
+ },
106
+ pageSize: {
107
+ type: Number,
108
+ default: 25
108
109
  }
109
110
  })
110
111
 
111
- const emit = defineEmits(['page-change', 'page-size-change'])
112
+ const emit = defineEmits(['page-change', 'page-size-change', 'update:page', 'update:pageSize'])
112
113
 
113
114
  const localPageSize = ref(props.pageSize)
114
115
  const localCurrentPage = ref(props.currentPage)
115
116
 
117
+ // Computed property to get the current page value (supports both v-model and regular props)
118
+ const currentPageValue = computed(() => {
119
+ // When using v-model, the page prop will be reactive and change
120
+ // When using regular props, currentPage will be reactive
121
+ return props.page
122
+ })
123
+
116
124
  const totalPages = computed(() => Math.ceil(props.total / localPageSize.value))
117
125
 
118
- const startItem = computed(() => (localCurrentPage.value - 1) * localPageSize.value)
119
- const endItem = computed(() => Math.min(localCurrentPage.value * localPageSize.value, props.total))
126
+ const startItem = computed(() => (currentPageValue.value - 1) * localPageSize.value)
127
+ const endItem = computed(() => Math.min(currentPageValue.value * localPageSize.value, props.total))
120
128
 
121
129
  const maxVisiblePages = 5
122
130
  const visiblePages = computed(() => {
123
131
  const pages = []
124
132
  const halfVisible = Math.floor(maxVisiblePages / 2)
125
133
 
126
- let start = Math.max(1, localCurrentPage.value - halfVisible)
134
+ let start = Math.max(1, currentPageValue.value - halfVisible)
127
135
  let end = Math.min(totalPages.value, start + maxVisiblePages - 1)
128
136
 
129
137
  if (end - start < maxVisiblePages - 1) {
@@ -143,22 +151,29 @@ const showFirstEllipsis = computed(() => visiblePages.value[0] > 2)
143
151
  const showLastEllipsis = computed(() => visiblePages.value[visiblePages.value.length - 1] < totalPages.value - 1)
144
152
 
145
153
  function goToPage(page) {
146
- if (page >= 1 && page <= totalPages.value && page !== localCurrentPage.value) {
154
+ if (page >= 1 && page <= totalPages.value && page !== currentPageValue.value) {
147
155
  localCurrentPage.value = page
148
156
  emit('page-change', page)
157
+ emit('update:page', page)
149
158
  }
150
159
  }
151
160
 
152
161
  function handlePageSizeChange() {
153
162
  localCurrentPage.value = 1
154
163
  emit('page-size-change', localPageSize.value)
164
+ emit('update:pageSize', localPageSize.value)
155
165
  emit('page-change', 1)
166
+ emit('update:page', 1)
156
167
  }
157
168
 
158
169
  watch(() => props.currentPage, (newPage) => {
159
170
  localCurrentPage.value = newPage
160
171
  })
161
172
 
173
+ watch(() => props.page, (newPage) => {
174
+ localCurrentPage.value = newPage
175
+ })
176
+
162
177
  watch(() => props.pageSize, (newSize) => {
163
178
  localPageSize.value = newSize
164
179
  })
@@ -187,71 +202,39 @@ watch(() => props.pageSize, (newSize) => {
187
202
  gap: 0.5rem;
188
203
  }
189
204
 
190
- .pagination-pages {
191
- display: flex;
192
- align-items: center;
193
- gap: 0.25rem;
194
- }
195
-
196
- .pagination-btn {
205
+ .button {
197
206
  display: flex;
198
207
  align-items: center;
199
208
  justify-content: center;
200
209
  min-width: 2.5rem;
201
210
  height: 2.5rem;
202
- padding: 0.5rem;
203
- border: 1px solid #dee2e6;
204
- background: #fff;
205
- color: #495057;
206
- text-decoration: none;
207
- border-radius: 4px;
208
- cursor: pointer;
209
- transition: all 0.2s ease;
210
- font-size: 0.875rem;
211
- }
212
-
213
- .pagination-btn:hover:not(:disabled) {
214
- background: #e9ecef;
215
- border-color: #adb5bd;
216
- color: #495057;
217
- }
218
-
219
- .pagination-btn.active {
220
- background: #c6d0d7;
221
- color: #333;
222
211
  }
223
212
 
224
- .pagination-btn:disabled {
213
+ .button:disabled {
225
214
  opacity: 0.5;
215
+ background: transparent;
226
216
  cursor: not-allowed;
227
217
  }
228
218
 
219
+ .button.active {
220
+ background: #545f69;
221
+ color: #fff;
222
+ }
223
+
229
224
  .pagination-ellipsis {
230
225
  padding: 0.5rem;
231
226
  color: #6c757d;
232
227
  font-size: 0.875rem;
233
228
  }
234
229
 
235
- .pagination-size {
236
- display: flex;
237
- align-items: center;
238
- gap: 0.5rem;
239
- font-size: 0.875rem;
240
- color: #6c757d;
241
- }
242
-
243
- .page-size-select {
244
- padding: 0.25rem 0.5rem;
245
- border: 1px solid #dee2e6;
246
- border-radius: 4px;
247
- background: #fff;
248
- font-size: 0.875rem;
230
+ #page-size {
231
+ background: transparent;
232
+ margin-left: 0.5rem;
233
+ margin-right: 0.5rem;
249
234
  }
250
235
 
251
- .page-size-select:focus {
252
- outline: none;
253
- border-color: #5681af;
254
- box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
236
+ option {
237
+ background: #545f69;
255
238
  }
256
239
 
257
240
  /* Responsive design */
@@ -39,3 +39,35 @@ const props = defineProps({
39
39
  },
40
40
  });
41
41
  </script>
42
+
43
+ <style scoped>
44
+ :slotted(button):not(.good),
45
+ :slotted(.button):not(.good)
46
+ {
47
+ background-color: white;
48
+ }
49
+
50
+ :slotted(button:hover):not(.good),
51
+ :slotted(.button:hover):not(.good)
52
+ {
53
+ background-color: #f0f0f0;
54
+ }
55
+
56
+ @media (prefers-color-scheme: dark) {
57
+ :slotted(button):not(.good),
58
+ :slotted(.button):not(.good)
59
+ {
60
+ background-color: #111;
61
+ border: 1px solid #222;
62
+ color: #fff;
63
+ }
64
+
65
+ :slotted(button:hover):not(.good),
66
+ :slotted(.button:hover):not(.good)
67
+ {
68
+ background-color: #222;
69
+ border: 1px solid #333;
70
+ color: #fff;
71
+ }
72
+ }
73
+ </style>
@@ -5,10 +5,10 @@
5
5
  <div class = "fg1" />
6
6
  <button class="stick-toggle" :aria-pressed="isStuck" :title="isStuck ? 'Unstick sidebar' : 'Stick sidebar'" @click="toggleStick">
7
7
  <span v-if="isStuck">
8
- <HugeiconsIcon :icon="Pin02Icon" width = "1em" height = "1em" />
8
+ <HugeiconsIcon :icon="Pin02Icon" width = "1em" height = "1em" :strokeWidth = 3 />
9
9
  </span>
10
10
  <span v-else>
11
- <HugeiconsIcon :icon="PinIcon" width = "1em" height = "1em" />
11
+ <HugeiconsIcon :icon="PinIcon" width = "1em" height = "1em" :strokeWidth = 3 />
12
12
  </span>
13
13
  </button>
14
14
  </div>
@@ -24,6 +24,7 @@
24
24
  <span>{{ link.title }}</span>
25
25
  </a>
26
26
  </div>
27
+ <div v-else-if="link.type === 'html'" v-html="link.html"></div>
27
28
  <router-link v-else :to="link.path" :class="{ active: isActive(link.path) }">
28
29
  <HugeiconsIcon :icon="link.icon" />
29
30
  <span>{{ link.title }}</span>
@@ -94,6 +95,16 @@ function addSeparator(id) {
94
95
  addNavigationLink(separator)
95
96
  }
96
97
 
98
+ function addHtml(html, options = {}) {
99
+ const htmlLink = {
100
+ name: options.name || `html-item-${Date.now()}`,
101
+ type: 'html',
102
+ html: html,
103
+ title: options.title || 'HTML Item'
104
+ }
105
+ addNavigationLink(htmlLink)
106
+ }
107
+
97
108
  function removeNavigationLink(linkId) {
98
109
  navigationLinks.value = navigationLinks.value.filter(link => link.id !== linkId)
99
110
  }
@@ -150,6 +161,7 @@ defineExpose({
150
161
  addRouterLink,
151
162
  addCallback,
152
163
  addSeparator,
164
+ addHtml,
153
165
  removeNavigationLink,
154
166
  clearNavigationLinks,
155
167
  getNavigationLinks,
@@ -171,6 +183,10 @@ li {
171
183
  padding: 0;
172
184
  }
173
185
 
186
+ button {
187
+ border: 0;
188
+ }
189
+
174
190
  .navigation-links a {
175
191
  display: flex;
176
192
  align-items: center;
package/vue/views/App.vue CHANGED
@@ -51,6 +51,7 @@
51
51
  sidebar.value.addSeparator('separator-2');
52
52
  sidebar.value.addCallback('Hello World', helloWorld, { icon: Pin02Icon })
53
53
  sidebar.value.addSeparator('separator-3');
54
+ sidebar.value.addHtml('<h2 style = "padding: 0.75em;">Administration</h2>', { name: 'admin-heading' })
54
55
  sidebar.value.addRouterLink('Admin')
55
56
  sidebar.value.open();
56
57
  sidebar.value.stick();
@@ -11,6 +11,7 @@
11
11
  <Table
12
12
  :data = "data"
13
13
  :headers = "headers"
14
+ :show-pagination = "true"
14
15
  >
15
16
  <template #cell-name="{ row, value }">
16
17
  <router-link :to="{ name: 'ViewItem', params: { id: row.name } }">
@@ -59,7 +60,61 @@
59
60
  { name: "Quinn", age: 23, city: "Indianapolis" },
60
61
  { name: "Rita", age: 37, city: "Seattle" },
61
62
  { name: "Sam", age: 39, city: "Denver" },
62
- { name: "Tina", age: 21, city: "Washington D.C." }
63
+ { name: "Tina", age: 21, city: "Washington D.C." },
64
+ { name: "Uma", age: 28, city: "Miami" },
65
+ { name: "Victor", age: 30, city: "Portland" },
66
+ { name: "Wendy", age: 29, city: "Nashville" },
67
+ { name: "Xavier", age: 31, city: "Oklahoma City" },
68
+ { name: "Yara", age: 27, city: "Tampa" },
69
+ { name: "Zane", age: 32, city: "Albuquerque" },
70
+ { name: "Ava", age: 26, city: "Las Vegas" },
71
+ { name: "Ben", age: 33, city: "New Orleans" },
72
+ { name: "Cara", age: 25, city: "San Francisco" },
73
+ { name: "Drew", age: 34, city: "Seattle" },
74
+ { name: "Ethan", age: 28, city: "Denver" },
75
+ { name: "Fiona", age: 30, city: "Washington D.C." },
76
+ { name: "George", age: 29, city: "Miami" },
77
+ { name: "Hannah", age: 32, city: "San Diego" },
78
+ { name: "Ian", age: 27, city: "Dallas" },
79
+ { name: "Jack", age: 31, city: "San Jose" },
80
+ { name: "Kathy", age: 26, city: "Austin" },
81
+ { name: "Leo", age: 33, city: "Jacksonville" },
82
+ { name: "Mia", age: 24, city: "Fort Worth" },
83
+ { name: "Nina", age: 38, city: "Columbus" },
84
+ { name: "Oscar", age: 36, city: "Charlotte" },
85
+ { name: "Paul", age: 34, city: "San Francisco" },
86
+ { name: "Quinn", age: 23, city: "Indianapolis" },
87
+ { name: "Rita", age: 37, city: "Seattle" },
88
+ { name: "Sam", age: 39, city: "Denver" },
89
+ { name: "Tina", age: 21, city: "Washington D.C." },
90
+ { name: "Uma", age: 28, city: "Miami" },
91
+ { name: "Victor", age: 30, city: "Portland" },
92
+ { name: "Wendy", age: 29, city: "Nashville" },
93
+ { name: "Xavier", age: 31, city: "Oklahoma City" },
94
+ { name: "Yara", age: 27, city: "Tampa" },
95
+ { name: "Zane", age: 32, city: "Albuquerque" },
96
+ { name: "Ava", age: 26, city: "Las Vegas" },
97
+ { name: "Ben", age: 33, city: "New Orleans" },
98
+ { name: "Cara", age: 25, city: "San Francisco" },
99
+ { name: "Drew", age: 34, city: "Seattle" },
100
+ { name: "Ethan", age: 28, city: "Denver" },
101
+ { name: "Fiona", age: 30, city: "Washington D.C." },
102
+ { name: "George", age: 29, city: "Miami" },
103
+ { name: "Hannah", age: 32, city: "San Diego" },
104
+ { name: "Ian", age: 27, city: "Dallas" },
105
+ { name: "Jack", age: 31, city: "San Jose" },
106
+ { name: "Kathy", age: 26, city: "Austin" },
107
+ { name: "Leo", age: 33, city: "Jacksonville" },
108
+ { name: "Mia", age: 24, city: "Fort Worth" },
109
+ { name: "Nina", age: 38, city: "Columbus" },
110
+ { name: "Oscar", age: 36, city: "Charlotte" },
111
+ { name: "Paul", age: 34, city: "San Francisco" },
112
+ { name: "Quinn", age: 23, city: "Indianapolis" },
113
+ { name: "Rita", age: 37, city: "Seattle" },
114
+ { name: "Sam", age: 39, city: "Denver" },
115
+ { name: "Tina", age: 21, city: "Washington D.C." },
116
+ { name: "Uma", age: 28, city: "Miami" },
117
+ { name: "Victor", age: 30, city: "Portland" },
63
118
  ]
64
119
  })
65
120
  </script>
@@ -10,6 +10,14 @@
10
10
  </Section>
11
11
 
12
12
  <Section title = "Lorum" id = "lorem">
13
+ <template #toolbar>
14
+ <button>
15
+ <HugeiconsIcon :icon = "HighlighterIcon" width = "1em" height = "1em" />
16
+ Highlight</button>
17
+ <button>Quote</button>
18
+ <button class = "good">Add</button>
19
+ </template>
20
+
13
21
  <p>
14
22
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
15
23
  Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
@@ -52,4 +60,6 @@
52
60
 
53
61
  <script setup>
54
62
  import Section from '../components/Section.vue';
63
+ import { HugeiconsIcon } from '@hugeicons/vue';
64
+ import { HighlighterIcon } from '@hugeicons/core-free-icons';
55
65
  </script>