picocrank 1.18.2 → 1.18.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.
@@ -1,5 +1,191 @@
1
1
  <template>
2
- <Section title="View Item" padding>
3
- View Item
4
- </Section>
2
+ <section class="with-header-and-content">
3
+ <div class="section-header flex-row">
4
+ <div class="fg1">
5
+ <h2 :title="item.subtitle">{{ item.title }}</h2>
6
+ </div>
7
+ <div role="toolbar">
8
+ <router-link :to="{ name: 'TableExample' }" class="button neutral">Back to table</router-link>
9
+ <button type="button" class="good">Edit</button>
10
+ </div>
11
+ </div>
12
+
13
+ <div class="section-content padding">
14
+ <p>{{ item.summary }}</p>
15
+
16
+ <dl>
17
+ <dt>ID</dt>
18
+ <dd>{{ id }}</dd>
19
+
20
+ <dt>Name</dt>
21
+ <dd>{{ item.name }}</dd>
22
+
23
+ <dt>Status</dt>
24
+ <dd>
25
+ <span class="tag" :class="item.statusClass">{{ item.status }}</span>
26
+ </dd>
27
+
28
+ <dt>Role</dt>
29
+ <dd>{{ item.role }}</dd>
30
+
31
+ <dt>Location</dt>
32
+ <dd>{{ item.location }}</dd>
33
+ </dl>
34
+ </div>
35
+
36
+ <div class="section-subheader">
37
+ <h3>Contact</h3>
38
+ </div>
39
+
40
+ <div class="section-content padding">
41
+ <dl>
42
+ <dt>Email</dt>
43
+ <dd><a :href="`mailto:${item.email}`">{{ item.email }}</a></dd>
44
+
45
+ <dt>Phone</dt>
46
+ <dd>{{ item.phone }}</dd>
47
+
48
+ <dt>Timezone</dt>
49
+ <dd>{{ item.timezone }}</dd>
50
+ </dl>
51
+ </div>
52
+
53
+ <div class="section-subheader">
54
+ <h3>Details</h3>
55
+ </div>
56
+
57
+ <div class="section-content padding">
58
+ <dl>
59
+ <dt>Department</dt>
60
+ <dd>{{ item.department }}</dd>
61
+
62
+ <dt>Manager</dt>
63
+ <dd>{{ item.manager }}</dd>
64
+
65
+ <dt>Start date</dt>
66
+ <dd>{{ item.startDate }}</dd>
67
+
68
+ <dt>Last updated</dt>
69
+ <dd>{{ item.lastUpdated }}</dd>
70
+ </dl>
71
+ </div>
72
+
73
+ <div class="section-subheader">
74
+ <h3>Recent activity</h3>
75
+ </div>
76
+
77
+ <div class="section-content padding">
78
+ <ul>
79
+ <li v-for="(entry, index) in item.activity" :key="index">
80
+ <span class="subtle">{{ entry.when }}</span>
81
+ — {{ entry.text }}
82
+ </li>
83
+ </ul>
84
+ </div>
85
+ </section>
5
86
  </template>
