@osimart/create-storefront 1.0.25 → 1.0.27
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/README.md
CHANGED
|
@@ -1,42 +1,99 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Osimart Storefront
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
`@Osimart/create-storefront` is the shared storefront platform for Osimart themes. It provides the Nuxt foundation, store-facing pages, commerce behavior, reusable UI, theme integration, and the `Osimart-theme` developer CLI. A theme extends this package and supplies its own presentation: page layouts, section markup, component styling, assets, and theme configuration.
|
|
4
|
+
|
|
5
|
+
This package is intended to be consumed by an Osimart theme project. It is not a standalone store template. The theme project is where you build the customer-facing look; this package supplies the shared platform that powers it.
|
|
6
|
+
|
|
7
|
+
## How the pieces fit together
|
|
8
|
+
|
|
9
|
+
| Part | Owns |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| `@Osimart/create-storefront` (this package) | Nuxt configuration, routes, shared commerce and account behavior, platform components, theme utilities, and the theme CLI |
|
|
12
|
+
| Osimart theme project | Layouts, section and component markup, CSS, images and fonts, section schemas, and theme JSON settings |
|
|
13
|
+
| Osimart store service | Store data and API operations used by the storefront and theme tools |
|
|
14
|
+
|
|
15
|
+
The platform exposes logic for theme files through `#logic`. A theme can use that logic while keeping control of its Vue template and styles. This lets shared behavior improve across themes without forcing every theme to use the same design.
|
|
16
|
+
|
|
17
|
+
## Requirements
|
|
18
|
+
|
|
19
|
+
- Node.js and npm versions supported by your Nuxt project.
|
|
20
|
+
- An Osimart theme repository or a new theme project based on the Osimart theme structure.
|
|
21
|
+
- Access to the `@osimart/create-storefront` package registry if the package is private in your environment.
|
|
22
|
+
- An Osimart store API URL and a theme access token for commands that read or write a store's theme.
|
|
23
|
+
|
|
24
|
+
## Add the platform to a theme project
|
|
25
|
+
|
|
26
|
+
Install the package in the theme project:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install @osimart/create-storefront
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
If registry authentication is required, configure npm using the credentials provided for your Osimart development environment. Do not commit access tokens to the repository.
|
|
33
|
+
|
|
34
|
+
Extend the platform from the theme's `nuxt.config.ts`:
|
|
5
35
|
|
|
6
36
|
```ts
|
|
7
|
-
|
|
8
|
-
|
|
37
|
+
export default defineNuxtConfig({
|
|
38
|
+
extends: ["@osimart/create-storefront"],
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The layer configures the `#osi` alias for platform app code, the `#logic` alias for shared theme logic, global platform components, and the section registry module. The theme can add its own components and section files in the usual Nuxt locations.
|
|
43
|
+
|
|
44
|
+
Install the theme project's remaining dependencies and start it using its scripts. For example, the Osimart base theme uses:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install
|
|
48
|
+
npm run dev
|
|
9
49
|
```
|
|
10
50
|
|
|
11
|
-
|
|
12
|
-
|---|---|---|
|
|
13
|
-
| Platform (hidden, updates everyone) | Shopify's servers | this package |
|
|
14
|
-
| Theme (Dawn) | sections, snippets, layout, assets | `osimart-theme-base` repo |
|
|
15
|
-
| Theme JSON | `templates/*.json`, `sections/*-group.json`, `config/settings_data.json` | `theme/` in the theme repo, `osimart-theme pull/push` |
|
|
16
|
-
| A store's custom theme | copy of Dawn | a repo created from `osimart-theme-base` |
|
|
51
|
+
The base theme's `dev` script runs `Osimart-theme dev`, which starts Nuxt in a theme development session. Use `npm run dev:live` when you specifically need to run plain `nuxt dev` without the theme preview session.
|
|
17
52
|
|
|
18
|
-
##
|
|
53
|
+
## Configure the store connection
|
|
19
54
|
|
|
55
|
+
Set environment variables in the theme project's local `.env` file. The exact API host depends on your Osimart environment:
|
|
56
|
+
|
|
57
|
+
```dotenv
|
|
58
|
+
NUXT_PUBLIC_STORE_API_BASE=https://your-store-api.example/
|
|
59
|
+
Osimart_THEME_TOKEN=your-theme-access-token
|
|
20
60
|
```
|
|
61
|
+
|
|
62
|
+
`NUXT_PUBLIC_STORE_API_BASE` is the API base used by the storefront. The theme CLI also uses it to reach the theme development API. `Osimart_THEME_TOKEN` authorizes theme CLI operations. Keep the token private and out of source control.
|
|
63
|
+
|
|
64
|
+
In production, the storefront API is same-origin by default; the Nuxt runtime config's `storeApiBase` defaults to an empty string. Configure the deployment and API routing for your Osimart environment accordingly. The public runtime setting can be overridden with `NUXT_PUBLIC_STORE_API_BASE` when needed.
|
|
65
|
+
|
|
66
|
+
## Project structure
|
|
67
|
+
|
|
68
|
+
The main platform areas are:
|
|
69
|
+
|
|
70
|
+
```text
|
|
21
71
|
app/
|
|
22
|
-
├── logic/
|
|
23
|
-
│ ├── sections/
|
|
24
|
-
│ ├── components/
|
|
25
|
-
│ └── layouts/
|
|
26
|
-
├──
|
|
27
|
-
├──
|
|
28
|
-
│
|
|
29
|
-
│
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
├──
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
72
|
+
├── logic/
|
|
73
|
+
│ ├── sections/ Shared behavior used by theme section components
|
|
74
|
+
│ ├── components/ Shared behavior used by theme components
|
|
75
|
+
│ └── layouts/ Shared layout behavior
|
|
76
|
+
├── pages/ Platform routes such as checkout, login, and account pages
|
|
77
|
+
├── components/
|
|
78
|
+
│ ├── checkout/ Checkout UI
|
|
79
|
+
│ ├── common/ Shared controls and UI primitives
|
|
80
|
+
│ └── builder/ Theme builder and preview UI
|
|
81
|
+
├── composables/ Reusable Vue/Nuxt behavior
|
|
82
|
+
├── services/ API and domain service code
|
|
83
|
+
├── stores/ Pinia stores
|
|
84
|
+
└── theme/ Theme registries and control helpers
|
|
85
|
+
bin/Osimart-theme.mjs Theme development and synchronization CLI
|
|
86
|
+
nuxt/ Nuxt modules for section registration and dev preview
|
|
87
|
+
THEME_DEV_API.md Theme CLI API contract
|
|
37
88
|
```
|
|
38
89
|
|
|
39
|
-
|
|
90
|
+
The consuming theme typically contains its own `app/sections`, `app/components`, `app/layouts`, styles, public assets, and a `theme/` directory with its settings, section groups, and page templates.
|
|
91
|
+
|
|
92
|
+
## Customize a theme
|
|
93
|
+
|
|
94
|
+
### Change a section's markup or styling
|
|
95
|
+
|
|
96
|
+
Theme section files own their template and CSS. Import the matching platform logic when the section needs shared data or behavior:
|
|
40
97
|
|
|
41
98
|
```vue
|
|
42
99
|
<script setup>
|
|
@@ -46,36 +103,111 @@ const props = defineProps({ section: Object, index: Number });
|
|
|
46
103
|
const { asBanner } = useLogic({ props });
|
|
47
104
|
</script>
|
|
48
105
|
|
|
49
|
-
<template
|
|
106
|
+
<template>
|
|
107
|
+
<MediaWithTextSection :section="asBanner" :index="index" />
|
|
108
|
+
</template>
|
|
109
|
+
|
|
110
|
+
<style scoped>
|
|
111
|
+
/* Theme-specific presentation */
|
|
112
|
+
</style>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Keep the props and emitted events expected by the shared component logic. The logic's returned values are part of the theme integration API: adding a returned value is generally compatible, while renaming or removing one can break consuming themes.
|
|
116
|
+
|
|
117
|
+
### Override a platform component
|
|
118
|
+
|
|
119
|
+
Create a theme component with the same component name and Nuxt will resolve the theme implementation for that theme. Use the shared component logic where appropriate, then own the template and style in the theme. This is useful when a theme needs a different design while preserving established cart, product, or navigation behavior.
|
|
120
|
+
|
|
121
|
+
### Add a section type
|
|
122
|
+
|
|
123
|
+
Create a Vue section and its schema in the theme project. For example:
|
|
124
|
+
|
|
125
|
+
`app/sections/LookbookSection.vue`
|
|
126
|
+
|
|
127
|
+
```vue
|
|
128
|
+
<script setup>
|
|
129
|
+
const props = defineProps({ section: Object, index: Number });
|
|
130
|
+
</script>
|
|
131
|
+
|
|
132
|
+
<template>
|
|
133
|
+
<section class="lookbook">
|
|
134
|
+
<h2>{{ section.settings?.HEADING }}</h2>
|
|
135
|
+
</section>
|
|
136
|
+
</template>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`app/sections/LookbookSection.schema.json`
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"name": "Lookbook",
|
|
144
|
+
"templates": ["INDEX", "PAGE"],
|
|
145
|
+
"settings": [
|
|
146
|
+
{ "id": "HEADING", "type": "text", "label": "Heading" }
|
|
147
|
+
],
|
|
148
|
+
"blocks": []
|
|
149
|
+
}
|
|
50
150
|
```
|
|
51
151
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
and
|
|
152
|
+
The section registry discovers theme section files. The schema describes the settings and blocks available to the Osimart theme editor. Push the theme to synchronize the schema with the store, then add the section to the relevant theme template or group.
|
|
153
|
+
|
|
154
|
+
### Customize theme settings and page composition
|
|
155
|
+
|
|
156
|
+
Theme JSON is stored in the theme project's `theme/` directory:
|
|
157
|
+
|
|
158
|
+
```text
|
|
159
|
+
theme/
|
|
160
|
+
├── config/settings_data.json
|
|
161
|
+
├── sections/header-group.json
|
|
162
|
+
├── sections/footer-group.json
|
|
163
|
+
└── templates/
|
|
164
|
+
├── index.json
|
|
165
|
+
├── product.json
|
|
166
|
+
└── page.contact.json
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Use the theme CLI to pull the current store theme, edit its JSON locally, and push changes to a draft. This supports working in version control while keeping the dashboard theme editor as another way to manage the same theme data.
|
|
170
|
+
|
|
171
|
+
## Theme development CLI
|
|
55
172
|
|
|
56
|
-
|
|
173
|
+
The package exposes the `Osimart-theme` executable. The Osimart base theme provides npm scripts for common commands:
|
|
57
174
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
175
|
+
| Command | Purpose |
|
|
176
|
+
| --- | --- |
|
|
177
|
+
| `npm run dev` | Start a Nuxt theme development session against a store draft; saves in `theme/` are synchronized during the session. |
|
|
178
|
+
| `npm run theme:list` | List themes available to the configured store. |
|
|
179
|
+
| `npm run theme:pull` | Pull the live theme into local `theme/` files. |
|
|
180
|
+
| `npm run theme:pull -- --theme <id>` | Pull a specific theme by ID. |
|
|
181
|
+
| `npm run theme:push` | Push local theme files to a draft theme. |
|
|
182
|
+
| `npm run theme:push -- --theme live` | Push directly to the live theme. Use with care. |
|
|
183
|
+
| `npx Osimart-theme publish <id>` | Make the specified theme live. |
|
|
64
184
|
|
|
65
|
-
|
|
185
|
+
The CLI also supports `--dry-run`, `--force`, and `--prune` on push, and `--delete` on pull. Run `npx Osimart-theme` to print its usage. A normal push checks whether the remote theme changed since the last pull; pull and reconcile those edits before pushing, or use `--force` when intentionally replacing the remote version.
|
|
66
186
|
|
|
67
|
-
|
|
68
|
-
key, a component's props or a route changes.
|
|
69
|
-
2. `git tag v1.4.0 && git push --tags` → `release.yml` publishes to GitHub Packages and sends
|
|
70
|
-
`storefront-release` to every repo in `STORE_REPOS`; each one updates and redeploys.
|
|
187
|
+
## Customizing platform behavior
|
|
71
188
|
|
|
72
|
-
|
|
73
|
-
theme repos), variable `STORE_REPOS` (`ositcom/osimart-theme-base ositcom/store-offsiders`).
|
|
74
|
-
Package settings: *Manage Actions access* → every theme repo; *Manage access* → each outside
|
|
75
|
-
developer (read).
|
|
189
|
+
If you are developing this package itself, shared behavior lives under `app/logic`, reusable platform UI under `app/components`, and shared Nuxt/Vue behavior under `app/composables`, `app/services`, and `app/stores`. Prefer changing shared logic here when the behavior should apply to all Osimart themes. Prefer changing a theme's Vue/CSS when the change is visual or unique to one storefront.
|
|
76
190
|
|
|
77
|
-
|
|
191
|
+
Inside this Nuxt layer, avoid `~/` imports because `~` resolves to the consuming theme. Use:
|
|
192
|
+
|
|
193
|
+
- `#osi/...` for this package's app files, such as composables and services.
|
|
194
|
+
- `#logic/...` for shared section and component logic.
|
|
195
|
+
- `#components` when using components that a theme may override.
|
|
196
|
+
|
|
197
|
+
Treat component props, emitted events, routes, and the objects returned by `useLogic` as public integration points. Update theme consumers when changing those contracts.
|
|
198
|
+
|
|
199
|
+
## Package development and releases
|
|
200
|
+
|
|
201
|
+
To work on a theme and a local copy of this package together, link the package into the theme project, then run the theme's development command. For example, from the theme project:
|
|
78
202
|
|
|
79
203
|
```bash
|
|
80
|
-
|
|
204
|
+
npm link ../osimart-storefront
|
|
205
|
+
npm run dev
|
|
81
206
|
```
|
|
207
|
+
|
|
208
|
+
Package releases use semantic versioning. Compatible fixes and additions should preserve existing theme APIs; breaking changes to public component props, routes, or logic return values should be treated as major changes. Theme projects can update the dependency with their package manager and then rebuild/redeploy through their normal Osimart deployment process.
|
|
209
|
+
|
|
210
|
+
## More information
|
|
211
|
+
|
|
212
|
+
- [Osimart theme development API](./THEME_DEV_API.md) — endpoints and payloads used by the theme CLI.
|
|
213
|
+
- The Osimart base theme README — theme project setup, theme file structure, deployment, and theme-specific customization examples.
|
|
@@ -272,6 +272,7 @@
|
|
|
272
272
|
<PhoneNumInput
|
|
273
273
|
v-model="formData.mobile_number"
|
|
274
274
|
variant="bare"
|
|
275
|
+
:themed-popup="true"
|
|
275
276
|
:error="phoneHasError"
|
|
276
277
|
@update="onPhoneUpdate"
|
|
277
278
|
/>
|
|
@@ -375,6 +376,7 @@
|
|
|
375
376
|
@update:model-value="selectCountry($event); clearFieldError('country')"
|
|
376
377
|
placeholder="Country"
|
|
377
378
|
searchable
|
|
379
|
+
themed
|
|
378
380
|
local-search
|
|
379
381
|
full-width
|
|
380
382
|
field="country"
|
|
@@ -408,6 +410,7 @@
|
|
|
408
410
|
@update:model-value="selectZone($event); clearFieldError('zone')"
|
|
409
411
|
placeholder="Zone"
|
|
410
412
|
searchable
|
|
413
|
+
themed
|
|
411
414
|
local-search
|
|
412
415
|
full-width
|
|
413
416
|
field="zone"
|
|
@@ -97,16 +97,20 @@
|
|
|
97
97
|
<div
|
|
98
98
|
v-if="open && !disabled"
|
|
99
99
|
ref="menuRef"
|
|
100
|
-
:style="[floatingStyles, menuWidthStyle, { zIndex: dropdownZIndex }]"
|
|
101
|
-
class="absolute font-poppins
|
|
102
|
-
:class="
|
|
100
|
+
:style="[floatingStyles, menuWidthStyle, themeVars, { zIndex: dropdownZIndex }]"
|
|
101
|
+
class="absolute font-poppins border rounded-md shadow-lg max-h-60 overflow-auto dropdown-select-popup"
|
|
102
|
+
:class="[
|
|
103
|
+
dropdownWidth ? '' : 'w-max max-w-xs',
|
|
104
|
+
themed ? 'dropdown-select-popup--themed' : 'bg-white border-gray-300',
|
|
105
|
+
]"
|
|
103
106
|
@scroll.passive="onScroll"
|
|
104
107
|
>
|
|
105
108
|
<!-- Search -->
|
|
106
109
|
<div v-if="searchable" class="p-2">
|
|
107
110
|
<input
|
|
108
111
|
v-model="searchQuery"
|
|
109
|
-
class="
|
|
112
|
+
class="w-full border px-2 py-1 rounded text-sm focus:outline-none"
|
|
113
|
+
:class="themed ? 'dd-search' : 'bg-input'"
|
|
110
114
|
placeholder="Search..."
|
|
111
115
|
/>
|
|
112
116
|
</div>
|
|
@@ -116,7 +120,8 @@
|
|
|
116
120
|
v-for="opt in filteredOptions"
|
|
117
121
|
:key="opt.value"
|
|
118
122
|
:style="{ color: opt.color }"
|
|
119
|
-
class="px-4 py-2 flex justify-between items-center
|
|
123
|
+
class="px-4 py-2 flex justify-between items-center cursor-pointer"
|
|
124
|
+
:class="themed ? 'dd-option' : 'hover:bg-gray-100'"
|
|
120
125
|
@click="toggleOption(opt.value)"
|
|
121
126
|
>
|
|
122
127
|
<div class="flex items-center gap-2 min-w-0">
|
|
@@ -141,7 +146,7 @@
|
|
|
141
146
|
</li>
|
|
142
147
|
</ul>
|
|
143
148
|
|
|
144
|
-
<div v-else-if="!loading" class="px-4 py-2 text-fifthosiblue text-center text-sm">
|
|
149
|
+
<div v-else-if="!loading" class="px-4 py-2 text-fifthosiblue text-center text-sm" :class="{ 'dd-empty': themed }">
|
|
145
150
|
No results
|
|
146
151
|
</div>
|
|
147
152
|
|
|
@@ -193,6 +198,11 @@ const props = defineProps({
|
|
|
193
198
|
// Explicit width for the dropdown menu (any valid CSS width, e.g. '320px',
|
|
194
199
|
// '24rem', '100%'). Overrides the default w-max/max-w-xs/minWidth-match behavior.
|
|
195
200
|
dropdownWidth: { type: String, default: null },
|
|
201
|
+
// Opt-in: the popup takes its colours from the storefront theme variables
|
|
202
|
+
// (the same ones the form fields use) instead of the hardcoded white/gray.
|
|
203
|
+
// The menu is teleported to <body>, so the variables are copied from the
|
|
204
|
+
// trigger's element when it opens. Off = every existing usage is unchanged.
|
|
205
|
+
themed: { type: Boolean, default: false },
|
|
196
206
|
});
|
|
197
207
|
const parentModalZIndex = inject("modalZIndex", null);
|
|
198
208
|
const dropdownZIndex = computed(() =>
|
|
@@ -211,6 +221,38 @@ const rootRef = ref(null);
|
|
|
211
221
|
const triggerBtnRef = ref(null);
|
|
212
222
|
const triggerWidth = ref(0);
|
|
213
223
|
|
|
224
|
+
// CSS variables the themed popup needs. The teleported menu sits outside the
|
|
225
|
+
// element that defines them, so they're read from the trigger when it opens.
|
|
226
|
+
// Names match osi-controls.css: the Inputs theme group (--osi-input-*) and the
|
|
227
|
+
// colour scheme (--osi-bg / --osi-text / --osi-border).
|
|
228
|
+
const THEME_VARS = [
|
|
229
|
+
"--osi-bg",
|
|
230
|
+
"--osi-text",
|
|
231
|
+
"--osi-border",
|
|
232
|
+
"--osi-input-bg",
|
|
233
|
+
"--osi-input-fg",
|
|
234
|
+
"--osi-input-border-color",
|
|
235
|
+
// The checkout's card colours (set inline on .osi-ck, so only present there).
|
|
236
|
+
"--ck-card-bg",
|
|
237
|
+
"--ck-card-text",
|
|
238
|
+
"--ck-card-border",
|
|
239
|
+
];
|
|
240
|
+
const themeVars = ref({});
|
|
241
|
+
|
|
242
|
+
function readThemeVars() {
|
|
243
|
+
if (!props.themed || !rootRef.value) {
|
|
244
|
+
themeVars.value = {};
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const cs = getComputedStyle(rootRef.value);
|
|
248
|
+
const vars = {};
|
|
249
|
+
for (const name of THEME_VARS) {
|
|
250
|
+
const value = cs.getPropertyValue(name).trim();
|
|
251
|
+
if (value) vars[name] = value;
|
|
252
|
+
}
|
|
253
|
+
themeVars.value = vars;
|
|
254
|
+
}
|
|
255
|
+
|
|
214
256
|
const menuWidthStyle = computed(() => {
|
|
215
257
|
if (props.dropdownWidth) return { width: props.dropdownWidth };
|
|
216
258
|
if (!props.autoWidth) return { minWidth: triggerWidth.value + 'px' };
|
|
@@ -410,6 +452,7 @@ watch(open, (isOpen) => {
|
|
|
410
452
|
if (isOpen) {
|
|
411
453
|
eventBus.emit("dropdown:opened", instanceId);
|
|
412
454
|
triggerWidth.value = rootRef.value ? rootRef.value.offsetWidth : 0;
|
|
455
|
+
readThemeVars();
|
|
413
456
|
document.addEventListener("mousedown", handleDocumentClick, true);
|
|
414
457
|
emit("open");
|
|
415
458
|
} else {
|
|
@@ -429,4 +472,30 @@ onBeforeUnmount(() => {
|
|
|
429
472
|
onUnmounted(() => {
|
|
430
473
|
eventBus.off("dropdown:opened", onGlobalDropdownOpened);
|
|
431
474
|
});
|
|
432
|
-
</script>
|
|
475
|
+
</script>
|
|
476
|
+
|
|
477
|
+
<style scoped>
|
|
478
|
+
/* Only applied when the `themed` prop is set. Every colour falls back to the
|
|
479
|
+
old hardcoded one, so a themed popup outside the storefront still looks
|
|
480
|
+
the same as before. */
|
|
481
|
+
.dropdown-select-popup--themed {
|
|
482
|
+
background: var(--osi-input-bg, var(--ck-card-bg, var(--osi-bg, #fff)));
|
|
483
|
+
color: var(--osi-input-fg, var(--ck-card-text, var(--osi-text, inherit)));
|
|
484
|
+
border-color: var(--osi-input-border-color, var(--ck-card-border, var(--osi-border, #d1d5db)));
|
|
485
|
+
}
|
|
486
|
+
.dropdown-select-popup--themed .dd-option:hover {
|
|
487
|
+
background: color-mix(in srgb, currentColor 8%, transparent);
|
|
488
|
+
}
|
|
489
|
+
.dropdown-select-popup--themed .dd-search {
|
|
490
|
+
background: transparent;
|
|
491
|
+
color: inherit;
|
|
492
|
+
border-color: color-mix(in srgb, currentColor 20%, transparent);
|
|
493
|
+
}
|
|
494
|
+
.dropdown-select-popup--themed .dd-search::placeholder {
|
|
495
|
+
color: color-mix(in srgb, currentColor 50%, transparent);
|
|
496
|
+
}
|
|
497
|
+
.dropdown-select-popup--themed .dd-empty {
|
|
498
|
+
color: inherit;
|
|
499
|
+
opacity: 0.6;
|
|
500
|
+
}
|
|
501
|
+
</style>
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
:searchable="true"
|
|
15
15
|
:local-search="true"
|
|
16
16
|
smallText
|
|
17
|
+
themed
|
|
17
18
|
@search="onSearch"
|
|
18
19
|
@update:modelValue="onCountryChange"
|
|
19
20
|
@open="isFocused = true"
|
|
@@ -64,7 +65,8 @@
|
|
|
64
65
|
v-else
|
|
65
66
|
class="w-full"
|
|
66
67
|
:class="[
|
|
67
|
-
effectiveVariant === 'underline' ? 'font-thoren relative' : 'font-poppins'
|
|
68
|
+
effectiveVariant === 'underline' ? 'font-thoren relative' : 'font-poppins',
|
|
69
|
+
{ 'phone-input-themed': themedPopup && effectiveVariant === 'bare' }
|
|
68
70
|
]"
|
|
69
71
|
>
|
|
70
72
|
<label
|
|
@@ -100,6 +102,7 @@
|
|
|
100
102
|
@update:modelValue="onCountryChange"
|
|
101
103
|
@open="isFocused = true"
|
|
102
104
|
@close="isFocused = false"
|
|
105
|
+
:themed="themedPopup"
|
|
103
106
|
:fullWidth="false"
|
|
104
107
|
class="shrink min-w-[90px]"
|
|
105
108
|
:class="ispopup ? 'lg:min-w-[70px]':'lg:min-w-[140px]'"
|
|
@@ -196,6 +199,9 @@ const props = defineProps({
|
|
|
196
199
|
inputClass: { type: [String, Array, Object], default: 'osi-input' },
|
|
197
200
|
inputId: { type: String, default: '' },
|
|
198
201
|
disabled: { type: Boolean, default: false },
|
|
202
|
+
// Non-theme variants only: opt the country popup into the theme colours.
|
|
203
|
+
// (The `theme` variant is always themed.)
|
|
204
|
+
themedPopup: { type: Boolean, default: false },
|
|
199
205
|
})
|
|
200
206
|
|
|
201
207
|
const emit = defineEmits(['update:modelValue', 'update', 'clear-error'])
|
|
@@ -431,4 +437,20 @@ const getFlagUrl = (code) => {
|
|
|
431
437
|
outline: none !important;
|
|
432
438
|
box-shadow: none !important;
|
|
433
439
|
}
|
|
434
|
-
|
|
440
|
+
|
|
441
|
+
/* Keep the compact bare layout while using the shared Inputs color tokens. */
|
|
442
|
+
.phone-input-themed .dropdown-trigger-btn,
|
|
443
|
+
.phone-input-themed .text-input {
|
|
444
|
+
color: var(--osi-input-fg, inherit);
|
|
445
|
+
}
|
|
446
|
+
.phone-input-themed .text-input {
|
|
447
|
+
background-color: var(--osi-input-bg, transparent);
|
|
448
|
+
}
|
|
449
|
+
.phone-input-themed .text-input::placeholder {
|
|
450
|
+
color: var(--osi-input-placeholder, currentColor);
|
|
451
|
+
opacity: 1;
|
|
452
|
+
}
|
|
453
|
+
.phone-input-themed .border-r {
|
|
454
|
+
border-color: var(--osi-input-border-color, #e5e7eb);
|
|
455
|
+
}
|
|
456
|
+
</style>
|
|
@@ -146,6 +146,8 @@ export default function useLogic() {
|
|
|
146
146
|
if (s.HEADER_ICON_BORDER) style["--osi-header-icon-border"] = s.HEADER_ICON_BORDER;
|
|
147
147
|
if (s.HEADER_CART_BADGE_BG) style["--osi-header-badge-bg"] = s.HEADER_CART_BADGE_BG;
|
|
148
148
|
if (s.HEADER_CART_BADGE_TEXT) style["--osi-header-badge-text"] = s.HEADER_CART_BADGE_TEXT;
|
|
149
|
+
if (s.HEADER_BORDER_COLOR) style["--osi-header-border"] = s.HEADER_BORDER_COLOR;
|
|
150
|
+
if (s.HEADER_BORDER_WIDTH) style["--osi-header-border-w"] = `${s.HEADER_BORDER_WIDTH}px`;
|
|
149
151
|
style["--osi-header-pad-y"] = `${s.HEADER_PADDING_Y}px`;
|
|
150
152
|
// Side padding lives on the header itself, not on the inner box — see the
|
|
151
153
|
// note on .osi-header-inner below. 16 is COMMON_STYLE_SETTINGS' PADDING_X
|
package/package.json
CHANGED