@katlux/block-ecommerce 0.1.0-beta.0 → 0.1.0-beta.2

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.
@@ -0,0 +1,6 @@
1
+ interface ModuleOptions {
2
+ }
3
+ declare const _default: any;
4
+
5
+ export { _default as default };
6
+ export type { ModuleOptions };
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "@katlux/block-ecommerce",
3
+ "configKey": "katluxEcommerceBlocks",
4
+ "compatibility": {
5
+ "nuxt": "^3.0.0"
6
+ },
7
+ "version": "0.1.0-beta.2",
8
+ "builder": {
9
+ "@nuxt/module-builder": "1.0.2",
10
+ "unbuild": "3.6.1"
11
+ }
12
+ }
@@ -0,0 +1,23 @@
1
+ import { defineNuxtModule, createResolver, addComponentsDir } from '@nuxt/kit';
2
+
3
+ const module$1 = defineNuxtModule({
4
+ meta: {
5
+ name: "@katlux/block-ecommerce",
6
+ configKey: "katluxEcommerceBlocks",
7
+ compatibility: {
8
+ nuxt: "^3.0.0"
9
+ }
10
+ },
11
+ defaults: {},
12
+ async setup(options, nuxt) {
13
+ const resolver = createResolver(import.meta.url);
14
+ addComponentsDir({
15
+ path: resolver.resolve("./runtime/components"),
16
+ pathPrefix: false,
17
+ prefix: "",
18
+ global: true
19
+ });
20
+ }
21
+ });
22
+
23
+ export { module$1 as default };
@@ -0,0 +1,32 @@
1
+ import type { KProductRowAction } from '@katlux/providers';
2
+ import { ADataProvider } from '@katlux/providers';
3
+ type __VLS_Props = {
4
+ dataProvider: ADataProvider;
5
+ layout?: 'grid' | 'list';
6
+ rowActions?: KProductRowAction[];
7
+ gridColumns?: number | string;
8
+ gap?: string;
9
+ };
10
+ declare var __VLS_1: {
11
+ item: import("@katlux/providers").TDataRow;
12
+ index: number;
13
+ }, __VLS_11: string, __VLS_12: any;
14
+ type __VLS_Slots = {} & {
15
+ [K in NonNullable<typeof __VLS_11>]?: (props: typeof __VLS_12) => any;
16
+ } & {
17
+ default?: (props: typeof __VLS_1) => any;
18
+ };
19
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
20
+ layout: "grid" | "list";
21
+ rowActions: KProductRowAction[];
22
+ gridColumns: number | string;
23
+ gap: string;
24
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
25
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
26
+ declare const _default: typeof __VLS_export;
27
+ export default _default;
28
+ type __VLS_WithSlots<T, S> = T & {
29
+ new (): {
30
+ $slots: S;
31
+ };
32
+ };
@@ -0,0 +1,52 @@
1
+ import type { ADataProvider, KProductRowAction } from '@katlux/providers';
2
+ import { type PropType } from 'vue';
3
+ export interface KProductListProps {
4
+ dataProvider: ADataProvider;
5
+ layout?: 'grid' | 'list';
6
+ rowActions?: KProductRowAction[];
7
+ gridColumns?: number | string;
8
+ gap?: string;
9
+ }
10
+ export declare const KProductListDefaultProps: {
11
+ dataProvider: {
12
+ type: PropType<ADataProvider>;
13
+ required: boolean;
14
+ };
15
+ layout: {
16
+ type: PropType<"grid" | "list">;
17
+ default: string;
18
+ };
19
+ rowActions: {
20
+ type: PropType<KProductRowAction[]>;
21
+ default: () => never[];
22
+ };
23
+ gridColumns: {
24
+ type: (StringConstructor | NumberConstructor)[];
25
+ default: number;
26
+ };
27
+ gap: {
28
+ type: StringConstructor;
29
+ default: string;
30
+ };
31
+ };
32
+ export declare function useKProductListLogic(props: KProductListProps): {
33
+ isGrid: import("vue").ComputedRef<boolean>;
34
+ gridStyles: import("vue").ComputedRef<{
35
+ display?: undefined;
36
+ gridTemplateColumns?: undefined;
37
+ gap?: undefined;
38
+ } | {
39
+ display: string;
40
+ gridTemplateColumns: string;
41
+ gap: string | undefined;
42
+ }>;
43
+ listStyles: import("vue").ComputedRef<{
44
+ display?: undefined;
45
+ flexDirection?: undefined;
46
+ gap?: undefined;
47
+ } | {
48
+ display: string;
49
+ flexDirection: "column";
50
+ gap: string | undefined;
51
+ }>;
52
+ };
@@ -0,0 +1,48 @@
1
+ import { computed } from "vue";
2
+ export const KProductListDefaultProps = {
3
+ dataProvider: {
4
+ type: Object,
5
+ required: true
6
+ },
7
+ layout: {
8
+ type: String,
9
+ default: "grid"
10
+ },
11
+ rowActions: {
12
+ type: Array,
13
+ default: () => []
14
+ },
15
+ gridColumns: {
16
+ type: [Number, String],
17
+ default: 4
18
+ },
19
+ gap: {
20
+ type: String,
21
+ default: "1rem"
22
+ }
23
+ };
24
+ export function useKProductListLogic(props) {
25
+ const isGrid = computed(() => props.layout !== "list");
26
+ const gridStyles = computed(() => {
27
+ if (!isGrid.value) return {};
28
+ const cols = props.gridColumns;
29
+ return {
30
+ display: "grid",
31
+ gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))`,
32
+ gap: props.gap
33
+ };
34
+ });
35
+ const listStyles = computed(() => {
36
+ if (isGrid.value) return {};
37
+ return {
38
+ display: "flex",
39
+ flexDirection: "column",
40
+ gap: props.gap
41
+ };
42
+ });
43
+ return {
44
+ isGrid,
45
+ gridStyles,
46
+ listStyles
47
+ };
48
+ }
@@ -29,42 +29,32 @@
29
29
  </div>
30
30
  </template>
31
31
 
32
- <script lang="ts" setup>
33
- import type { KProductItemData, KProductRowAction } from '@katlux/providers'
34
- import { ADataProvider } from '@katlux/providers'
35
- import { useKProductListLogic } from './KProductList.logic'
36
-
37
- const props = withDefaults(defineProps<{
38
- dataProvider: ADataProvider
39
- layout?: 'grid' | 'list'
40
- rowActions?: KProductRowAction[]
41
- gridColumns?: number | string
42
- gap?: string
43
- }>(), {
44
- layout: 'grid',
45
- rowActions: () => [],
46
- gridColumns: 4,
47
- gap: '1rem'
48
- })
49
-
50
- const { isGrid, gridStyles, listStyles } = useKProductListLogic(props as any)
32
+ <script setup>
33
+ import { ADataProvider } from "@katlux/providers";
34
+ import { useKProductListLogic } from "./KProductList.logic";
35
+ const props = defineProps({
36
+ dataProvider: { type: Object, required: true },
37
+ layout: { type: String, required: false, default: "grid" },
38
+ rowActions: { type: Array, required: false, default: () => [] },
39
+ gridColumns: { type: [Number, String], required: false, default: 4 },
40
+ gap: { type: String, required: false, default: "1rem" }
41
+ });
42
+ const { isGrid, gridStyles, listStyles } = useKProductListLogic(props);
51
43
  </script>
52
44
 
53
- <style lang="scss" scoped>
45
+ <style scoped>
54
46
  .k-product-list {
55
- width: 100%;
56
-
57
- .list-container {
58
- width: 100%;
59
- }
60
-
61
- .footer {
62
- display: flex;
63
- justify-content: flex-end;
64
- margin-top: var(--gap-lg, 24px);
65
- .pagination {
66
- flex: 1;
67
- }
68
- }
47
+ width: 100%;
48
+ }
49
+ .k-product-list .list-container {
50
+ width: 100%;
51
+ }
52
+ .k-product-list .footer {
53
+ display: flex;
54
+ justify-content: flex-end;
55
+ margin-top: var(--gap-lg, 24px);
56
+ }
57
+ .k-product-list .footer .pagination {
58
+ flex: 1;
69
59
  }
70
60
  </style>
@@ -0,0 +1,32 @@
1
+ import type { KProductRowAction } from '@katlux/providers';
2
+ import { ADataProvider } from '@katlux/providers';
3
+ type __VLS_Props = {
4
+ dataProvider: ADataProvider;
5
+ layout?: 'grid' | 'list';
6
+ rowActions?: KProductRowAction[];
7
+ gridColumns?: number | string;
8
+ gap?: string;
9
+ };
10
+ declare var __VLS_1: {
11
+ item: import("@katlux/providers").TDataRow;
12
+ index: number;
13
+ }, __VLS_11: string, __VLS_12: any;
14
+ type __VLS_Slots = {} & {
15
+ [K in NonNullable<typeof __VLS_11>]?: (props: typeof __VLS_12) => any;
16
+ } & {
17
+ default?: (props: typeof __VLS_1) => any;
18
+ };
19
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
20
+ layout: "grid" | "list";
21
+ rowActions: KProductRowAction[];
22
+ gridColumns: number | string;
23
+ gap: string;
24
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
25
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
26
+ declare const _default: typeof __VLS_export;
27
+ export default _default;
28
+ type __VLS_WithSlots<T, S> = T & {
29
+ new (): {
30
+ $slots: S;
31
+ };
32
+ };
@@ -0,0 +1,8 @@
1
+ import type { KProductItemData, KProductRowAction } from '@katlux/providers';
2
+ type __VLS_Props = {
3
+ item: KProductItemData;
4
+ rowActions?: KProductRowAction[];
5
+ };
6
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
@@ -0,0 +1,20 @@
1
+ import { type PropType } from 'vue';
2
+ import type { KProductItemData, KProductRowAction } from '@katlux/providers';
3
+ export interface KProductListItemProps {
4
+ item: KProductItemData;
5
+ rowActions?: KProductRowAction[];
6
+ }
7
+ export declare const KProductListItemDefaultProps: {
8
+ item: {
9
+ type: PropType<KProductItemData>;
10
+ required: true;
11
+ };
12
+ rowActions: {
13
+ type: PropType<KProductRowAction[]>;
14
+ default: () => never[];
15
+ };
16
+ };
17
+ export declare function useKProductListItemLogic(props: KProductListItemProps): {
18
+ hasActions: import("vue").ComputedRef<boolean | undefined>;
19
+ formattedPrice: import("vue").ComputedRef<string>;
20
+ };
@@ -0,0 +1,25 @@
1
+ import { computed } from "vue";
2
+ export const KProductListItemDefaultProps = {
3
+ item: {
4
+ type: Object,
5
+ required: true
6
+ },
7
+ rowActions: {
8
+ type: Array,
9
+ default: () => []
10
+ }
11
+ };
12
+ export function useKProductListItemLogic(props) {
13
+ const hasActions = computed(() => props.rowActions && props.rowActions.length > 0);
14
+ const formattedPrice = computed(() => {
15
+ if (props.item.price === void 0) return "";
16
+ return new Intl.NumberFormat("en-US", {
17
+ style: "currency",
18
+ currency: props.item.currency || "USD"
19
+ }).format(props.item.price);
20
+ });
21
+ return {
22
+ hasActions,
23
+ formattedPrice
24
+ };
25
+ }
@@ -0,0 +1,113 @@
1
+ <template lang="pug">
2
+ .k-product-list-item
3
+ .item-image(v-if="item.image")
4
+ img(:src="item.image" :alt="item.title")
5
+ .item-image-placeholder(v-else)
6
+ KIcon(name="tabler:photo" class="placeholder-icon")
7
+ .item-details
8
+ h3.item-title {{ item.title }}
9
+ p.item-description(v-if="item.description") {{ item.description }}
10
+ .item-price(v-if="formattedPrice") {{ formattedPrice }}
11
+ .item-actions(v-if="hasActions")
12
+ KButton(
13
+ v-for="(action, index) in rowActions"
14
+ :key="index"
15
+ :class="action.color || 'primary'"
16
+ @click="action.action(item)"
17
+ )
18
+ template(v-if="action.icon" #icon)
19
+ KIcon(:name="action.icon")
20
+ | {{ action.label }}
21
+ </template>
22
+
23
+ <script setup>
24
+ const props = defineProps({
25
+ item: { type: Object, required: true },
26
+ rowActions: { type: Array, required: false }
27
+ });
28
+ import { useKProductListItemLogic } from "./KProductListItem.logic";
29
+ const { hasActions, formattedPrice } = useKProductListItemLogic(props);
30
+ </script>
31
+
32
+ <style scoped>
33
+ .k-product-list-item {
34
+ display: flex;
35
+ flex-direction: column;
36
+ border: 1px solid var(--border-color-light, #e9ecef);
37
+ border-radius: var(--border-radius-md, 8px);
38
+ overflow: hidden;
39
+ background: var(--bg-color-surface, #ffffff);
40
+ transition: box-shadow 0.2s ease, transform 0.2s ease;
41
+ height: 100%;
42
+ }
43
+ .k-product-list-item:hover {
44
+ box-shadow: var(--shadow-md, 0 4px 12px rgba(0, 0, 0, 0.08));
45
+ transform: translateY(-2px);
46
+ }
47
+ .k-product-list-item .item-image, .k-product-list-item .item-image-placeholder {
48
+ width: 100%;
49
+ aspect-ratio: 1;
50
+ overflow: hidden;
51
+ display: flex;
52
+ align-items: center;
53
+ justify-content: center;
54
+ background: var(--bg-color-muted, #f8f9fa);
55
+ border-bottom: 1px solid var(--border-color-light, #e9ecef);
56
+ }
57
+ .k-product-list-item .item-image img {
58
+ width: 100%;
59
+ height: 100%;
60
+ object-fit: cover;
61
+ transition: transform 0.3s ease;
62
+ }
63
+ .k-product-list-item:hover .item-image img {
64
+ transform: scale(1.05);
65
+ }
66
+ .k-product-list-item .item-image-placeholder .placeholder-icon {
67
+ font-size: 3rem;
68
+ color: var(--text-color-muted, #adb5bd);
69
+ opacity: 0.5;
70
+ }
71
+ .k-product-list-item .item-details {
72
+ padding: var(--gap-md, 16px);
73
+ flex-grow: 1;
74
+ display: flex;
75
+ flex-direction: column;
76
+ }
77
+ .k-product-list-item .item-title {
78
+ margin: 0 0 var(--gap-xs, 4px) 0;
79
+ font-size: var(--font-size-lg, 1.1rem);
80
+ font-weight: 600;
81
+ color: var(--text-color-primary, #212529);
82
+ line-height: 1.3;
83
+ }
84
+ .k-product-list-item .item-description {
85
+ margin: 0 0 var(--gap-sm, 8px) 0;
86
+ font-size: var(--font-size-sm, 0.9rem);
87
+ color: var(--text-color-secondary, #6c757d);
88
+ line-height: 1.4;
89
+ flex-grow: 1;
90
+ display: -webkit-box;
91
+ -webkit-line-clamp: 2;
92
+ -webkit-box-orient: vertical;
93
+ overflow: hidden;
94
+ }
95
+ .k-product-list-item .item-price {
96
+ font-size: var(--font-size-xl, 1.25rem);
97
+ font-weight: 700;
98
+ color: var(--color-primary, #0d6efd);
99
+ margin-top: auto;
100
+ padding-top: var(--gap-sm, 8px);
101
+ }
102
+ .k-product-list-item .item-actions {
103
+ padding: var(--gap-md, 16px);
104
+ padding-top: 0;
105
+ display: flex;
106
+ gap: var(--gap-sm, 8px);
107
+ flex-wrap: wrap;
108
+ }
109
+ .k-product-list-item .item-actions :deep(.KButton) {
110
+ flex: 1;
111
+ min-width: 100px;
112
+ }
113
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { KProductItemData, KProductRowAction } from '@katlux/providers';
2
+ type __VLS_Props = {
3
+ item: KProductItemData;
4
+ rowActions?: KProductRowAction[];
5
+ };
6
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
@@ -0,0 +1,3 @@
1
+ export { default } from './module.mjs'
2
+
3
+ export { type ModuleOptions } from './module.mjs'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@katlux/block-ecommerce",
3
- "version": "0.1.0-beta.0",
3
+ "version": "0.1.0-beta.2",
4
4
  "description": "Pre-built eCommerce block components for Katlux toolkit",
5
5
  "author": "Katlux",
6
6
  "license": "MIT",
@@ -10,7 +10,8 @@
10
10
  "main": "./dist/module.mjs",
11
11
  "scripts": {
12
12
  "build": "nuxt-module-build build",
13
- "dev": "nuxt-module-build build --stub"
13
+ "dev": "nuxt-module-build build --stub",
14
+ "prepublishOnly": "npm run build"
14
15
  },
15
16
  "exports": {
16
17
  ".": {
@@ -27,5 +28,8 @@
27
28
  "@nuxt/kit": "^3.20.1",
28
29
  "pug": "^3.0.0",
29
30
  "sass": "^1.80.0"
30
- }
31
+ },
32
+ "files": [
33
+ "dist"
34
+ ]
31
35
  }
package/build.config.ts DELETED
@@ -1,4 +0,0 @@
1
- export default {
2
- failOnWarn: false,
3
- externals: ['#app', '@katlux/toolkit', '@katlux/providers']
4
- }
package/src/module.ts DELETED
@@ -1,25 +0,0 @@
1
- import { defineNuxtModule, createResolver, addComponentsDir } from '@nuxt/kit'
2
-
3
- export interface ModuleOptions { }
4
-
5
- export default defineNuxtModule<ModuleOptions>({
6
- meta: {
7
- name: '@katlux/block-ecommerce',
8
- configKey: 'katluxEcommerceBlocks',
9
- compatibility: {
10
- nuxt: '^3.0.0'
11
- }
12
- },
13
- defaults: {},
14
- async setup(options, nuxt) {
15
- const resolver = createResolver(import.meta.url)
16
-
17
- // Add components directory
18
- addComponentsDir({
19
- path: resolver.resolve('./runtime/components'),
20
- pathPrefix: false,
21
- prefix: '',
22
- global: true
23
- })
24
- }
25
- })
@@ -1,64 +0,0 @@
1
- import type { ADataProvider, KProductItemData, KProductRowAction } from '@katlux/providers'
2
- import { computed, type PropType } from 'vue'
3
-
4
- export interface KProductListProps {
5
- dataProvider: ADataProvider
6
- layout?: 'grid' | 'list'
7
- rowActions?: KProductRowAction[]
8
- gridColumns?: number | string
9
- gap?: string
10
- }
11
-
12
- export const KProductListDefaultProps = {
13
- dataProvider: {
14
- type: Object as PropType<ADataProvider>,
15
- required: true,
16
- },
17
- layout: {
18
- type: String as PropType<'grid' | 'list'>,
19
- default: 'grid',
20
- },
21
- rowActions: {
22
- type: Array as PropType<KProductRowAction[]>,
23
- default: () => [],
24
- },
25
- gridColumns: {
26
- type: [Number, String],
27
- default: 4,
28
- },
29
- gap: {
30
- type: String,
31
- default: '1rem',
32
- }
33
- }
34
-
35
- export function useKProductListLogic(props: KProductListProps) {
36
- const isGrid = computed(() => props.layout !== 'list')
37
-
38
- const gridStyles = computed(() => {
39
- if (!isGrid.value) return {}
40
-
41
- const cols = props.gridColumns
42
- return {
43
- display: 'grid',
44
- gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))`,
45
- gap: props.gap
46
- }
47
- })
48
-
49
- const listStyles = computed(() => {
50
- if (isGrid.value) return {}
51
-
52
- return {
53
- display: 'flex',
54
- flexDirection: 'column' as const,
55
- gap: props.gap
56
- }
57
- })
58
-
59
- return {
60
- isGrid,
61
- gridStyles,
62
- listStyles
63
- }
64
- }
@@ -1,36 +0,0 @@
1
- import { computed, type PropType } from 'vue'
2
- import type { KProductItemData, KProductRowAction } from '@katlux/providers'
3
-
4
-
5
- export interface KProductListItemProps {
6
- item: KProductItemData
7
- rowActions?: KProductRowAction[]
8
- }
9
-
10
- export const KProductListItemDefaultProps = {
11
- item: {
12
- type: Object as PropType<KProductItemData>,
13
- required: true as const,
14
- },
15
- rowActions: {
16
- type: Array as PropType<KProductRowAction[]>,
17
- default: () => [],
18
- },
19
- }
20
-
21
- export function useKProductListItemLogic(props: KProductListItemProps) {
22
- const hasActions = computed(() => props.rowActions && props.rowActions.length > 0)
23
-
24
- const formattedPrice = computed(() => {
25
- if (props.item.price === undefined) return ''
26
- return new Intl.NumberFormat('en-US', {
27
- style: 'currency',
28
- currency: props.item.currency || 'USD'
29
- }).format(props.item.price)
30
- })
31
-
32
- return {
33
- hasActions,
34
- formattedPrice
35
- }
36
- }
@@ -1,129 +0,0 @@
1
- <template lang="pug">
2
- .k-product-list-item
3
- .item-image(v-if="item.image")
4
- img(:src="item.image" :alt="item.title")
5
- .item-image-placeholder(v-else)
6
- KIcon(name="tabler:photo" class="placeholder-icon")
7
- .item-details
8
- h3.item-title {{ item.title }}
9
- p.item-description(v-if="item.description") {{ item.description }}
10
- .item-price(v-if="formattedPrice") {{ formattedPrice }}
11
- .item-actions(v-if="hasActions")
12
- KButton(
13
- v-for="(action, index) in rowActions"
14
- :key="index"
15
- :class="action.color || 'primary'"
16
- @click="action.action(item)"
17
- )
18
- template(v-if="action.icon" #icon)
19
- KIcon(:name="action.icon")
20
- | {{ action.label }}
21
- </template>
22
-
23
- <script lang="ts" setup>
24
- import type { KProductItemData, KProductRowAction } from '@katlux/providers'
25
-
26
- const props = defineProps<{
27
- item: KProductItemData
28
- rowActions?: KProductRowAction[]
29
- }>()
30
-
31
- import { useKProductListItemLogic } from './KProductListItem.logic'
32
- const { hasActions, formattedPrice } = useKProductListItemLogic(props)
33
- </script>
34
-
35
- <style lang="scss" scoped>
36
- .k-product-list-item {
37
- display: flex;
38
- flex-direction: column;
39
- border: 1px solid var(--border-color-light, #e9ecef);
40
- border-radius: var(--border-radius-md, 8px);
41
- overflow: hidden;
42
- background: var(--bg-color-surface, #ffffff);
43
- transition: box-shadow 0.2s ease, transform 0.2s ease;
44
- height: 100%;
45
-
46
- &:hover {
47
- box-shadow: var(--shadow-md, 0 4px 12px rgba(0,0,0,0.08));
48
- transform: translateY(-2px);
49
- }
50
-
51
- .item-image, .item-image-placeholder {
52
- width: 100%;
53
- aspect-ratio: 1;
54
- overflow: hidden;
55
- display: flex;
56
- align-items: center;
57
- justify-content: center;
58
- background: var(--bg-color-muted, #f8f9fa);
59
- border-bottom: 1px solid var(--border-color-light, #e9ecef);
60
- }
61
-
62
- .item-image img {
63
- width: 100%;
64
- height: 100%;
65
- object-fit: cover;
66
- transition: transform 0.3s ease;
67
- }
68
-
69
- &:hover .item-image img {
70
- transform: scale(1.05);
71
- }
72
-
73
- .item-image-placeholder {
74
- .placeholder-icon {
75
- font-size: 3rem;
76
- color: var(--text-color-muted, #adb5bd);
77
- opacity: 0.5;
78
- }
79
- }
80
-
81
- .item-details {
82
- padding: var(--gap-md, 16px);
83
- flex-grow: 1;
84
- display: flex;
85
- flex-direction: column;
86
- }
87
-
88
- .item-title {
89
- margin: 0 0 var(--gap-xs, 4px) 0;
90
- font-size: var(--font-size-lg, 1.1rem);
91
- font-weight: 600;
92
- color: var(--text-color-primary, #212529);
93
- line-height: 1.3;
94
- }
95
-
96
- .item-description {
97
- margin: 0 0 var(--gap-sm, 8px) 0;
98
- font-size: var(--font-size-sm, 0.9rem);
99
- color: var(--text-color-secondary, #6c757d);
100
- line-height: 1.4;
101
- flex-grow: 1;
102
- display: -webkit-box;
103
- -webkit-line-clamp: 2;
104
- -webkit-box-orient: vertical;
105
- overflow: hidden;
106
- }
107
-
108
- .item-price {
109
- font-size: var(--font-size-xl, 1.25rem);
110
- font-weight: 700;
111
- color: var(--color-primary, #0d6efd);
112
- margin-top: auto;
113
- padding-top: var(--gap-sm, 8px);
114
- }
115
-
116
- .item-actions {
117
- padding: var(--gap-md, 16px);
118
- padding-top: 0;
119
- display: flex;
120
- gap: var(--gap-sm, 8px);
121
- flex-wrap: wrap;
122
-
123
- :deep(.KButton) {
124
- flex: 1;
125
- min-width: 100px;
126
- }
127
- }
128
- }
129
- </style>