87
+
88
+ <script setup>
89
+ import { computed } from 'vue'
90
+
91
+ const props = defineProps({
92
+ id: {
93
+ type: [String, Number],
94
+ required: true,
95
+ },
96
+ })
97
+
98
+ const itemsById = {
99
+ 1: {
100
+ title: 'Acme deployment',
101
+ subtitle: 'Production release #1842',
102
+ summary: 'Read-only detail view for a single record, using Femtocrank definition lists and section subheaders.',
103
+ name: 'Acme deployment',
104
+ status: 'Active',
105
+ statusClass: 'fg-good',
106
+ role: 'Release',
107
+ location: 'us-east-1',
108
+ email: 'releases@example.com',
109
+ phone: '+1 555 0100',
110
+ timezone: 'UTC',
111
+ department: 'Platform engineering',
112
+ manager: 'Alex Morgan',
113
+ startDate: '12 March 2026',
114
+ lastUpdated: '9 July 2026, 09:14',
115
+ activity: [
116
+ { when: '2 hours ago', text: 'Health check passed for all regions.' },
117
+ { when: 'Yesterday', text: 'Configuration reviewed by on-call engineer.' },
118
+ { when: '3 days ago', text: 'Deployed from build 1842.' },
119
+ ],
120
+ },
121
+ Alice: {
122
+ title: 'Alice',
123
+ subtitle: 'Senior analyst',
124
+ summary: 'Example person record linked from the table example.',
125
+ name: 'Alice',
126
+ status: 'Active',
127
+ statusClass: 'fg-good',
128
+ role: 'Senior analyst',
129
+ location: 'New York',
130
+ email: 'alice@example.com',
131
+ phone: '+1 555 0142',
132
+ timezone: 'America/New_York',
133
+ department: 'Operations',
134
+ manager: 'Jordan Lee',
135
+ startDate: '4 June 2022',
136
+ lastUpdated: '8 July 2026, 16:30',
137
+ activity: [
138
+ { when: 'Today', text: 'Approved quarterly forecast submission.' },
139
+ { when: '2 days ago', text: 'Updated territory assignment.' },
140
+ ],
141
+ },
142
+ Bob: {
143
+ title: 'Bob',
144
+ subtitle: 'Support specialist',
145
+ summary: 'Example person record linked from the table example.',
146
+ name: 'Bob',
147
+ status: 'Away',
148
+ statusClass: 'fg-warning',
149
+ role: 'Support specialist',
150
+ location: 'Los Angeles',
151
+ email: 'bob@example.com',
152
+ phone: '+1 555 0198',
153
+ timezone: 'America/Los_Angeles',
154
+ department: 'Customer success',
155
+ manager: 'Sam Patel',
156
+ startDate: '19 January 2024',
157
+ lastUpdated: '5 July 2026, 11:02',
158
+ activity: [
159
+ { when: '4 hours ago', text: 'Set status to away.' },
160
+ { when: 'Yesterday', text: 'Closed ticket #8821.' },
161
+ ],
162
+ },
163
+ }
164
+
165
+ const item = computed(() => {
166
+ const key = String(props.id)
167
+ if (itemsById[key]) {
168
+ return itemsById[key]
169
+ }
170
+
171
+ const label = key.charAt(0).toUpperCase() + key.slice(1)
172
+ return {
173
+ title: label,
174
+ subtitle: `Record ${key}`,
175
+ summary: 'Generic detail view for records opened from the table or navigation.',
176
+ name: label,
177
+ status: 'Unknown',
178
+ statusClass: 'fg-note',
179
+ role: '—',
180
+ location: '—',
181
+ email: `${key.toLowerCase()}@example.com`,
182
+ phone: '—',
183
+ timezone: 'UTC',
184
+ department: '—',
185
+ manager: '—',
186
+ startDate: '—',
187
+ lastUpdated: '—',
188
+ activity: [{ when: '—', text: 'No activity recorded for this item.' }],
189
+ }
190
+ })
191
+ </script>
@@ -1,114 +1,236 @@
1
1
  <template>
2
- <Section title = "Welcome" classes = "">
3
- <template #toolbar>
4
- <button @click="toggleSidebarEnabled" :class="sidebarEnabled ? 'neutral' : 'good'">
5
- <HugeiconsIcon :icon="Menu01Icon" width="1em" height="1em" />
6
- {{ sidebarEnabled ? 'Disable Sidebar' : 'Enable Sidebar' }}
7
- </button>
8
- <button @click="toggleBrandingEnabled" :class="brandingEnabled ? 'neutral' : 'good'">
9
- <HugeiconsIcon :icon="HighlighterIcon" width="1em" height="1em" />
10
- {{ brandingEnabled ? 'Hide Logo & Title' : 'Show Logo & Title' }}
11
- </button>
12
- <div style="display: flex; gap: 1rem; align-items: center;">
13
- <label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
14
- <input type="checkbox" :checked="quickSearchEnabled" @change="toggleQuickSearchEnabled" />
15
- <span>Enable QuickSearch</span>
16
- </label>
17
- <label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
18
- <input type="checkbox" :checked="topBarEnabled" @change="toggleTopBarEnabled" />
19
- <span>Enable TopBar</span>
20
- </label>
2
+ <Section
3
+ id="welcome"
4
+ subtitle="Vue components on the Femtocrank theme"
5
+ classes="welcome-hero-section"
6
+ >
7
+ <template #title>
8
+ <div class="welcome-hero-title">
9
+ <img :src="logoUrl" alt="" class="welcome-logo" />
10
+ <span>Welcome to PicoCrank</span>
21
11
  </div>
22
- <button type="button" @click="toggleBreadcrumbsEnabled" :class="breadcrumbsEnabled ? 'neutral' : 'good'">
23
- {{ breadcrumbsEnabled ? 'Hide Breadcrumbs' : 'Show Breadcrumbs' }}
24
- </button>
25
12
  </template>
