@lightspeed/crane 1.2.1 → 1.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +11 -0
  3. package/UPGRADE.md +30 -0
  4. package/dist/app.d.mts +32 -3
  5. package/dist/app.d.ts +32 -3
  6. package/dist/app.mjs +1 -1
  7. package/dist/cli.mjs +8 -8
  8. package/package.json +7 -8
  9. package/template/eslint.config.cjs +1 -0
  10. package/template/footers/example-footer/component/MadeWith.vue +1 -1
  11. package/template/headers/example-header/ExampleHeader.vue +2 -11
  12. package/template/headers/example-header/component/CategoriesDropdown.vue +165 -0
  13. package/template/headers/example-header/component/NavigationMenu.vue +83 -10
  14. package/template/package.json +3 -5
  15. package/template/page-templates/assets/template_cover_image.png +0 -0
  16. package/template/page-templates/example-template/configuration.ts +22 -0
  17. package/template/page-templates/example-template/pages/catalog.ts +8 -0
  18. package/template/page-templates/example-template/pages/category.ts +8 -0
  19. package/template/page-templates/example-template/pages/home.ts +178 -0
  20. package/template/page-templates/example-template/pages/product.ts +8 -0
  21. package/template/reference/sections/about-us/settings/content.ts +4 -7
  22. package/template/reference/sections/about-us/showcases/1.ts +0 -3
  23. package/template/reference/sections/about-us/showcases/2.ts +0 -3
  24. package/template/reference/sections/intro-slider/assets/category_4.jpg +0 -0
  25. package/template/reference/sections/intro-slider/assets/category_4@2x.jpg +0 -0
  26. package/template/reference/sections/intro-slider/settings/content.ts +1 -0
  27. package/template/reference/sections/intro-slider/showcases/1.ts +0 -18
  28. package/template/reference/sections/intro-slider/showcases/2.ts +0 -18
  29. package/template/reference/sections/tag-lines/settings/content.ts +1 -0
  30. package/template/reference/sections/tag-lines/showcases/1.ts +0 -12
  31. package/template/reference/sections/tag-lines/showcases/2.ts +0 -12
  32. package/template/sections/example-section/settings/content.ts +0 -3
  33. package/template/sections/example-section/showcases/1.ts +0 -12
  34. package/template/sections/example-section/showcases/2.ts +0 -12
  35. package/template/sections/example-section/showcases/3.ts +0 -12
  36. package/template/templates/template.ts +0 -12
  37. package/types.d.ts +71 -45
  38. package/template/.eslintrc.cjs +0 -3
