picocrank 1.21.1 → 1.21.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": "picocrank",
3
- "version": "1.21.1",
3
+ "version": "1.21.3",
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": {
@@ -0,0 +1,30 @@
1
+ <template>
2
+ <label v-if="!fake" :for="controlId">{{ label }}</label>
3
+ <span v-else class="fake-label">{{ label }}</span>
4
+ <slot />
5
+ </template>
6
+
7
+ <script setup>
8
+ import { computed } from 'vue'
9
+
10
+ const props = defineProps({
11
+ label: {
12
+ type: String,
13
+ required: true,
14
+ },
15
+ for: {
16
+ type: String,
17
+ default: '',
18
+ },
19
+ htmlFor: {
20
+ type: String,
21
+ default: '',
22
+ },
23
+ fake: {
24
+ type: Boolean,
25
+ default: false,
26
+ },
27
+ })
28
+
29
+ const controlId = computed(() => props.htmlFor || props.for)
30
+ </script>
@@ -0,0 +1,8 @@
1
+ <template>
2
+ <form>
3
+ <slot />
4
+ <fieldset v-if="$slots.actions">
5
+ <slot name="actions" />
6
+ </fieldset>
7
+ </form>
8
+ </template>
@@ -140,28 +140,35 @@ function toggleSection(sectionId) {
140
140
 
141
141
  const navigationGroups = computed(() => {
142
142
  const ungrouped = []
143
+ const trailingUngrouped = []
143
144
  const sections = []
144
145
  let currentSection = null
146
+ let afterSeparator = false
145
147
 
146
148
  for (const link of navigationLinks.value) {
147
149
  if (link.type === 'section') {
148
150
  currentSection = { id: link.name, title: link.title, items: [] }
149
151
  sections.push(currentSection)
152
+ afterSeparator = false
150
153
  continue
151
154
  }
152
155
 
153
156
  if (link.type === 'separator') {
157
+ currentSection = null
158
+ afterSeparator = true
154
159
  continue
155
160
  }
156
161
 
157
162
  if (currentSection) {
158
163
  currentSection.items.push(link)
164
+ } else if (afterSeparator) {
165
+ trailingUngrouped.push(link)
159
166
  } else {
160
167
  ungrouped.push(link)
161
168
  }
162
169
  }
163
170
 
164
- return { ungrouped, sections }
171
+ return { ungrouped, trailingUngrouped, sections }
165
172
  })
166
173
 
167
174
  const visibleNavItems = computed(() => {
@@ -191,6 +198,10 @@ const visibleNavItems = computed(() => {
191
198
  }
192
199
  }
193
200
 
201
+ for (const link of navigationGroups.value.trailingUngrouped) {
202
+ items.push({ key: link.name, kind: 'link', link })
203
+ }
204
+
194
205
  return items
195
206
  })
196
207
 
@@ -20,14 +20,14 @@
20
20
  </Section>
21
21
 
22
22
  <Section title="Form actions" subtitle="Semantic button types in a fieldset">
23
- <form class="buttons-demo-form" @submit.prevent="onSubmit">
24
- <fieldset>
23
+ <FormLayout class="buttons-demo-form" @submit.prevent="onSubmit">
24
+ <template #actions>
25
25
  <button type="submit">Submit</button>
26
26
  <button type="cancel">Cancel</button>
27
27
  <button type="reset" @click="onReset">Reset</button>
28
- </fieldset>
29
- <p v-if="lastAction" class="subtle">{{ lastAction }}</p>
30
- </form>
28
+ </template>
29
+ </FormLayout>
30
+ <p v-if="lastAction" class="subtle">{{ lastAction }}</p>
31
31
  </Section>
32
32
 
33
33
  <Section title="Links as buttons" subtitle="Anchor elements with the .button class">
@@ -68,6 +68,7 @@
68
68
  <script setup>
69
69
  import { ref } from 'vue'
70
70
  import Section from '../components/Section.vue'
71
+ import FormLayout from '../components/FormLayout.vue'
71
72
 
72
73
  const lastAction = ref('')
73
74
 
@@ -7,181 +7,176 @@
7
7
  <button @click="resetForm">Reset</button>
8
8
  </template>
9
9
 
10
- <form @submit.prevent="handleSubmit">
11
- <!-- Name -->
12
- <label for="name">Name</label>
13
- <input
14
- id="name"
15
- v-model="formData.name"
16
- type="text"
17
- placeholder="Enter your name"
18
- required
19
- />
20
-
21
- <!-- Role (radio group) -->
22
- <span class="fake-label">Role</span>
23
- <div class="radio-group">
24
- <label>
25
- <input
26
- type="radio"
27
- name="role"
28
- value="sales"
29
- v-model="formData.role"
30
- />
31
- <span>Sales</span>
32
- </label>
33
- <label>
34
- <input
35
- type="radio"
36
- name="role"
37
- value="engineering"
38
- v-model="formData.role"
39
- />
40
- <span>Engineering</span>
41
- </label>
42
- <label>
43
- <input
44
- type="radio"
45
- name="role"
46
- value="hr"
47
- v-model="formData.role"
48
- />
49
- <span>HR</span>
50
- </label>
51
- </div>
52
-
53
- <!-- Salary (select) -->
54
- <label for="salary">Salary</label>
55
- <select id="salary" v-model="formData.salary">
56
- <option value="0">Select a salary</option>
57
- <option value="10000">£10,000</option>
58
- <option value="20000">£20,000</option>
59
- <option value="30000">£30,000</option>
60
- <option value="40000">£40,000</option>
61
- <option value="50000">£50,000</option>
62
- </select>
63
-
64
- <!-- Email -->
65
- <label for="email">Email</label>
66
- <input
67
- id="email"
68
- v-model="formData.email"
69
- type="email"
70
- placeholder="Enter your email"
71
- required
72
- />
73
-
74
- <!-- Administrator checkbox -->
75
- <label>Is Administrator?</label>
76
- <input
77
- type="checkbox"
78
- id="is-admin"
79
- v-model="formData.isAdmin"
80
- />
81
-
82
- <!-- Website -->
83
- <label for="website">Website</label>
84
- <input
85
- id="website"
86
- v-model="formData.website"
87
- type="url"
88
- placeholder="https://example.com"
89
- />
90
-
91
- <!-- Favourite food (disabled) -->
92
- <label for="favourite-food">Favourite Food</label>
93
- <input
94
- id="favourite-food"
95
- v-model="formData.favouriteFood"
96
- type="text"
97
- placeholder="e.g. Pizza"
98
- disabled
99
- />
100
-
101
- <!-- Favourite colour -->
102
- <label for="favourite-colour">Favourite Colour</label>
103
- <input
104
- id="favourite-colour"
105
- v-model="formData.favouriteColour"
106
- type="color"
107
- />
108
-
109
- <!-- Favourite number -->
110
- <label for="favourite-number">Favourite Number</label>
111
- <input
112
- id="favourite-number"
113
- v-model.number="formData.favouriteNumber"
114
- type="number"
115
- min="0"
116
- max="100"
117
- step="1"
118
- />
119
-
120
- <!-- Newsletters (checkbox group) -->
121
- <span class="fake-label">
122
- Newsletters
123
- </span>
124
- <div>
125
- <label>
126
- <input
127
- type="checkbox"
128
- id="newsletter1"
129
- v-model="formData.newsletter1"
130
- />
131
- <span>Newsletter 1</span>
132
- </label>
133
-
134
- <label>
135
- <input
136
- type="checkbox"
137
- id="newsletter2"
138
- v-model="formData.newsletter2"
139
- />
140
- <span>Newsletter 2</span>
141
- </label>
142
- </div>
143
-
144
- <!-- Comments -->
145
- <label for="comments">Comments</label>
146
- <textarea
147
- id="comments"
148
- v-model="formData.comments"
149
- ></textarea>
150
-
151
- <!-- Readonly textarea -->
152
- <label for="readonly">Readonly</label>
153
- <textarea
154
- id="readonly"
155
- v-model="formData.readonlyText"
156
- readonly
157
- ></textarea>
158
-
159
- <!-- Disabled textarea -->
160
- <label for="disabled">Disabled</label>
161
- <textarea
162
- id="disabled"
163
- v-model="formData.disabledText"
164
- disabled
165
- ></textarea>
166
-
167
- <!-- Datetime -->
168
- <label for="datetime">
169
- Datetime
170
- </label>
171
- <input
172
- id="datetime"
173
- type="datetime-local"
174
- v-model="formData.datetime"
175
- />
176
-
177
- <!-- Buttons in fieldset to match simple.html -->
178
- <fieldset>
10
+ <FormLayout @submit.prevent="handleSubmit">
11
+ <FormField label="Name" for="name">
12
+ <input
13
+ id="name"
14
+ v-model="formData.name"
15
+ type="text"
16
+ placeholder="Enter your name"
17
+ required
18
+ />
19
+ </FormField>
20
+
21
+ <FormField label="Role" fake>
22
+ <div class="radio-group">
23
+ <label>
24
+ <input
25
+ type="radio"
26
+ name="role"
27
+ value="sales"
28
+ v-model="formData.role"
29
+ />
30
+ <span>Sales</span>
31
+ </label>
32
+ <label>
33
+ <input
34
+ type="radio"
35
+ name="role"
36
+ value="engineering"
37
+ v-model="formData.role"
38
+ />
39
+ <span>Engineering</span>
40
+ </label>
41
+ <label>
42
+ <input
43
+ type="radio"
44
+ name="role"
45
+ value="hr"
46
+ v-model="formData.role"
47
+ />
48
+ <span>HR</span>
49
+ </label>
50
+ </div>
51
+ </FormField>
52
+
53
+ <FormField label="Salary" for="salary">
54
+ <select id="salary" v-model="formData.salary">
55
+ <option value="0">Select a salary</option>
56
+ <option value="10000">£10,000</option>
57
+ <option value="20000">£20,000</option>
58
+ <option value="30000">£30,000</option>
59
+ <option value="40000">£40,000</option>
60
+ <option value="50000">£50,000</option>
61
+ </select>
62
+ </FormField>
63
+
64
+ <FormField label="Email" for="email">
65
+ <input
66
+ id="email"
67
+ v-model="formData.email"
68
+ type="email"
69
+ placeholder="Enter your email"
70
+ required
71
+ />
72
+ </FormField>
73
+
74
+ <FormField label="Is Administrator?">
75
+ <input
76
+ type="checkbox"
77
+ id="is-admin"
78
+ v-model="formData.isAdmin"
79
+ />
80
+ </FormField>
81
+
82
+ <FormField label="Website" for="website">
83
+ <input
84
+ id="website"
85
+ v-model="formData.website"
86
+ type="url"
87
+ placeholder="https://example.com"
88
+ />
89
+ </FormField>
90
+
91
+ <FormField label="Favourite Food" for="favourite-food">
92
+ <input
93
+ id="favourite-food"
94
+ v-model="formData.favouriteFood"
95
+ type="text"
96
+ placeholder="e.g. Pizza"
97
+ disabled
98
+ />
99
+ </FormField>
100
+
101
+ <FormField label="Favourite Colour" for="favourite-colour">
102
+ <input
103
+ id="favourite-colour"
104
+ v-model="formData.favouriteColour"
105
+ type="color"
106
+ />
107
+ </FormField>
108
+
109
+ <FormField label="Favourite Number" for="favourite-number">
110
+ <input
111
+ id="favourite-number"
112
+ v-model.number="formData.favouriteNumber"
113
+ type="number"
114
+ min="0"
115
+ max="100"
116
+ step="1"
117
+ />
118
+ </FormField>
119
+
120
+ <FormField label="Newsletters" fake>
121
+ <div>
122
+ <label>
123
+ <input
124
+ type="checkbox"
125
+ id="newsletter1"
126
+ v-model="formData.newsletter1"
127
+ />
128
+ <span>Newsletter 1</span>
129
+ </label>
130
+
131
+ <label>
132
+ <input
133
+ type="checkbox"
134
+ id="newsletter2"
135
+ v-model="formData.newsletter2"
136
+ />
137
+ <span>Newsletter 2</span>
138
+ </label>
139
+ </div>
140
+ </FormField>
141
+
142
+ <FormField label="Comments" for="comments">
143
+ <textarea
144
+ id="comments"
145
+ v-model="formData.comments"
146
+ ></textarea>
147
+ </FormField>
148
+
149
+ <FormField label="Readonly" for="readonly">
150
+ <textarea
151
+ id="readonly"
152
+ v-model="formData.readonlyText"
153
+ readonly
154
+ ></textarea>
155
+ </FormField>
156
+
157
+ <FormField label="Disabled" for="disabled">
158
+ <textarea
159
+ id="disabled"
160
+ v-model="formData.disabledText"
161
+ disabled
162
+ ></textarea>
163
+ </FormField>
164
+
165
+ <FormField label="Datetime" for="datetime">
166
+ <input
167
+ id="datetime"
168
+ type="datetime-local"
169
+ v-model="formData.datetime"
170
+ />
171
+ </FormField>
172
+
173
+ <template #actions>
179
174
  <button type="submit">Submit</button>
180
175
  <button type="button" @click="handleCancel">Cancel</button>
181
176
  <button type="reset" @click="resetForm">Reset</button>
182
177
  <button type="submit" disabled>Disabled</button>
183
- </fieldset>
184
- </form>
178
+ </template>
179
+ </FormLayout>
185
180
 
186
181
  <div v-if="submitted" class="form-result">
187
182
  <h3>Form Submitted!</h3>
@@ -193,6 +188,8 @@
193
188
  <script setup>
194
189
  import { ref } from 'vue'
195
190
  import Section from '../components/Section.vue'
191
+ import FormLayout from '../components/FormLayout.vue'
192
+ import FormField from '../components/FormField.vue'
196
193
 
197
194
  const formData = ref({
198
195
  name: '',
@@ -246,4 +243,4 @@ function resetForm() {
246
243
  }
247
244
  submitted.value = false
248
245
  }
249
- </script>
246
+ </script>
@@ -89,15 +89,16 @@
89
89
  title="In context"
90
90
  subtitle="Inline notifications beside or below form fields"
91
91
  >
92
- <form class="status-form" @submit.prevent="submitForm">
93
- <label for="status-email">Email</label>
94
- <input
95
- id="status-email"
96
- v-model="formEmail"
97
- type="email"
98
- placeholder="you@example.com"
99
- required
100
- />
92
+ <FormLayout class="status-form" @submit.prevent="submitForm">
93
+ <FormField label="Email" for="status-email">
94
+ <input
95
+ id="status-email"
96
+ v-model="formEmail"
97
+ type="email"
98
+ placeholder="you@example.com"
99
+ required
100
+ />
101
+ </FormField>
101
102
 
102
103
  <p
103
104
  v-if="formMessage"
@@ -109,17 +110,19 @@
109
110
  {{ formMessage }}
110
111
  </p>
111
112
 
112
- <fieldset>
113
+ <template #actions>
113
114
  <button type="submit" class="good">Save</button>
114
115
  <button type="button" @click="clearForm">Clear</button>
115
- </fieldset>
116
- </form>
116
+ </template>
117
+ </FormLayout>
117
118
  </Section>
118
119
  </template>
119
120
 
120
121
  <script setup>
121
122
  import { ref, computed } from 'vue'
122
123
  import Section from '../components/Section.vue'
124
+ import FormLayout from '../components/FormLayout.vue'
125
+ import FormField from '../components/FormField.vue'
123
126
  import { useNotificationPopups } from '../composables/useNotificationPopups.js'
124
127
 
125
128
  const { show: showPopup, dismissAll: dismissAllPopups } = useNotificationPopups()