26
- <p>
27
- Welcome to <strong>PicoCrank</strong>! This is a flexible example application to showcase a modular Vue 3 UI with a focus on ease of navigation, live UI controls, and modern design elements. Use the toolbar above to interactively enable or disable interface features like the sidebar, branding, QuickSearch, the top navigation bar, and route breadcrumbs in the header.
28
- </p>
29
- <p>
30
- Explore the sections below to see reusable components in action, including toolbars, buttons, and layout options. PicoCrank is designed to be a starting point for your own rapid Vue projects&mdash;inspect the code and try customizing the UI!
31
- </p>
32
-
33
- </Section>
34
13
 
35
- <Section title = "Lorum" id = "lorem">
36
- <template #toolbar>
37
- <button>
38
- <HugeiconsIcon :icon = "HighlighterIcon" width = "1em" height = "1em" />
39
- Highlight</button>
40
- <button>Quote</button>
41
- <button class = "good">Add</button>
42
- </template>
14
+ <p class="welcome-lead">
15
+ Build admin-style UIs quickly with ready-made Vue components and a minimal,
16
+ accessible CSS theme. Browse examples by how you would use them in a real app.
17
+ </p>
43
18
 
44
- <div role = "toolbar">
45
- <button class = "neutral">
46
- Neutral
47
- </button>
48
- <button class = "good">
49
- Good
50
- </button>
51
- <button class = "bad">
52
- Bad
53
- </button>
19
+ <div role="toolbar" class="welcome-actions">
20
+ <router-link :to="{ name: 'PageStructure' }" class="button good">
21
+ Configure layout
22
+ </router-link>
23
+ <a href="#start-here" class="button neutral">Browse components</a>
24
+ <a
25
+ href="https://github.com/jamesread/picocrank"
26
+ class="button neutral"
27
+ target="_blank"
28
+ rel="noopener noreferrer"
29
+ >
30
+ View on GitHub
31
+ </a>
54
32
  </div>
55
33
 
56
- <p>
57
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
58
- Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
59
- Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
60
- Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
34
+ <p class="subtle">
35
+ New here? Open <router-link :to="{ name: 'PageStructure' }">Page structure</router-link>
36
+ to toggle the sidebar, header branding, QuickSearch, and more.
61
37
  </p>
62
38
  </Section>
63
39
 
64
- <Section title = "Shakespeare" id = "shakespeare">
65
- <p>
66
- To be, or not to be, that is the question:
67
- Whether 'tis nobler in the mind to suffer
68
- The slings and arrows of outrageous fortune,
69
- Or to take arms against a sea of troubles
70
- And by opposing end them. To die: to sleep;
71
- No more; and by a sleep to say we end
72
- The heart-ache and the thousand natural shocks
73
- That flesh is heir to, 'tis a consummation
74
- Devoutly to be wish'd. To die, to sleep;
75
- To sleep: perchance to dream: ay, there's the rub;
76
- For in that sleep of death what dreams may come
77
- When we have shuffled off this mortal coil,
78
- Must give us pause: there's the respect
79
- That makes calamity of so long life;
80
- For who would bear the whips and scorns of time,
81
- The oppressor's wrong
82
- </p>
40
+ <Section
41
+ id="start-here"
42
+ title="Start here"
43
+ subtitle="Jump into the example areas"
44
+ >
45
+ <Navigation ref="welcomeNavigation">
46
+ <NavigationGrid />
47
+ </Navigation>
83
48
  </Section>
84
49
 
85
- <Section title = "More Lorem" id = "more-lorem">
86
- <p>
87
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
88
- Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
89
- Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
90
- Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
91
- </p>
50
+ <Section
51
+ title="Popular examples"
52
+ subtitle="A few good places to begin"
53
+ >
54
+ <div class="grid-boxed welcome-popular-grid">
55
+ <router-link
56
+ v-for="example in popularExamples"
57
+ :key="example.name"
58
+ :to="{ name: example.name }"
59
+ class="stat-display welcome-popular-card"
60
+ >
61
+ <span class="subtle">{{ example.category }}</span>
62
+ <span class="stat">{{ example.label }}</span>
63
+ <span class="welcome-popular-description">{{ example.description }}</span>
64
+ </router-link>
65
+ </div>
92
66
  </Section>
93
67
 