@@ -0,0 +1,165 @@
1
+ <template>
2
+ <teleport to="body">
3
+ <div
4
+ v-if="categories.length > 0"
5
+ class="categories-dropdown"
6
+ :style="{ left: offsetLeft }"
7
+ @mouseleave="emit('hide')"
8
+ @blur="emit('hide')"
9
+ >
10
+ <ul class="category-list">
11
+ <li
12
+ v-if="categoryId === undefined"
13
+ class="category-item"
14
+ >
15
+ <a
16
+ :href="getCategoryLink('/products')"
17
+ class="category-link"
18
+ >
19
+ <span>View all</span>
20
+ </a>
21
+ </li>
22
+ <li
23
+ v-for="category in categories"
24
+ :key="category.id"
25
+ class="category-item"
26
+ >
27
+ <a
28
+ :href="getCategoryLink(category.urlPath)"
29
+ class="category-link"
30
+ >
31
+ <span>{{ category.name }}</span>
32
+ <span v-if="category.hasChildren" class="arrow">▶</span>
33
+ </a>
34
+ <ul v-if="category.hasChildren" class="subcategory-list">
35
+ <li
36
+ v-for="child in category.children"
37
+ :key="child.id"
38
+ class="subcategory-item"
39
+ >
40
+ <a
41
+ :href="getCategoryLink(child.urlPath)"
42
+ class="subcategory-link"
43
+ >
44
+ {{ child.name }}
45
+ </a>
46
+ </li>
47
+ </ul>
48
+ </li>
49
+ </ul>
50
+ </div>
51
+ </teleport>
52
+ </template>
53
+
54
+ <script setup lang="ts">
55
+ import { computed } from 'vue';
56
+ import { useVueBaseProps } from '@lightspeed/crane';
57
+ import { Design } from '../type.ts';
58
+
59
+ interface Props {
60
+ element?: HTMLElement;
61
+ categoryId?: number;
62
+ }
63
+
64
+ const props = defineProps<Props>();
65
+ const emit = defineEmits(['hide']);
66
+
67
+ const baseProps = useVueBaseProps<Props, Design>();
68
+
69
+ const isPreviewMode = computed(() => Boolean(baseProps.site?.value?.isPreviewMode));
70
+ const categoryTree = computed(() => baseProps.category?.value?.categoryTree ?? []);
71
+ const categories = computed(() => categoryTree.value
72
+ .filter(filterUniqueCategories)
73
+ .filter((category) => filterByCategoryId(category, props.categoryId))
74
+ .map((category) => ({
75
+ ...category,
76
+ hasChildren: category.children?.length > 0,
77
+ }))
78
+ );
79
+ const offsetLeft = computed(() => `${props.element?.offsetLeft ?? 0}px`);
80
+
81
+ function filterUniqueCategories(category: CategoryTree, index: number, categoryTree: CategoryTree[]) {
82
+ return categoryTree.findIndex((c) => c.id === category.id) === index;
83
+ }
84
+
85
+ function filterByCategoryId(category: CategoryTree, categoryId?: number) {
86
+ if (categoryId !== undefined) {
87
+ return category.id === categoryId;
88
+ }
89
+ return true;
90
+ }
91
+
92
+ const getCategoryLink = (urlPath: string) => {
93
+ if (isPreviewMode.value) {
94
+ return undefined;
95
+ }
96
+ return urlPath;
97
+ };
98
+ </script>
99
+
100
+ <style scoped lang="scss">
101
+ .categories-dropdown {
102
+ position: absolute;
103
+ top: 48px;
104
+ left: 12px;
105
+ background-color: #ffffff;
106
+ border: 1px solid #ddd;
107
+ border-radius: 6px;
108
+ min-width: 200px;
109
+ padding: 8px;
110
+ z-index: 1000;
111
+ }
112
+
113
+ .category-list,
114
+ .subcategory-list {
115
+ list-style-type: none;
116
+ margin: 0;
117
+ padding: 0;
118
+ }
119
+
120
+ .category-item,
121
+ .subcategory-item {
122
+ cursor: pointer;
123
+ position: relative;
124
+ margin: 0;
125
+ padding: 6px 10px;
126
+ }
127
+
128
+ .category-link,
129
+ .subcategory-link {
130
+ display: flex;
131
+ align-items: center;
132
+ justify-content: space-between;
133
+ color: #080d12;
134
+ text-decoration: none;
135
+ width: 100%;
136
+ transition: color 0.35s ease;
137
+
138
+ &:hover {
139
+ color: #0056b3;
140
+ }
141
+ }
142
+
143
+ .arrow {
144
+ margin-left: 8px;
145
+ font-size: 10px;
146
+ color: #b7b7b7;
147
+ }
148
+
149
+ .subcategory-list {
150
+ display: none;
151
+ position: absolute;
152
+ top: 0;
153
+ left: 100%;
154
+ background-color: #ffffff;
155
+ border: 1px solid #ddd;
156
+ border-radius: 6px;
157
+ min-width: 200px;
158
+ padding: 8px;
159
+ z-index: 1001;
160
+ }
161
+
162
+ .category-item:hover > .subcategory-list {
163
+ display: block;
164
+ }
165
+ </style>
@@ -3,42 +3,109 @@
3
3
  v-if="navigationMenu.hasContent"
4
4
  role="navigation"
5
5
  >
6
- <ul>
6
+ <ul class="navigation-menu">
7
7
  <li
