@propeller-commerce/propeller-v2-vue-ui 0.3.14
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/CHANGELOG.md +549 -0
- package/LICENSE +21 -0
- package/MIGRATION.md +178 -0
- package/README.md +282 -0
- package/STYLING.md +119 -0
- package/dist/ProductVideos.vue_vue_type_script_setup_true_lang-BSXOpWBD.js +1706 -0
- package/dist/ProductVideos.vue_vue_type_script_setup_true_lang-BSXOpWBD.js.map +1 -0
- package/dist/ProductVideos.vue_vue_type_script_setup_true_lang-cfRT3L_k.cjs +1705 -0
- package/dist/ProductVideos.vue_vue_type_script_setup_true_lang-cfRT3L_k.cjs.map +1 -0
- package/dist/__mocks__/decorators.d.ts +17 -0
- package/dist/__mocks__/fixtures.d.ts +43 -0
- package/dist/__mocks__/mockServices.d.ts +7 -0
- package/dist/components/AccountIconAndMenu.vue.d.ts +152 -0
- package/dist/components/ActionCode.vue.d.ts +29 -0
- package/dist/components/AddToCart.vue.d.ts +122 -0
- package/dist/components/AddToFavorite.vue.d.ts +19 -0
- package/dist/components/AddressCard.vue.d.ts +169 -0
- package/dist/components/AddressSelector.vue.d.ts +30 -0
- package/dist/components/Breadcrumbs.vue.d.ts +69 -0
- package/dist/components/CartBonusItems.vue.d.ts +21 -0
- package/dist/components/CartCarriers.vue.d.ts +24 -0
- package/dist/components/CartIconAndSidebar.vue.d.ts +104 -0
- package/dist/components/CartItem.vue.d.ts +178 -0
- package/dist/components/CartOverview.vue.d.ts +41 -0
- package/dist/components/CartPaymethods.vue.d.ts +23 -0
- package/dist/components/CartSummary.vue.d.ts +58 -0
- package/dist/components/CategoryDescription.vue.d.ts +29 -0
- package/dist/components/CategoryShortDescription.vue.d.ts +18 -0
- package/dist/components/ClusterCard.vue.d.ts +251 -0
- package/dist/components/ClusterConfigurator.vue.d.ts +41 -0
- package/dist/components/ClusterInfo.vue.d.ts +73 -0
- package/dist/components/ClusterJsonLd.vue.d.ts +10 -0
- package/dist/components/ClusterOptions.vue.d.ts +44 -0
- package/dist/components/CompanySwitcher.vue.d.ts +18 -0
- package/dist/components/DeliveryDate.vue.d.ts +27 -0
- package/dist/components/FavoriteListDetails.vue.d.ts +94 -0
- package/dist/components/FavoriteListItem.vue.d.ts +141 -0
- package/dist/components/FavoriteLists.vue.d.ts +73 -0
- package/dist/components/ForgotPassword.vue.d.ts +35 -0
- package/dist/components/GridFilters.vue.d.ts +64 -0
- package/dist/components/GridPagination.vue.d.ts +34 -0
- package/dist/components/GridTitle.vue.d.ts +22 -0
- package/dist/components/GridToolbar.vue.d.ts +117 -0
- package/dist/components/ItemListJsonLd.vue.d.ts +10 -0
- package/dist/components/ItemStock.vue.d.ts +31 -0
- package/dist/components/ItemsOverview.vue.d.ts +40 -0
- package/dist/components/LoginForm.vue.d.ts +139 -0
- package/dist/components/Menu.vue.d.ts +73 -0
- package/dist/components/OrderActions.vue.d.ts +25 -0
- package/dist/components/OrderBonusItems.vue.d.ts +19 -0
- package/dist/components/OrderItemCard.vue.d.ts +47 -0
- package/dist/components/OrderList.vue.d.ts +66 -0
- package/dist/components/OrderShipments.vue.d.ts +11 -0
- package/dist/components/OrderSummary.vue.d.ts +49 -0
- package/dist/components/OrderTotals.vue.d.ts +34 -0
- package/dist/components/PriceToggle.vue.d.ts +24 -0
- package/dist/components/ProductBulkPrices.vue.d.ts +35 -0
- package/dist/components/ProductBundles.vue.d.ts +88 -0
- package/dist/components/ProductCard.vue.d.ts +332 -0
- package/dist/components/ProductDescription.vue.d.ts +30 -0
- package/dist/components/ProductDownloads.vue.d.ts +22 -0
- package/dist/components/ProductGallery.vue.d.ts +25 -0
- package/dist/components/ProductGrid.vue.d.ts +290 -0
- package/dist/components/ProductInfo.vue.d.ts +179 -0
- package/dist/components/ProductJsonLd.vue.d.ts +10 -0
- package/dist/components/ProductPrice.vue.d.ts +42 -0
- package/dist/components/ProductShortDescription.vue.d.ts +18 -0
- package/dist/components/ProductSlider.vue.d.ts +176 -0
- package/dist/components/ProductSpecifications.vue.d.ts +37 -0
- package/dist/components/ProductTabs.vue.d.ts +83 -0
- package/dist/components/ProductVideos.vue.d.ts +22 -0
- package/dist/components/PropellerProvider.vue.d.ts +40 -0
- package/dist/components/PurchaseAuthorizationConfigurator.vue.d.ts +44 -0
- package/dist/components/PurchaseAuthorizationRequests.vue.d.ts +50 -0
- package/dist/components/QuoteActions.vue.d.ts +22 -0
- package/dist/components/RegisterForm.vue.d.ts +87 -0
- package/dist/components/SearchBar.vue.d.ts +71 -0
- package/dist/components/UserDetails.vue.d.ts +44 -0
- package/dist/components/defaults/DefaultProductBadges.vue.d.ts +3 -0
- package/dist/components/defaults/DefaultProductImage.vue.d.ts +3 -0
- package/dist/components/defaults/DefaultProductSurcharges.vue.d.ts +3 -0
- package/dist/composables/shared/usePagination.d.ts +19 -0
- package/dist/composables/shared/useServiceFetch.d.ts +9 -0
- package/dist/composables/shared/useUserIdentity.d.ts +13 -0
- package/dist/composables/shared/utils/cartInit.d.ts +18 -0
- package/dist/composables/shared/utils/fetchActiveCart.d.ts +10 -0
- package/dist/composables/shared/utils/mergeAnonymousCart.d.ts +12 -0
- package/dist/composables/vue/useAddress.d.ts +50 -0
- package/dist/composables/vue/useAuth.d.ts +80 -0
- package/dist/composables/vue/useCart.d.ts +64 -0
- package/dist/composables/vue/useCheckout.d.ts +41 -0
- package/dist/composables/vue/useClusterConfigurator.d.ts +26 -0
- package/dist/composables/vue/useCompany.d.ts +31 -0
- package/dist/composables/vue/useFavorites.d.ts +39 -0
- package/dist/composables/vue/useInfraProps.d.ts +27 -0
- package/dist/composables/vue/useMenu.d.ts +24 -0
- package/dist/composables/vue/useOrders.d.ts +64 -0
- package/dist/composables/vue/useProductBundles.d.ts +37 -0
- package/dist/composables/vue/useProductInfo.d.ts +33 -0
- package/dist/composables/vue/useProductSearch.d.ts +49 -0
- package/dist/composables/vue/useProductSlider.d.ts +31 -0
- package/dist/composables/vue/useProductSpecs.d.ts +24 -0
- package/dist/composables/vue/usePurchaseAuthorization.d.ts +102 -0
- package/dist/composables/vue/useResolvedProps.d.ts +42 -0
- package/dist/composables/vue/useServices.d.ts +13 -0
- package/dist/context/ProductGridContext.d.ts +59 -0
- package/dist/context/PropellerContext.d.ts +51 -0
- package/dist/index-BN8nyGRL.js +518 -0
- package/dist/index-BN8nyGRL.js.map +1 -0
- package/dist/index-CrrZsxTR.cjs +517 -0
- package/dist/index-CrrZsxTR.cjs.map +1 -0
- package/dist/index.cjs +20086 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +109 -0
- package/dist/index.js +20088 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +67 -0
- package/dist/pure.cjs +16 -0
- package/dist/pure.cjs.map +1 -0
- package/dist/pure.d.ts +37 -0
- package/dist/pure.js +16 -0
- package/dist/pure.js.map +1 -0
- package/dist/shared.cjs +50 -0
- package/dist/shared.cjs.map +1 -0
- package/dist/shared.d.ts +14 -0
- package/dist/shared.js +50 -0
- package/dist/shared.js.map +1 -0
- package/dist/styles.css +2 -0
- package/package.json +91 -0
package/MIGRATION.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Migration guide
|
|
2
|
+
|
|
3
|
+
## `Menu`: optional pre-fetched `tree` prop (additive)
|
|
4
|
+
|
|
5
|
+
**When:** during `0.1.0` stabilization (pre-publish).
|
|
6
|
+
|
|
7
|
+
`Menu` now accepts an optional `tree?: MenuCategory[]` prop. When supplied,
|
|
8
|
+
the component skips its internal `useMenu` fetch and renders the tree
|
|
9
|
+
directly — mirroring the long-standing `ProductGrid.products` opt-in.
|
|
10
|
+
|
|
11
|
+
**No migration required.** Omitting the prop preserves the legacy
|
|
12
|
+
client-side fetch behaviour. This is purely opt-in for hosts that want to
|
|
13
|
+
move the category tree fetch into the SSR layer (e.g. propeller-vue's
|
|
14
|
+
`entry-server.ts` always-on prefetch) so the menu HTML lands in the initial
|
|
15
|
+
response and the host can attach cache tags via the SDK's `headers` config.
|
|
16
|
+
|
|
17
|
+
### Recommended pattern for Vue SSR consumers
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
// entry-server.ts — Vue 3 + Vite SSR (or any Node-side render hook)
|
|
21
|
+
import { fetchMenu, getAnonymousInfra } from '@/lib/server'
|
|
22
|
+
import { useMenuStore } from '@/stores/menu'
|
|
23
|
+
|
|
24
|
+
// Inside the render flow, before renderToString:
|
|
25
|
+
const tree = await fetchMenu(getAnonymousInfra(), BASE_CATEGORY_ID)
|
|
26
|
+
useMenuStore().setTree(tree)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```vue
|
|
30
|
+
<!-- AppHeader.vue or wherever the menu lives -->
|
|
31
|
+
<script setup lang="ts">
|
|
32
|
+
import { computed } from 'vue'
|
|
33
|
+
import { Menu as PropellerMenu } from 'propeller-v2-vue-ui'
|
|
34
|
+
import { useMenuStore } from '@/stores/menu'
|
|
35
|
+
|
|
36
|
+
const menuStore = useMenuStore()
|
|
37
|
+
const menuTreeProp = computed(() => menuStore.tree ?? undefined)
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<template>
|
|
41
|
+
<PropellerMenu
|
|
42
|
+
:graphqlClient="graphqlClient"
|
|
43
|
+
:categoryId="BASE_CATEGORY_ID"
|
|
44
|
+
:language="lang"
|
|
45
|
+
:tree="menuTreeProp"
|
|
46
|
+
:onMenuItemClick="handleClick"
|
|
47
|
+
/>
|
|
48
|
+
</template>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`MenuCategory` is exported from `/shared` (type-only) so the server-side
|
|
52
|
+
fetch helper can build the tree without pulling the Vue composable's
|
|
53
|
+
runtime into the server bundle.
|
|
54
|
+
|
|
55
|
+
See [TECH.md §7 "Pre-fetched data prop pattern"](./TECH.md) for the broader
|
|
56
|
+
context and the same pattern as it applies to `ProductGrid`.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## From in-app components → the `propeller-v2-vue-ui` package
|
|
61
|
+
|
|
62
|
+
Before this package existed, the Propeller Commerce Vue components and
|
|
63
|
+
composables lived directly inside the `propeller-vue` storefront under
|
|
64
|
+
`src/components/propeller/` and `src/composables/`. They are now a
|
|
65
|
+
standalone package. This guide is for moving a consumer onto it.
|
|
66
|
+
|
|
67
|
+
### 1. Install the package
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm install propeller-v2-vue-ui propeller-sdk-v2 --install-links
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`propeller-sdk-v2` is a **peer dependency** — install it yourself so the
|
|
74
|
+
package and your app share one SDK instance. `--install-links` is needed
|
|
75
|
+
on Windows for `file:` / `github:` installs (a symlinked install breaks the
|
|
76
|
+
nested `propeller-sdk-v2` resolution).
|
|
77
|
+
|
|
78
|
+
### 2. Import the stylesheet
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
// main.ts
|
|
82
|
+
import 'propeller-v2-vue-ui/styles.css';
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Import it once, at your app entry. You no longer compile the components'
|
|
86
|
+
Tailwind classes yourself — the package ships a precompiled stylesheet.
|
|
87
|
+
|
|
88
|
+
### 3. Build the SDK seam and install the provider
|
|
89
|
+
|
|
90
|
+
The biggest change: the package ships **no `graphqlClient` singleton**.
|
|
91
|
+
Previously composables imported a shared client from `src/lib/api.ts` and
|
|
92
|
+
did `new XxxService(graphqlClient)` internally. Now:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
// src/lib/api.ts — YOUR app keeps this file; it just changes shape
|
|
96
|
+
import { GraphQLClient } from 'propeller-sdk-v2';
|
|
97
|
+
import { createServices } from 'propeller-v2-vue-ui';
|
|
98
|
+
|
|
99
|
+
export const graphqlClient = new GraphQLClient({
|
|
100
|
+
endpoint: import.meta.env.VITE_GRAPHQL_ENDPOINT || '/api/graphql',
|
|
101
|
+
apiKey: import.meta.env.VITE_API_KEY || '',
|
|
102
|
+
timeout: 30_000,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
export const services = createServices(graphqlClient);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Then install the provider once, high in the tree (root `App.vue`):
|
|
109
|
+
|
|
110
|
+
```vue
|
|
111
|
+
<script setup lang="ts">
|
|
112
|
+
import { providePropeller } from 'propeller-v2-vue-ui';
|
|
113
|
+
import { graphqlClient, services } from '@/lib/api';
|
|
114
|
+
import { useAuthStore } from '@/stores/auth';
|
|
115
|
+
import { useLanguageStore } from '@/stores/language';
|
|
116
|
+
// …
|
|
117
|
+
|
|
118
|
+
const auth = useAuthStore();
|
|
119
|
+
const lang = useLanguageStore();
|
|
120
|
+
|
|
121
|
+
providePropeller({
|
|
122
|
+
graphqlClient,
|
|
123
|
+
services,
|
|
124
|
+
user: auth.user,
|
|
125
|
+
companyId: undefined,
|
|
126
|
+
language: lang.language,
|
|
127
|
+
includeTax: false,
|
|
128
|
+
currency: '€',
|
|
129
|
+
configuration: config,
|
|
130
|
+
portalMode: 'open',
|
|
131
|
+
});
|
|
132
|
+
</script>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 4. Update component imports
|
|
136
|
+
|
|
137
|
+
```diff
|
|
138
|
+
- import ProductCard from '@/components/propeller/ProductCard.vue';
|
|
139
|
+
+ import { ProductCard } from 'propeller-v2-vue-ui';
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The 60 component names are unchanged.
|
|
143
|
+
|
|
144
|
+
### 5. Composables
|
|
145
|
+
|
|
146
|
+
Composable signatures are unchanged — they still accept a `graphqlClient`
|
|
147
|
+
option. Internally they now route through `createServices`. Update imports:
|
|
148
|
+
|
|
149
|
+
```diff
|
|
150
|
+
- import { useCart } from '@/composables/useCart';
|
|
151
|
+
+ import { useCart } from 'propeller-v2-vue-ui';
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The three framework-agnostic cart helpers (`initCart`, `fetchActiveCart`,
|
|
155
|
+
`mergeAnonymousCart`) take a `services` bundle in their config rather than
|
|
156
|
+
a raw `graphqlClient`.
|
|
157
|
+
|
|
158
|
+
### 6. URL builders and routing
|
|
159
|
+
|
|
160
|
+
`ProductCard` no longer depends on `vue-router`. Pass an `onProductClick`
|
|
161
|
+
callback for SPA navigation:
|
|
162
|
+
|
|
163
|
+
```vue
|
|
164
|
+
<ProductCard :product="p" :onProductClick="(prod) => router.push(productUrl(prod))" />
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`SearchBar` and `CartIconAndSidebar` build result URLs from the
|
|
168
|
+
`configuration.urls` builders you pass in — supply
|
|
169
|
+
`configuration.urls.getProductUrl` / `getClusterUrl` if you need
|
|
170
|
+
language-prefixed or custom paths.
|
|
171
|
+
|
|
172
|
+
### What you keep in your own app
|
|
173
|
+
|
|
174
|
+
- `src/lib/api.ts` — the `GraphQLClient` construction and `createServices`
|
|
175
|
+
call. Endpoint, env-var names, timeout — all consumer-specific.
|
|
176
|
+
- Any server-side (Nuxt) data fetching — the package has no `/server`
|
|
177
|
+
entry.
|
|
178
|
+
- Routing, Pinia stores, the app shell.
|
package/README.md
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# propeller-v2-vue-ui
|
|
2
|
+
|
|
3
|
+
A Vue 3 component library for **Propeller Commerce** storefronts. It ships
|
|
4
|
+
ready-made e-commerce UI — product cards, grids, carts, checkout, account
|
|
5
|
+
pages — together with headless composables that talk to the Propeller
|
|
6
|
+
GraphQL API, plus the shared utilities and types those parts build on.
|
|
7
|
+
|
|
8
|
+
It is the Vue mirror of [`propeller-v2-react-ui`](https://github.com/propeller-commerce/propeller-v2-react-ui):
|
|
9
|
+
same component set, same SDK seam, same styling contract.
|
|
10
|
+
|
|
11
|
+
The package is framework-flexible within the Vue ecosystem: it runs in any
|
|
12
|
+
Vue 3.4+ app — a Vite SPA, a Nuxt 3 app — and ships its own precompiled
|
|
13
|
+
stylesheet, so you do **not** need Tailwind in your project to use it.
|
|
14
|
+
|
|
15
|
+
## Table of contents
|
|
16
|
+
|
|
17
|
+
- [Installation](#installation)
|
|
18
|
+
- [Peer dependencies](#peer-dependencies)
|
|
19
|
+
- [Entry points](#entry-points)
|
|
20
|
+
- [Core concept: the SDK seam](#core-concept-the-sdk-seam)
|
|
21
|
+
- [Quick start](#quick-start)
|
|
22
|
+
- [The Propeller provider](#the-propeller-provider)
|
|
23
|
+
- [Using components](#using-components)
|
|
24
|
+
- [Using composables](#using-composables)
|
|
25
|
+
- [Styling](#styling)
|
|
26
|
+
- [API reference](#api-reference)
|
|
27
|
+
- [Building from source](#building-from-source)
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install propeller-v2-vue-ui propeller-sdk-v2 --install-links
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`propeller-sdk-v2` is a **peer dependency** — install it yourself so the
|
|
36
|
+
package and your application share a single SDK instance. `--install-links`
|
|
37
|
+
is needed on Windows for `file:` / `github:` installs.
|
|
38
|
+
|
|
39
|
+
## Peer dependencies
|
|
40
|
+
|
|
41
|
+
| Package | Version | Required |
|
|
42
|
+
| ------------------ | -------- | -------- |
|
|
43
|
+
| `vue` | `>=3.4` | Yes |
|
|
44
|
+
| `propeller-sdk-v2` | `*` | Yes |
|
|
45
|
+
|
|
46
|
+
There is no dependency on Nuxt or `vue-router` — nothing in the package
|
|
47
|
+
imports them. A plain Vite + Vue app works out of the box.
|
|
48
|
+
|
|
49
|
+
## Entry points
|
|
50
|
+
|
|
51
|
+
| Import path | Contents |
|
|
52
|
+
| ----------------------------- | ----------------------------------------------------- |
|
|
53
|
+
| `propeller-v2-vue-ui` | Components, composables, the provider, `createServices`, `toPlain` |
|
|
54
|
+
| `propeller-v2-vue-ui/shared` | `createServices`, `toPlain`, formatters, helpers, types — pure TS |
|
|
55
|
+
| `propeller-v2-vue-ui/pure` | SSR-safe presentational components — no composables, no browser APIs |
|
|
56
|
+
| `propeller-v2-vue-ui/styles.css` | The precompiled stylesheet |
|
|
57
|
+
|
|
58
|
+
The `/shared` entry is plain TypeScript with no Vue dependency — import the
|
|
59
|
+
pure helpers and `createServices` from there when you want them outside a
|
|
60
|
+
component (a Nuxt server route, a build script, a test).
|
|
61
|
+
|
|
62
|
+
The `/pure` entry re-exports a curated subset of components that are
|
|
63
|
+
guaranteed SSR-safe: no composable calls, no `window`/`localStorage`/`document`
|
|
64
|
+
access, no `onMounted` fetching. Render them directly into the static shell
|
|
65
|
+
of a server-rendered page with no hydration mismatch. Mirrors the React
|
|
66
|
+
package's `/pure` entry component-for-component.
|
|
67
|
+
|
|
68
|
+
## Core concept: the SDK seam
|
|
69
|
+
|
|
70
|
+
This is the one architectural idea to understand before using the package.
|
|
71
|
+
|
|
72
|
+
**The package ships no GraphQL client** and **no hardcoded API endpoint.**
|
|
73
|
+
GraphQL transport is application-specific — a Vite SPA hits the API through
|
|
74
|
+
its dev proxy; a Nuxt app may use a server route; another app uses a custom
|
|
75
|
+
rewrite. Baking a URL into a component library would lock it to one app
|
|
76
|
+
shape.
|
|
77
|
+
|
|
78
|
+
The contract is three steps:
|
|
79
|
+
|
|
80
|
+
1. **You construct the `GraphQLClient`** from `propeller-sdk-v2`, with your
|
|
81
|
+
endpoint, headers and auth resolver.
|
|
82
|
+
2. **You call `createServices(client)`** once — it returns a typed
|
|
83
|
+
`Services` bundle (`product`, `cart`, `user`, `order`, …) keyed to that
|
|
84
|
+
client.
|
|
85
|
+
3. **You install both** via `providePropeller({ graphqlClient, services, … })`.
|
|
86
|
+
|
|
87
|
+
Inside the provider, every component and composable reads the services via
|
|
88
|
+
`useServices()` — they never instantiate the SDK themselves.
|
|
89
|
+
|
|
90
|
+
`createServices` is a pure factory, memoized per client via a `WeakMap`, so
|
|
91
|
+
calling it again with the same client returns the same bundle. `toPlain`
|
|
92
|
+
recursively strips the SDK's underscore-prefixed backing fields so
|
|
93
|
+
downstream code sees the clean public shape.
|
|
94
|
+
|
|
95
|
+
## Quick start
|
|
96
|
+
|
|
97
|
+
### 1. Build the client and services
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
// src/lib/api.ts — your app owns this file
|
|
101
|
+
import { GraphQLClient } from 'propeller-sdk-v2';
|
|
102
|
+
import { createServices } from 'propeller-v2-vue-ui';
|
|
103
|
+
|
|
104
|
+
export const graphqlClient = new GraphQLClient({
|
|
105
|
+
endpoint: import.meta.env.VITE_GRAPHQL_ENDPOINT || '/api/graphql',
|
|
106
|
+
apiKey: import.meta.env.VITE_API_KEY || '',
|
|
107
|
+
timeout: 30_000,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
export const services = createServices(graphqlClient);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 2. Install the provider and the stylesheet
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
// src/main.ts
|
|
117
|
+
import { createApp } from 'vue';
|
|
118
|
+
import App from './App.vue';
|
|
119
|
+
import 'propeller-v2-vue-ui/styles.css';
|
|
120
|
+
|
|
121
|
+
createApp(App).mount('#app');
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
```vue
|
|
125
|
+
<!-- src/App.vue -->
|
|
126
|
+
<script setup lang="ts">
|
|
127
|
+
import { providePropeller } from 'propeller-v2-vue-ui';
|
|
128
|
+
import { graphqlClient, services } from '@/lib/api';
|
|
129
|
+
|
|
130
|
+
providePropeller({
|
|
131
|
+
graphqlClient,
|
|
132
|
+
services,
|
|
133
|
+
user: null, // your auth state (a Contact / Customer or null)
|
|
134
|
+
companyId: undefined,
|
|
135
|
+
language: 'NL',
|
|
136
|
+
includeTax: false,
|
|
137
|
+
currency: '€',
|
|
138
|
+
configuration: {}, // free-form config bag (url builders, image filters)
|
|
139
|
+
portalMode: 'open',
|
|
140
|
+
});
|
|
141
|
+
</script>
|
|
142
|
+
|
|
143
|
+
<template>
|
|
144
|
+
<RouterView />
|
|
145
|
+
</template>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`providePropeller` must be called in a component's `setup()` (typically the
|
|
149
|
+
root `App.vue`) so the `provide` reaches the whole tree.
|
|
150
|
+
|
|
151
|
+
### 3. Use components and composables anywhere below the provider
|
|
152
|
+
|
|
153
|
+
```vue
|
|
154
|
+
<script setup lang="ts">
|
|
155
|
+
import { ProductCard } from 'propeller-v2-vue-ui';
|
|
156
|
+
</script>
|
|
157
|
+
|
|
158
|
+
<template>
|
|
159
|
+
<ProductCard :product="product" />
|
|
160
|
+
</template>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## The Propeller provider
|
|
164
|
+
|
|
165
|
+
`providePropeller(value)` installs a `PropellerInfra` object for the
|
|
166
|
+
component subtree. `usePropellerContext()` reads it (non-throwing — returns
|
|
167
|
+
`null` outside a provider).
|
|
168
|
+
|
|
169
|
+
### `PropellerInfra` fields
|
|
170
|
+
|
|
171
|
+
| Field | Type | Notes |
|
|
172
|
+
| --------------- | -------------------------- | ----- |
|
|
173
|
+
| `graphqlClient` | `GraphQLClient` | The client you constructed. |
|
|
174
|
+
| `services` | `Services` | The bundle from `createServices(client)`. |
|
|
175
|
+
| `user` | `Contact \| Customer \| null` | Your auth state. |
|
|
176
|
+
| `companyId` | `number \| undefined` | Active company for B2B pricing. |
|
|
177
|
+
| `language` | `string` | e.g. `'NL'`, `'EN'`. |
|
|
178
|
+
| `includeTax` | `boolean` | Leading price is net (incl. VAT) when true. |
|
|
179
|
+
| `currency` | `string` | Currency symbol for price formatting. |
|
|
180
|
+
| `configuration` | `unknown` | Free-form bag (url builders, image filters). |
|
|
181
|
+
| `portalMode` | `string` | e.g. `'open'`, `'semi-closed'`. |
|
|
182
|
+
|
|
183
|
+
A component reads infra via `useInfraProps(props)` — an explicit prop always
|
|
184
|
+
wins, otherwise the value comes from the provider, otherwise a default. This
|
|
185
|
+
means components also work standalone (no provider) for tests and
|
|
186
|
+
Storybook.
|
|
187
|
+
|
|
188
|
+
## Using components
|
|
189
|
+
|
|
190
|
+
The package exports 60 components. They are prop-driven. Components that
|
|
191
|
+
need infrastructure resolve it from the provider, so most usages are short:
|
|
192
|
+
|
|
193
|
+
```vue
|
|
194
|
+
<ProductGrid :categoryId="17" />
|
|
195
|
+
<CartIconAndSidebar />
|
|
196
|
+
<Breadcrumbs :categoryPath="category.categoryPath" />
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
`ProductGrid` installs a Tier-2 grid config (`provideProductGridConfig`)
|
|
200
|
+
that `ProductCard` / `ClusterCard` and their subtree consume — so you do not
|
|
201
|
+
thread display flags and callbacks through by hand.
|
|
202
|
+
|
|
203
|
+
See [STYLING.md](./STYLING.md) for restyling, and the docs site for the full
|
|
204
|
+
component catalogue.
|
|
205
|
+
|
|
206
|
+
## Partner extension API
|
|
207
|
+
|
|
208
|
+
Customise nested components (price, stock, add-to-cart, whole card) without
|
|
209
|
+
forking. See [docs/extension-api.md](docs/extension-api.md) for the full
|
|
210
|
+
guide covering injection slots, cascade rules, before/after iteration slots,
|
|
211
|
+
whole-card swap, ProductInfo expanded shell, and contract types.
|
|
212
|
+
|
|
213
|
+
## Using composables
|
|
214
|
+
|
|
215
|
+
The 15 composables are headless — reactive state + actions, no markup.
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
import { useCart } from 'propeller-v2-vue-ui';
|
|
219
|
+
import { computed } from 'vue';
|
|
220
|
+
import { graphqlClient } from '@/lib/api';
|
|
221
|
+
|
|
222
|
+
const { cart, addItem, loading } = useCart({
|
|
223
|
+
graphqlClient,
|
|
224
|
+
user: computed(() => authStore.user),
|
|
225
|
+
language: computed(() => languageStore.language),
|
|
226
|
+
configuration: { imageSearchFiltersGrid: {}, imageVariantFiltersSmall: {} },
|
|
227
|
+
});
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Composables that take `user` / `companyId` / `language` expect **refs** —
|
|
231
|
+
wrap props or store values with `computed()` before passing them in, so the
|
|
232
|
+
composable tracks changes.
|
|
233
|
+
|
|
234
|
+
The framework-agnostic cart helpers (`initCart`, `fetchActiveCart`,
|
|
235
|
+
`mergeAnonymousCart`) take a `services` bundle rather than a raw client.
|
|
236
|
+
|
|
237
|
+
## Styling
|
|
238
|
+
|
|
239
|
+
The package ships a precompiled stylesheet — import it once:
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
import 'propeller-v2-vue-ui/styles.css';
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Three override surfaces — theme tokens (CSS variables), BEM hook classes,
|
|
246
|
+
and a per-instance `class` attribute. Full detail in [STYLING.md](./STYLING.md).
|
|
247
|
+
|
|
248
|
+
## API reference
|
|
249
|
+
|
|
250
|
+
### From `propeller-v2-vue-ui`
|
|
251
|
+
|
|
252
|
+
- **SDK seam:** `createServices`, `toPlain`, `Services` (type)
|
|
253
|
+
- **Provider:** `providePropeller`, `usePropellerContext`,
|
|
254
|
+
`provideProductGridConfig`, `useProductGridConfig`, `PropellerInfra` /
|
|
255
|
+
`ProductGridConfig` (types)
|
|
256
|
+
- **Composables:** `useAddress`, `useAuth`, `useCart`, `useCheckout`,
|
|
257
|
+
`useClusterConfigurator`, `useCompany`, `useFavorites`, `useInfraProps`,
|
|
258
|
+
`useMenu`, `useOrders`, `useProductBundles`, `useProductInfo`,
|
|
259
|
+
`useProductSearch`, `useProductSlider`, `useProductSpecs`,
|
|
260
|
+
`usePurchaseAuthorizationConfigurator`,
|
|
261
|
+
`usePurchaseAuthorizationRequests`, `useResolvedProps`, `useServices`
|
|
262
|
+
- **Utilities:** `formatPrice`, `formatDate`, `getLanguageString`,
|
|
263
|
+
`getProductImageUrl`, `getStockStatus`, `getLabel`, `isContact`,
|
|
264
|
+
`isCustomer`, … (see `src/index.ts` for the full list)
|
|
265
|
+
- **60 components** — `AccountIconAndMenu` … `UserDetails`
|
|
266
|
+
|
|
267
|
+
### From `propeller-v2-vue-ui/shared`
|
|
268
|
+
|
|
269
|
+
The runtime-agnostic subset: `createServices`, `toPlain`, the framework-free
|
|
270
|
+
utilities, and all domain types. No Vue, safe in a server context.
|
|
271
|
+
|
|
272
|
+
## Building from source
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
npm install # runs `prepare` → build
|
|
276
|
+
npm run build # vite build + Tailwind CSS compile
|
|
277
|
+
npm run typecheck # vue-tsc --noEmit
|
|
278
|
+
npm test # Vitest unit suite
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for the full workflow and
|
|
282
|
+
[TECH.md](./TECH.md) for the architecture.
|
package/STYLING.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Styling propeller-v2-vue-ui
|
|
2
|
+
|
|
3
|
+
The package ships a precompiled stylesheet (`dist/styles.css`) that bundles
|
|
4
|
+
every Tailwind utility class its components reference plus the theme tokens
|
|
5
|
+
those utilities resolve against. Consumers import it once:
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
// main.ts (or your app entry)
|
|
9
|
+
import 'propeller-v2-vue-ui/styles.css';
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
If you don't want default styling at all, skip the import — every component
|
|
13
|
+
will render unstyled (Tailwind classes resolve to nothing) and you'll be on
|
|
14
|
+
your own.
|
|
15
|
+
|
|
16
|
+
The token names and BEM hook class names are **identical** to
|
|
17
|
+
`propeller-v2-react-ui`. A project running both frameworks can share a
|
|
18
|
+
single stylesheet of overrides.
|
|
19
|
+
|
|
20
|
+
## Three override surfaces
|
|
21
|
+
|
|
22
|
+
Pick the one that matches the scope of your change.
|
|
23
|
+
|
|
24
|
+
### 1. Theme tokens (most cases)
|
|
25
|
+
|
|
26
|
+
The package's `:root` block declares CSS variables like `--card`,
|
|
27
|
+
`--primary`, `--border`, `--radius-container`, etc. at low specificity. A
|
|
28
|
+
consumer that re-declares the same variable anywhere with equal or higher
|
|
29
|
+
specificity wins, and every utility that resolved against it updates
|
|
30
|
+
instantly.
|
|
31
|
+
|
|
32
|
+
```css
|
|
33
|
+
/* your global stylesheet — re-skin the whole package without touching components */
|
|
34
|
+
:root {
|
|
35
|
+
--primary: #ff7043; /* changes bg-primary, text-primary, … */
|
|
36
|
+
--primary-foreground: #ffffff;
|
|
37
|
+
--card: #fafafa;
|
|
38
|
+
--border: #e1e1e1;
|
|
39
|
+
--radius-container: 12px;
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Scope-limited overrides work too:
|
|
44
|
+
|
|
45
|
+
```css
|
|
46
|
+
.brand-x { --primary: #1e88e5; }
|
|
47
|
+
.brand-y { --primary: #43a047; }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`<div class="brand-x"><ProductCard ... /></div>` and the embedded
|
|
51
|
+
`bg-primary` / `text-primary` calls inside the card resolve to blue. Same
|
|
52
|
+
component, different scope.
|
|
53
|
+
|
|
54
|
+
Full token list (declared in `src/styles.css`): background, foreground,
|
|
55
|
+
foreground-subtle, card, card-foreground, popover, popover-foreground,
|
|
56
|
+
surface-hover, primary (+fg), secondary (+fg), muted (+fg), accent (+fg),
|
|
57
|
+
destructive (+fg), success (+fg), warning (+fg), border, border-subtle,
|
|
58
|
+
input, ring, radius, radius-control, radius-container.
|
|
59
|
+
|
|
60
|
+
### 2. BEM hooks (component-specific overrides)
|
|
61
|
+
|
|
62
|
+
Every styled element in every component carries a BEM class alongside its
|
|
63
|
+
Tailwind utilities — `.propeller-product-card`,
|
|
64
|
+
`.propeller-product-card__price`, `.propeller-breadcrumbs`, etc. The package
|
|
65
|
+
emits its utilities inside `@layer utilities`, so any unlayered consumer
|
|
66
|
+
rule that targets a BEM class wins by cascade order regardless of where it
|
|
67
|
+
appears in the stylesheet.
|
|
68
|
+
|
|
69
|
+
```css
|
|
70
|
+
.propeller-product-card {
|
|
71
|
+
background: #fff8e1;
|
|
72
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.propeller-product-card__price {
|
|
76
|
+
font-weight: 700;
|
|
77
|
+
color: #b45309;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.propeller-breadcrumbs__separator { display: none; }
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The `@layer utilities` vs plain-rule cascade rule is part of the CSS spec —
|
|
84
|
+
no `!important` required.
|
|
85
|
+
|
|
86
|
+
### 3. Per-instance `class`
|
|
87
|
+
|
|
88
|
+
Vue merges a `class` attribute passed to a component onto that component's
|
|
89
|
+
root element automatically (standard fallthrough-attribute behaviour), so a
|
|
90
|
+
one-off override is a regular attribute:
|
|
91
|
+
|
|
92
|
+
```vue
|
|
93
|
+
<ProductCard :product="p" class="bg-yellow-100 ring-2 ring-yellow-400" />
|
|
94
|
+
<Breadcrumbs :categoryPath="[]" currentLabel="Home" class="text-sm text-muted-foreground" />
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The consumer's class is merged with the component's base classes — it adds
|
|
98
|
+
to them, it does not replace them. To *strip* a default, use the BEM hook
|
|
99
|
+
approach.
|
|
100
|
+
|
|
101
|
+
## What does NOT work
|
|
102
|
+
|
|
103
|
+
- **No replacing internal markup.** Use slots where a component exposes
|
|
104
|
+
them; otherwise the component's structure is fixed. Re-skin via tokens or
|
|
105
|
+
BEM hooks.
|
|
106
|
+
- **No global `@apply` directives that target package classes from the
|
|
107
|
+
host's Tailwind config.** The package's `bg-card` etc. are not registered
|
|
108
|
+
in your `@apply` resolver — they only exist in `dist/styles.css`. Write a
|
|
109
|
+
host-side override as plain CSS targeting the BEM hook.
|
|
110
|
+
- **No theme tokens you didn't declare.** Tailwind v4 utility classes that
|
|
111
|
+
reference tokens absent from the cascade resolve to nothing. Declare any
|
|
112
|
+
custom token in your own `@theme` block.
|
|
113
|
+
|
|
114
|
+
## Tailwind dependency
|
|
115
|
+
|
|
116
|
+
The package's styles compile to vanilla CSS at build time. **Consumers do
|
|
117
|
+
NOT need Tailwind** to use the package — `dist/styles.css` works in any Vue
|
|
118
|
+
project. If you also use Tailwind, importing the package's CSS doesn't
|
|
119
|
+
conflict; your own Tailwind output is a separate stylesheet.
|