68
+ <Section
69
+ title="Try it"
70
+ subtitle="Small interactions you can trigger right now"
71
+ >
72
+ <dl>
73
+ <dt>Notification popup</dt>
74
+ <dd>
75
+ <button type="button" class="good" @click="showWelcomeToast">
76
+ Show a toast
77
+ </button>
78
+ <p class="subtle">Corner notifications stack, auto-dismiss, and can link elsewhere.</p>
79
+ </dd>
80
+
81
+ <dt>Light and dark mode</dt>
82
+ <dd>
83
+ <p>
84
+ Use the sun/moon button in the header to switch themes. Your preference is
85
+ remembered across visits.
86
+ </p>
87
+ </dd>
88
+
89
+ <dt>Femtocrank theme</dt>
90
+ <dd>
91
+ <p>
92
+ PicoCrank is built on
93
+ <a href="https://github.com/jamesread/femtocrank" target="_blank" rel="noopener noreferrer">Femtocrank</a>
94
+ &mdash; zero-dependency CSS with high accessibility defaults.
95
+ </p>
96
+ </dd>
97
+ </dl>
98
+ </Section>
94
99
  </template>
95
100
 
96
101
  <script setup>
97
- import { inject } from 'vue';
98
- import Section from '../components/Section.vue';
99
- import { HugeiconsIcon } from '@hugeicons/vue';
100
- import { HighlighterIcon } from '@hugeicons/core-free-icons';
101
- import { Menu01Icon } from '@hugeicons/core-free-icons';
102
-
103
- // Inject sidebar and branding state and toggle functions from App.vue
104
- const sidebarEnabled = inject('sidebarEnabled');
105
- const toggleSidebarEnabled = inject('toggleSidebarEnabled');
106
- const brandingEnabled = inject('brandingEnabled');
107
- const toggleBrandingEnabled = inject('toggleBrandingEnabled');
108
- const quickSearchEnabled = inject('quickSearchEnabled');
109
- const toggleQuickSearchEnabled = inject('toggleQuickSearchEnabled');
110
- const topBarEnabled = inject('topBarEnabled');
111
- const toggleTopBarEnabled = inject('toggleTopBarEnabled');
112
- const breadcrumbsEnabled = inject('breadcrumbsEnabled');
113
- const toggleBreadcrumbsEnabled = inject('toggleBreadcrumbsEnabled');
102
+ import { ref, onMounted } from 'vue'
103
+ import Section from '../components/Section.vue'
104
+ import Navigation from '../components/Navigation.vue'
105
+ import NavigationGrid from '../components/NavigationGrid.vue'
106
+ import { useNotificationPopups } from '../composables/useNotificationPopups.js'
107
+ import logoUrl from '/logo.png'
108
+
109
+ const { show: showPopup } = useNotificationPopups()
110
+ const welcomeNavigation = ref(null)
111
+
112
+ const popularExamples = [
113
+ {
114
+ name: 'TableExample',
115
+ category: 'Data display',
116
+ label: 'Table',
117
+ description: 'Sortable columns and pagination',
118
+ },
119
+ {
120
+ name: 'CalendarExample',
121
+ category: 'Data display',
122
+ label: 'Calendar',
123
+ description: 'Events, drag-and-drop, and date ranges',
124
+ },
125
+ {
126
+ name: 'ButtonsExample',
127
+ category: 'Forms & input',
128
+ label: 'Buttons',
129
+ description: 'Variants, toolbars, and form actions',
130
+ },
131
+ {
132
+ name: 'StatusExample',
133
+ category: 'Feedback',
134
+ label: 'Status',
135
+ description: 'Inline messages and notification popups',
136
+ },
137
+ ]
138
+
139
+ function showWelcomeToast() {
140
+ showPopup({
141
+ label: 'WELCOME',
142
+ class: 'success',
143
+ message: 'Thanks for exploring PicoCrank!',
144
+ linkTo: { name: 'TableExample' },
145
+ linkLabel: 'Try the table example',
146
+ })
147
+ }
148
+
149
+ onMounted(() => {
150
+ if (!welcomeNavigation.value) {
151
+ return
152
+ }
153
+
154
+ const nav = welcomeNavigation.value
155
+
156
+ nav.addRouterLink('PageStructure', 'Page structure', {
157
+ description: 'Sidebar, header, QuickSearch, and breadcrumbs',
158
+ })
159
+ nav.addRouterLink('TableExample', 'Table', {
160
+ description: 'Lists with sorting and pagination',
161
+ })
162
+ nav.addRouterLink('CalendarExample', 'Calendar', {
163
+ description: 'Month view with events',
164
+ })
165
+ nav.addRouterLink('ButtonsExample', 'Buttons', {
166
+ description: 'Button variants and toolbars',
167
+ })
168
+ nav.addRouterLink('FormExample', 'Forms', {
169
+ description: 'Labels, inputs, and fieldsets',
170
+ })
171
+ nav.addRouterLink('LoginExample', 'Login', {
172
+ description: 'Auth form with OAuth tabs',
173
+ })
174
+ nav.addRouterLink('StatusExample', 'Status', {
175
+ description: 'Notifications and inline status',
176
+ })
177
+ nav.addRouterLink('NavigationGridExample', 'Navigation grid', {
178
+ description: 'Icon grid for settings hubs',
179
+ })
180
+ nav.addRouterLink('TabsExample', 'Tabs', {
181
+ description: 'Tabbed content regions',
182
+ })
183
+ })
114
184
  </script>