8
8
  v-for="item in navigationMenu.items"
9
9
  :key="item.id"
10
+ class="menu-item"
11
+ @mouseenter="onMouseEnter(item, $event)"
12
+ @mouseleave="onMouseLeave(item)"
13
+ @focusin="onMouseEnter(item, $event)"
14
+ @focusout="onMouseLeave(item)"
10
15
  >
11
- <a @click="item.performAction">
16
+ <a
17
+ class="menu-link"
18
+ @click="item.performAction"
19
+ >
12
20
  {{ item.title }}
13
21
  </a>
22
+ <span
23
+ v-if="item.showStoreCategories && isItemWithCategoriesDropdown(item)"
24
+ class="arrow"
25
+ >
26
+
27
+ </span>
14
28
  </li>
15
29
  </ul>
30
+ <CategoriesDropdown
31
+ v-if="showCategoriesDropdown"
32
+ :element="activeItem?.element"
33
+ :category-id="categoryId"
34
+ @hide="hideDropdown"
35
+ />
16
36
  </nav>
17
37
  </template>
18
38
 
19
39
  <script setup lang="ts">
40
+ import { ref, computed } from 'vue';
20
41
  import { useNavigationMenuElementContent } from '@lightspeed/crane';
42
+ import CategoriesDropdown from './CategoriesDropdown.vue';
43
+
44
+ interface ActiveItem {
45
+ actionLink: ActionLink;
46
+ element?: HTMLElement;
47
+ }
21
48
 
22
49
  const navigationMenu = useNavigationMenuElementContent();
50
+ const activeItem = ref<ActiveItem | null>();
51
+
52
+ const showCategoriesDropdown = computed(() => {
53
+ const item = activeItem.value?.actionLink;
54
+ if (isItemWithCategoriesDropdown(item)) {
55
+ return item?.showStoreCategories ?? true;
56
+ }
57
+ return false;
58
+ });
59
+
60
+ const categoryId = computed(() => {
61
+ const item = activeItem.value?.actionLink;
62
+ if (item?.type === 'GO_TO_CATEGORY') {
63
+ return item.categoryId;
64
+ }
65
+ return undefined;
66
+ });
67
+
68
+ function isItemWithCategoriesDropdown(item?: ActionLink) {
69
+ return item?.type === 'GO_TO_STORE' || item?.type === 'GO_TO_CATEGORY';
70
+ }
71
+
72
+ function onMouseEnter(item: ActionLink, event: MouseEvent | FocusEvent) {
73
+ activeItem.value = {
74
+ actionLink: item,
75
+ element: event.target as HTMLElement,
76
+ };
77
+ }
78
+
79
+ function onMouseLeave(item: ActionLink | null) {
80
+ if (activeItem.value?.actionLink === item) {
81
+ activeItem.value = null;
82
+ }
83
+ }
84
+
85
+ function hideDropdown() {
86
+ activeItem.value = null;
87
+ }
23
88
  </script>
24
89
 
25
- <style lang="scss" scoped>
26
- ul {
27
- gap: 16px;
90
+ <style scoped lang="scss">
91
+ .navigation-menu {
28
92
  display: flex;
93
+ gap: 16px;
29
94
  overflow-x: auto;
30
95
  white-space: nowrap;
31
96
  height: 48px;
97
+ list-style-type: none;
98
+ margin: 0;
99
+ padding: 0;
32
100
  }
33
101
 
34
- li {
35
- list-style: none;
102
+ .menu-item {
103
+ position: relative;
36
104
  display: flex;
37
105
  align-items: center;
38
- min-width: fit-content;
39
106
  }
40
107
 
41
- a {
108
+ .menu-link {
42
109
  cursor: pointer;
43
110
  font-size: 16px;
44
111
  font-weight: 500;
@@ -47,7 +114,13 @@ a {
47
114
  transition: color 0.35s ease;
48
115
 
49
116
  &:hover {
50
- color: #0056b3
117
+ color: #0056b3;
51
118
  }
52
119
  }
120
+
121
+ .arrow {
122
+ margin-left: 8px;
123
+ font-size: 8px;
124
+ color: #cacaca;
125
+ }
53
126
  </style>
@@ -9,15 +9,13 @@
9
9
  "dependencies": {
10
10
  "@lightspeed/crane": "latest",
11
11
  "@lightspeed/eslint-config-crane": "latest",
12
- "vue": "^3.4.0"
12
+ "vue": "^3.4.0",
13
+ "eslint": "9.22.0"
13
14
  },
14
15
  "engines": {
15
- "node": ">=20"
16
+ "node": ">=22"
16
17
  },
17
18
  "overrides": {
18
- "eslint": {
19
- "file-entry-cache": "8.0.0"
20
- },
21
19
  "glob": "^9.3.5",
22
20
  "stylus": "^0.63.0"
23
21
  }
@@ -0,0 +1,22 @@
1
+ export default {
2
+ metadata: {
3
+ name: 'Example Template :: Standard Preset',
4
+ description: 'Standard Preset for the Example template',
5
+ preview_url: 'https://template_preview.company.site',
6
+ cover_image: {
7
+ set: {
8
+ ORIGINAL: {
9
+ url: 'template_cover_image.png',
10
+ },
11
+ },
12
+ },
13
+ },
14
+ header: {
15
+ type: 'default',
16
+ id: 'header',
17
+ },
18
+ footer: {
19
+ type: 'default',
20
+ id: 'footer',
21
+ },
22
+ };
@@ -0,0 +1,8 @@
1
+ export default {
2
+ sections: [
3
+ {
4
+ type: 'default',
5
+ id: 'product-browser',
6
+ },
7
+ ],
8
+ };
@@ -0,0 +1,8 @@
1
+ export default {
2
+ sections: [
3
+ {
4
+ type: 'default',
5
+ id: 'product-browser',
6
+ },
7
+ ],
8
+ };
@@ -0,0 +1,178 @@
1
+ export default {
2
+ sections: [
3
+ {
4
+ type: 'default',
5
+ id: 'slider',
6
+ showcase_id: '001',
7
+ },
8
+ {
9
+ type: 'custom',
10
+ id: 'example-section',
11
+ showcase_id: '1',
12
+ },
13
+ {
14
+ type: 'custom',
15
+ id: 'example-section',
16
+ showcase_overrides: {
17
+ content: {
18
+ section_title: {
19
+ type: 'INPUTBOX',
20
+ text: '$label.template_standard_showcase_1.section_title.text',
21
+ },
22
+
23
+ images: {
24
+ type: 'DECK',
25
+ cards: [
26
+ {
27
+ settings: {
28
+ image_text: {
29
+ type: 'TEXTAREA',
30
+ text: '$label.showcase_2.image_text_1.text',
31
+ },
32
+ image_content: {
33
+ type: 'IMAGE',
34
+ imageData: {
35
+ set: {
36
+ MOBILE_WEBP_LOW_RES: {
37
+ url: 'new_tshirts_collection_mobile_low.jpeg',
38
+ },
39
+ MOBILE_WEBP_HI_RES: {
40
+ url: 'new_tshirts_collection_mobile_high.jpeg',
41
+ },
42
+ WEBP_LOW_RES: {
43
+ url: 'new_tshirts_collection_pc_low.jpeg',
44
+ },
45
+ WEBP_HI_2X_RES: {
46
+ url: 'new_tshirts_collection_pc_high.jpeg',
47
+ },
48
+ },
49
+ borderInfo: {},
50
+ },
51
+ },
52
+ image_link: {
53
+ type: 'INPUTBOX',
54
+ text: '$label.showcase_2.image_link_1.text',
55
+ },
56
+ },
57
+ },
58
+ {
59
+ settings: {
60
+ image_text: {
61
+ type: 'TEXTAREA',
62
+ text: '$label.showcase_2.image_text_2.text',
63
+ },
64
+ image_content: {
65
+ type: 'IMAGE',
66
+ imageData: {
67
+ set: {
68
+ MOBILE_WEBP_LOW_RES: {
69
+ url: 'autumn_looks_mobile_low.jpeg',
70
+ },
71
+ MOBILE_WEBP_HI_RES: {
72
+ url: 'autumn_looks_mobile_high.jpeg',
73
+ },
74
+ WEBP_LOW_RES: {
75
+ url: 'autumn_looks_pc_low.jpeg',
76
+ },
77
+ WEBP_HI_2X_RES: {
78
+ url: 'autumn_looks_pc_high.jpeg',
79
+ },
80
+ },
81
+ borderInfo: {},
82
+ },
83
+ },
84
+ },
85
+ },
86
+
87
+ {
88
+ settings: {
89
+ image_text: {
90
+ type: 'TEXTAREA',
91
+ text: '$label.showcase_2.image_text_3.text',
92
+ },
93
+ image_content: {
94
+ type: 'IMAGE',
95
+ imageData: {
96
+ set: {
97
+ MOBILE_WEBP_LOW_RES: {
98
+ url: 'bianka_wardrobe_mobile_low.jpeg',
99
+ },
100
+ MOBILE_WEBP_HI_RES: {
101
+ url: 'bianka_wardrobe_mobile_high.jpeg',
102
+ },
103
+ WEBP_LOW_RES: {
104
+ url: 'bianka_wardrobe_pc_low.jpeg',
105
+ },
106
+ WEBP_HI_2X_RES: {
107
+ url: 'bianka_wardrobe_pc_high.jpeg',
108
+ },
109
+ },
110
+ borderInfo: {},
111
+ },
112
+ },
113
+ },
114
+ },
115
+
116
+ {
117
+ settings: {
118
+ image_text: {
119
+ type: 'TEXTAREA',
120
+ text: '$label.showcase_2.image_text_4.text',
121
+ },
122
+ image_content: {
123
+ type: 'IMAGE',
124
+ imageData: {
125
+ set: {
126
+ MOBILE_WEBP_LOW_RES: {
127
+ url: 'story_of_jane_mobile_low.jpeg',
128
+ },
129
+ MOBILE_WEBP_HI_RES: {
130
+ url: 'story_of_jane_mobile_high.jpeg',
131
+ },
132
+ WEBP_LOW_RES: {
133
+ url: 'story_of_jane_pc_low.jpeg',
134
+ },
135
+ WEBP_HI_2X_RES: {
136
+ url: 'story_of_jane_pc_high.jpeg',
137
+ },
138
+ },
139
+ borderInfo: {},
140
+ },
141
+ },
142
+ },
143
+ },
144
+ ],
145
+ },
146
+ },
147
+ design: {
148
+ section_title: {
149
+ type: 'TEXT',
150
+ font: 'global.fontFamily.body',
151
+ size: 42,
152
+ bold: true,
153
+ italic: true,
154
+ color: '#222',
155
+ },
156
+ image_text: {
157
+ type: 'TEXT',
158
+ size: 22,
159
+ bold: false,
160
+ italic: true,
161
+ },
162
+ background: {
163
+ type: 'BACKGROUND',
164
+ style: 'COLOR',
165
+ color: 'global.color.background',
166
+ },
167
+ image_content: {
168
+ type: 'IMAGE',
169
+ overlay: 'GRADIENT',
170
+ color: ['#FFFFFF', '#CCCCCC'],
171
+ },
172
+ },
173
+ layoutId: 'Caption_Under_Image',
174
+ blockName: '$label.showcase_2.blockName',
175
+ },
176
+ },
177
+ ],
178
+ };
@@ -0,0 +1,8 @@
1
+ export default {
2
+ sections: [
3
+ {
4
+ type: 'default',
5
+ id: 'product-browser',
6
+ },
7
+ ],
8
+ };
@@ -41,20 +41,17 @@ const content: ContentEditor = {
41
41
  label: '$label.image_content.label',
42
42
  defaults: {
43
43
  set: {
44
- LOW_RES: {
45
- url: 'section_picture.jpg',
46
- },
47
44
  MOBILE_WEBP_LOW_RES: {
48
- url: 'section_picture.jpg',
45
+ url: 'our_company_in_numbers.jpg',
49
46
  },
50
47
  MOBILE_WEBP_HI_RES: {
51
- url: 'section_picture@2x.jpg',
48
+ url: 'our_company_in_numbers.jpg',
52
49
  },
53
50
  WEBP_LOW_RES: {
54
- url: 'section_picture@2x.jpg',
51
+ url: 'our_company_in_numbers.jpg',
55
52
  },
56
53
  WEBP_HI_2X_RES: {
57
- url: 'section_picture@2x.jpg',
54
+ url: 'our_company_in_numbers.jpg',
58
55
  },
59
56
  },
60
57
  borderInfo: {},
@@ -18,9 +18,6 @@ export default {
18
18
  type: 'IMAGE',
19
19
  imageData: {
20
20
  set: {
21
- LOW_RES: {
22
- url: 'our_company_in_numbers.jpg',
23
- },
24
21
  MOBILE_WEBP_LOW_RES: {
25
22
  url: 'our_company_in_numbers.jpg',
26
23
  },
@@ -18,9 +18,6 @@ export default {
18
18
  type: 'IMAGE',
19
19
  imageData: {
20
20
  set: {
21
- LOW_RES: {
22
- url: 'our_team.jpg',
23
- },
24
21
  MOBILE_WEBP_LOW_RES: {
25
22
  url: 'our_team.jpg',
26
23
  },
@@ -35,6 +35,7 @@ export default {
35
35
  image_text: {
36
36
  type: 'TEXTAREA',
37
37
  label: '$label.slide_text.label',
38
+ placeholder: '$label.slide_text.label',
38
39
  },
39
40
  },
40
41
  },
@@ -40,9 +40,6 @@ export default {
40
40
  type: 'IMAGE',
41
41
  imageData: {
42
42
  set: {
43
- LOW_RES: {
44
- url: 'category_1.jpg',
45
- },
46
43
  MOBILE_WEBP_LOW_RES: {
47
44
  url: 'category_1.jpg',
48
45
  },
@@ -71,9 +68,6 @@ export default {
71
68
  type: 'IMAGE',
72
69
  imageData: {
73
70
  set: {
74
- LOW_RES: {
75
- url: 'category_2.jpg',
76
- },
77
71
  MOBILE_WEBP_LOW_RES: {
78
72
  url: 'category_2.jpg',
79
73
  },
@@ -102,9 +96,6 @@ export default {
102
96
  type: 'IMAGE',
103
97
  imageData: {
104
98
  set: {
105
- LOW_RES: {
106
- url: 'category_3.jpg',
107
- },
108
99
  MOBILE_WEBP_LOW_RES: {
109
100
  url: 'category_3.jpg',
110
101
  },
@@ -133,9 +124,6 @@ export default {
133
124
  type: 'IMAGE',
134
125
  imageData: {
135
126
  set: {
136
- LOW_RES: {
137
- url: 'category_4.jpg',
138
- },
139
127
  MOBILE_WEBP_LOW_RES: {
140
128
  url: 'category_4.jpg',
141
129
  },
@@ -165,9 +153,6 @@ export default {
165
153
  type: 'IMAGE',
166
154
  imageData: {
167
155
  set: {
168
- LOW_RES: {
169
- url: 'category_5.jpg',
170
- },
171
156
  MOBILE_WEBP_LOW_RES: {
172
157
  url: 'category_5.jpg',
173
158
  },
@@ -196,9 +181,6 @@ export default {
196
181
  type: 'IMAGE',
197
182
  imageData: {
198
183
  set: {
199
- LOW_RES: {
200
- url: 'category_6.png',
201
- },
202
184
  MOBILE_WEBP_LOW_RES: {
203
185
  url: 'category_6.jpg',
204
186
  },