185
+
186
+ <style scoped>
187
+ .welcome-hero-title {
188
+ display: flex;
189
+ align-items: center;
190
+ gap: 0.75rem;
191
+ }
192
+
193
+ .welcome-logo {
194
+ width: 2.5rem;
195
+ height: 2.5rem;
196
+ }
197
+
198
+ .welcome-lead {
199
+ font-size: 1.05rem;
200
+ max-width: 42rem;
201
+ }
202
+
203
+ .welcome-actions {
204
+ display: flex;
205
+ flex-wrap: wrap;
206
+ gap: 0.5rem;
207
+ margin-bottom: 0.75rem;
208
+ }
209
+
210
+ .welcome-popular-grid {
211
+ grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
212
+ }
213
+
214
+ .welcome-popular-card {
215
+ display: flex;
216
+ flex-direction: column;
217
+ gap: 0.35rem;
218
+ text-decoration: none;
219
+ color: inherit;
220
+ transition: transform 0.15s ease, box-shadow 0.15s ease;
221
+ }
222
+
223
+ .welcome-popular-card:hover {
224
+ transform: translateY(-2px);
225
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
226
+ }
227
+
228
+ .welcome-popular-description {
229
+ font-size: 0.85rem;
230
+ color: #666;
231
+ }
232
+
233
+ html[data-theme="dark"] .welcome-popular-description {
234
+ color: #bbb;
235
+ }
236
+ </style>
@@ -1,82 +0,0 @@
1
- <template>
2
- <Section title="User Control Panel" subtitle="Manage your account settings and preferences">
3
- <Navigation ref="localNavigation">
4
- <NavigationGrid />
5
- </Navigation>
6
- </Section>
7
- </template>
8
-
9
- <script setup>
10
- import { ref, onMounted } from 'vue';
11
- import Section from '../components/Section.vue';
12
- import NavigationGrid from '../components/NavigationGrid.vue';
13
- import Navigation from '../components/Navigation.vue';
14
- import {
15
- ViewIcon,
16
- EditIcon,
17
- SecurityValidationIcon,
18
- HomeIcon
19
- } from '@hugeicons/core-free-icons';
20
-
21
- const localNavigation = ref(null);
22
-
23
- onMounted(() => {
24
- if (localNavigation.value) {
25
- // Add user control panel links to local navigation
26
- localNavigation.value.addCallback('Profile', () => {
27
- alert('Navigate to Profile');
28
- }, {
29
- icon: ViewIcon,
30
- name: 'user-profile',
31
- description: 'View and edit your profile information'
32
- });
33
-
34
- localNavigation.value.addCallback('Account Settings', () => {
35
- alert('Navigate to Account Settings');
36
- }, {
37
- icon: EditIcon,
38
- name: 'account-settings',
39
- description: 'Manage your account configuration'
40
- });
41
-
42
- localNavigation.value.addCallback('Security', () => {
43
- alert('Navigate to Security Settings');
44
- }, {
45
- icon: SecurityValidationIcon,
46
- name: 'security-settings',
47
- description: 'Change password and security options'
48
- });
49
-
50
- localNavigation.value.addCallback('Preferences', () => {
51
- alert('Navigate to Preferences');
52
- }, {
53
- icon: HomeIcon,
54
- name: 'preferences',
55
- description: 'Customize your application preferences'
56
- });
57
-
58
- localNavigation.value.addCallback('Notifications', () => {
59
- alert('Navigate to Notifications');
60
- }, {
61
- icon: ViewIcon,
62
- name: 'notifications',
63
- description: 'Manage notification settings',
64
- iconColor: '#e67e22',
65
- indicator: true
66
- });
67
-
68
- localNavigation.value.addCallback('Billing', () => {
69
- alert('Navigate to Billing');
70
- }, {
71
- icon: EditIcon,
72
- name: 'billing',
73
- description: 'View billing and subscription details',
74
- disabled: true
75
- });
76
- }
77
- });
78
- </script>
79
-
80
- <style scoped>
81
- /* Additional styles if needed */
82
- </style>