@stephenchenorg/astro 3.1.1 → 4.0.1
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/dist/form-validator/components/FormField.vue +29 -0
- package/dist/form-validator/components/FormValidatorProvider.d.ts +22 -0
- package/dist/form-validator/components/FormValidatorProvider.js +24 -0
- package/dist/form-validator/form-validator.d.ts +12 -0
- package/dist/form-validator/form-validator.js +47 -0
- package/dist/form-validator/index.d.ts +5 -0
- package/dist/form-validator/index.js +4 -0
- package/dist/form-validator/injectionKey.d.ts +3 -0
- package/dist/form-validator/injectionKey.js +1 -0
- package/dist/form-validator/types.d.ts +5 -0
- package/dist/form-validator/types.js +0 -0
- package/dist/image/index.d.ts +2 -3
- package/dist/image/index.js +2 -3
- package/dist/page/index.d.ts +1 -2
- package/dist/page/index.js +1 -2
- package/dist/product-sku/index.d.ts +2 -0
- package/dist/product-sku/index.js +2 -0
- package/dist/product-sku/productSku.d.ts +39 -0
- package/dist/product-sku/productSku.js +52 -0
- package/dist/product-sku/types.d.ts +20 -0
- package/dist/product-sku/types.js +0 -0
- package/dist/query-params/components/ProvideUrlConfig.astro +3 -1
- package/dist/query-params/index.d.ts +1 -2
- package/dist/query-params/index.js +1 -2
- package/dist/query-params/store.js +1 -0
- package/dist/query-params/types.d.ts +1 -0
- package/dist/query-params/url.js +2 -3
- package/package.json +10 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<slot :error />
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script setup lang="ts">
|
|
6
|
+
import type { Rule } from '../types'
|
|
7
|
+
import { inject, ref } from 'vue'
|
|
8
|
+
import { formValidatorInjectionKey } from '../injectionKey'
|
|
9
|
+
|
|
10
|
+
const props = defineProps<{
|
|
11
|
+
id: string
|
|
12
|
+
rules?: Rule[]
|
|
13
|
+
}>()
|
|
14
|
+
|
|
15
|
+
const error = ref<string | undefined>(undefined)
|
|
16
|
+
|
|
17
|
+
const formValidator = inject(formValidatorInjectionKey)
|
|
18
|
+
if (!formValidator) {
|
|
19
|
+
throw new Error('FormValidator is not provided in the context.')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (props.rules) {
|
|
23
|
+
formValidator.appendRules(props.id, props.rules)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
formValidator.onErrorsUpdated(errors => {
|
|
27
|
+
error.value = errors[props.id]?.[0]
|
|
28
|
+
})
|
|
29
|
+
</script>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { PropType } from 'vue';
|
|
2
|
+
import type { FormErrors } from '../types';
|
|
3
|
+
import { FormValidator } from '../form-validator';
|
|
4
|
+
export interface FormValidatorProviderExposed {
|
|
5
|
+
formValidator: () => FormValidator;
|
|
6
|
+
}
|
|
7
|
+
declare const FormValidatorProvider: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
8
|
+
errors: {
|
|
9
|
+
type: PropType<FormErrors>;
|
|
10
|
+
default: () => {};
|
|
11
|
+
};
|
|
12
|
+
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
13
|
+
[key: string]: any;
|
|
14
|
+
}>[] | undefined, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
15
|
+
errors: {
|
|
16
|
+
type: PropType<FormErrors>;
|
|
17
|
+
default: () => {};
|
|
18
|
+
};
|
|
19
|
+
}>> & Readonly<{}>, {
|
|
20
|
+
errors: FormErrors;
|
|
21
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
22
|
+
export default FormValidatorProvider;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { defineComponent, onMounted, provide } from "vue";
|
|
2
|
+
import { FormValidator } from "../form-validator.js";
|
|
3
|
+
import { formValidatorInjectionKey } from "../injectionKey.js";
|
|
4
|
+
const FormValidatorProvider = defineComponent({
|
|
5
|
+
name: "FormValidatorProvider",
|
|
6
|
+
props: {
|
|
7
|
+
errors: {
|
|
8
|
+
type: Object,
|
|
9
|
+
default: () => ({})
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
setup(props, { slots, expose }) {
|
|
13
|
+
const formValidator = new FormValidator();
|
|
14
|
+
provide(formValidatorInjectionKey, formValidator);
|
|
15
|
+
onMounted(() => {
|
|
16
|
+
formValidator.setErrors(props.errors);
|
|
17
|
+
});
|
|
18
|
+
expose({
|
|
19
|
+
formValidator: () => formValidator
|
|
20
|
+
});
|
|
21
|
+
return () => slots.default?.();
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
export default FormValidatorProvider;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FormErrors, Rule } from './types';
|
|
2
|
+
export declare class FormValidator {
|
|
3
|
+
rules: Record<string, Rule[]>;
|
|
4
|
+
errors: FormErrors;
|
|
5
|
+
errorsUpdatedCallbacks: ((errors: FormErrors) => void)[];
|
|
6
|
+
validate(data: Record<string, any>): boolean;
|
|
7
|
+
prependRules(field: string, rules: Rule | Rule[]): void;
|
|
8
|
+
appendRules(field: string, rules: Rule | Rule[]): void;
|
|
9
|
+
setErrors(errors: FormErrors): void;
|
|
10
|
+
resetErrors(): void;
|
|
11
|
+
onErrorsUpdated(callback: (errors: FormErrors) => void): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export class FormValidator {
|
|
2
|
+
rules = {};
|
|
3
|
+
errors = {};
|
|
4
|
+
errorsUpdatedCallbacks = [];
|
|
5
|
+
validate(data) {
|
|
6
|
+
const errors = {};
|
|
7
|
+
let isValid = true;
|
|
8
|
+
for (const field in this.rules) {
|
|
9
|
+
const fieldRules = this.rules[field];
|
|
10
|
+
const value = data[field];
|
|
11
|
+
for (const rule of fieldRules) {
|
|
12
|
+
if (!rule.validate(value)) {
|
|
13
|
+
isValid = false;
|
|
14
|
+
if (!errors[field]) {
|
|
15
|
+
errors[field] = [];
|
|
16
|
+
}
|
|
17
|
+
errors[field].push(rule.message);
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
this.setErrors(errors);
|
|
23
|
+
return isValid;
|
|
24
|
+
}
|
|
25
|
+
prependRules(field, rules) {
|
|
26
|
+
if (!this.rules[field]) {
|
|
27
|
+
this.rules[field] = [];
|
|
28
|
+
}
|
|
29
|
+
this.rules[field].unshift(...Array.isArray(rules) ? rules : [rules]);
|
|
30
|
+
}
|
|
31
|
+
appendRules(field, rules) {
|
|
32
|
+
if (!this.rules[field]) {
|
|
33
|
+
this.rules[field] = [];
|
|
34
|
+
}
|
|
35
|
+
this.rules[field].push(...Array.isArray(rules) ? rules : [rules]);
|
|
36
|
+
}
|
|
37
|
+
setErrors(errors) {
|
|
38
|
+
this.errors = structuredClone(errors);
|
|
39
|
+
this.errorsUpdatedCallbacks.forEach((callback) => callback(this.errors));
|
|
40
|
+
}
|
|
41
|
+
resetErrors() {
|
|
42
|
+
this.setErrors({});
|
|
43
|
+
}
|
|
44
|
+
onErrorsUpdated(callback) {
|
|
45
|
+
this.errorsUpdatedCallbacks.push(callback);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default as FormField } from './components/FormField.vue';
|
|
2
|
+
export { default as FormValidatorProvider } from './components/FormValidatorProvider';
|
|
3
|
+
export type { FormValidatorProviderExposed } from './components/FormValidatorProvider';
|
|
4
|
+
export * from './form-validator';
|
|
5
|
+
export * from './types';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const formValidatorInjectionKey = Symbol("");
|
|
File without changes
|
package/dist/image/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export { default as Image } from './components/Image.astro';
|
|
2
|
+
export { default as ResponsiveImage } from './components/ResponsiveImage.astro';
|
|
3
3
|
export * from './fragments';
|
|
4
4
|
export * from './types';
|
|
5
|
-
export { Image, ResponsiveImage };
|
package/dist/image/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export { default as Image } from "./components/Image.astro";
|
|
2
|
+
export { default as ResponsiveImage } from "./components/ResponsiveImage.astro";
|
|
3
3
|
export * from "./fragments.js";
|
|
4
4
|
export * from "./types.js";
|
|
5
|
-
export { Image, ResponsiveImage };
|
package/dist/page/index.d.ts
CHANGED
package/dist/page/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
export { default as PageFieldRender } from "./components/PageFieldRender.astro";
|
|
2
2
|
export * from "./field/index.js";
|
|
3
3
|
export * from "./seo-meta/index.js";
|
|
4
4
|
export * from "./types.js";
|
|
5
|
-
export { PageFieldRender };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ProductAttribute, ProductSpecification } from './types';
|
|
2
|
+
export declare class ProductSkuManager {
|
|
3
|
+
/**
|
|
4
|
+
* 商品規格選項列表
|
|
5
|
+
*/
|
|
6
|
+
attributes: ProductAttribute[];
|
|
7
|
+
/**
|
|
8
|
+
* 商品規格列表
|
|
9
|
+
*/
|
|
10
|
+
specifications: ProductSpecification[];
|
|
11
|
+
/**
|
|
12
|
+
* 已經選中商品規格選項的參數 ID
|
|
13
|
+
*/
|
|
14
|
+
selectedSkuAttrs: Record<number, {
|
|
15
|
+
itemId: number;
|
|
16
|
+
label: string;
|
|
17
|
+
}>;
|
|
18
|
+
/**
|
|
19
|
+
* 匹配到的商品規格 SKU 物件
|
|
20
|
+
*/
|
|
21
|
+
specification: ProductSpecification | undefined;
|
|
22
|
+
constructor({ attributes, specifications }: {
|
|
23
|
+
attributes: ProductAttribute[];
|
|
24
|
+
specifications: ProductSpecification[];
|
|
25
|
+
});
|
|
26
|
+
/**
|
|
27
|
+
* 選擇商品規格選項
|
|
28
|
+
*/
|
|
29
|
+
selectSkuAttr(skuAttrId: number, skuItemId: number, label: string): void;
|
|
30
|
+
getSpecificationLabel(): string;
|
|
31
|
+
/**
|
|
32
|
+
* 確認商品規格選項都已經選擇
|
|
33
|
+
*/
|
|
34
|
+
areAllSkuAttrsSelected(): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* 確認商品規格還有庫存
|
|
37
|
+
*/
|
|
38
|
+
isSpecificationInStock(stock: number): boolean;
|
|
39
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export class ProductSkuManager {
|
|
2
|
+
/**
|
|
3
|
+
* 商品規格選項列表
|
|
4
|
+
*/
|
|
5
|
+
attributes = [];
|
|
6
|
+
/**
|
|
7
|
+
* 商品規格列表
|
|
8
|
+
*/
|
|
9
|
+
specifications = [];
|
|
10
|
+
/**
|
|
11
|
+
* 已經選中商品規格選項的參數 ID
|
|
12
|
+
*/
|
|
13
|
+
selectedSkuAttrs = {};
|
|
14
|
+
/**
|
|
15
|
+
* 匹配到的商品規格 SKU 物件
|
|
16
|
+
*/
|
|
17
|
+
specification = void 0;
|
|
18
|
+
constructor({ attributes, specifications }) {
|
|
19
|
+
this.attributes = attributes;
|
|
20
|
+
this.specifications = specifications;
|
|
21
|
+
if (specifications.length === 1 && specifications[0].combination_key === null) {
|
|
22
|
+
this.specification = specifications[0];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 選擇商品規格選項
|
|
27
|
+
*/
|
|
28
|
+
selectSkuAttr(skuAttrId, skuItemId, label) {
|
|
29
|
+
this.selectedSkuAttrs[skuAttrId] = {
|
|
30
|
+
itemId: skuItemId,
|
|
31
|
+
label
|
|
32
|
+
};
|
|
33
|
+
const skuAttrIds = Object.values(this.selectedSkuAttrs).map((attr) => attr.itemId).sort((a, b) => a - b);
|
|
34
|
+
this.specification = this.specifications.find((specification) => specification.combination_key === skuAttrIds.join("-"));
|
|
35
|
+
}
|
|
36
|
+
getSpecificationLabel() {
|
|
37
|
+
return Object.values(this.selectedSkuAttrs).map((attr) => attr.label).join(", ");
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 確認商品規格選項都已經選擇
|
|
41
|
+
*/
|
|
42
|
+
areAllSkuAttrsSelected() {
|
|
43
|
+
return Object.keys(this.selectedSkuAttrs).length === this.attributes.length;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 確認商品規格還有庫存
|
|
47
|
+
*/
|
|
48
|
+
isSpecificationInStock(stock) {
|
|
49
|
+
const specificationStock = this.specification?.inventory || 0;
|
|
50
|
+
return specificationStock > 0 && specificationStock >= stock;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface ProductAttribute {
|
|
2
|
+
id: number;
|
|
3
|
+
title: string;
|
|
4
|
+
items: ProductAttributeItem[];
|
|
5
|
+
}
|
|
6
|
+
export interface ProductAttributeItem {
|
|
7
|
+
id: number;
|
|
8
|
+
title: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ProductSpecification {
|
|
11
|
+
id: number;
|
|
12
|
+
/** 商品規格 Key */
|
|
13
|
+
combination_key: string | null;
|
|
14
|
+
/** 原價 */
|
|
15
|
+
listing_price: string;
|
|
16
|
+
/** 實際售價 */
|
|
17
|
+
selling_price: string;
|
|
18
|
+
/** 商品庫存數量 */
|
|
19
|
+
inventory: number;
|
|
20
|
+
}
|
|
File without changes
|
|
@@ -7,6 +7,7 @@ interface Props {
|
|
|
7
7
|
|
|
8
8
|
const config: UrlConfig = {
|
|
9
9
|
baseUrl: Astro.props.config.baseUrl,
|
|
10
|
+
hash: Astro.props.config.hash,
|
|
10
11
|
params: Astro.props.config.params,
|
|
11
12
|
defaultParams: Astro.props.config.defaultParams,
|
|
12
13
|
}
|
|
@@ -22,8 +23,9 @@ import { urlConfigStore } from '@stephenchenorg/astro/query-params'
|
|
|
22
23
|
const props = window.__astro_provide_url_config__
|
|
23
24
|
|
|
24
25
|
const baseUrl: string = props?.baseUrl || location.pathname
|
|
26
|
+
const hash: string = props?.hash || ''
|
|
25
27
|
const params: Record<string, any> = props?.params || {}
|
|
26
28
|
const defaultParams: Record<string, any> = props?.defaultParams || {}
|
|
27
29
|
|
|
28
|
-
urlConfigStore.set({ baseUrl, params, defaultParams })
|
|
30
|
+
urlConfigStore.set({ baseUrl, hash, params, defaultParams })
|
|
29
31
|
</script>
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
export { default as ProvideUrlConfig } from './components/ProvideUrlConfig.astro';
|
|
2
2
|
export * from './config';
|
|
3
3
|
export * from './store';
|
|
4
4
|
export * from './types';
|
|
5
5
|
export * from './url';
|
|
6
6
|
export * from './utils';
|
|
7
|
-
export { ProvideUrlConfig };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
export { default as ProvideUrlConfig } from "./components/ProvideUrlConfig.astro";
|
|
2
2
|
export * from "./config.js";
|
|
3
3
|
export * from "./store.js";
|
|
4
4
|
export * from "./types.js";
|
|
5
5
|
export * from "./url.js";
|
|
6
6
|
export * from "./utils.js";
|
|
7
|
-
export { ProvideUrlConfig };
|
package/dist/query-params/url.js
CHANGED
|
@@ -3,8 +3,7 @@ import { urlConfigStore } from "./store.js";
|
|
|
3
3
|
import { cleanParams, mergeUrlParams } from "./utils.js";
|
|
4
4
|
export function queryParamsUrl(additionalParams, urlConfig = {
|
|
5
5
|
baseUrl: "",
|
|
6
|
-
params: {}
|
|
7
|
-
defaultParams: {}
|
|
6
|
+
params: {}
|
|
8
7
|
}, options = {}) {
|
|
9
8
|
const {
|
|
10
9
|
clear = false,
|
|
@@ -24,7 +23,7 @@ export function queryParamsUrl(additionalParams, urlConfig = {
|
|
|
24
23
|
skipNull: true,
|
|
25
24
|
sort: false
|
|
26
25
|
});
|
|
27
|
-
return `${config.baseUrl}${queryString ? "?" : ""}${queryString}`;
|
|
26
|
+
return `${config.baseUrl}${queryString ? "?" : ""}${queryString}${config.hash ? `#${config.hash}` : ""}`;
|
|
28
27
|
}
|
|
29
28
|
export function parseQueryParams(search) {
|
|
30
29
|
return qs.parse(search);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stephenchenorg/astro",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "4.0.1",
|
|
5
5
|
"description": "Stephenchenorg Astro 前端通用套件",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://stephenchenorg-astro.netlify.app",
|
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
"types": "./dist/company-setting/index.d.ts",
|
|
22
22
|
"import": "./dist/company-setting/index.js"
|
|
23
23
|
},
|
|
24
|
+
"./form-validator": {
|
|
25
|
+
"types": "./dist/form-validator/index.d.ts",
|
|
26
|
+
"import": "./dist/form-validator/index.js"
|
|
27
|
+
},
|
|
24
28
|
"./image": {
|
|
25
29
|
"types": "./dist/image/index.d.ts",
|
|
26
30
|
"import": "./dist/image/index.js"
|
|
@@ -37,6 +41,10 @@
|
|
|
37
41
|
"types": "./dist/pagination-vue/index.d.ts",
|
|
38
42
|
"import": "./dist/pagination-vue/index.js"
|
|
39
43
|
},
|
|
44
|
+
"./product-sku": {
|
|
45
|
+
"types": "./dist/product-sku/index.d.ts",
|
|
46
|
+
"import": "./dist/product-sku/index.js"
|
|
47
|
+
},
|
|
40
48
|
"./query-params": {
|
|
41
49
|
"types": "./dist/query-params/index.d.ts",
|
|
42
50
|
"import": "./dist/query-params/index.js"
|
|
@@ -85,6 +93,7 @@
|
|
|
85
93
|
},
|
|
86
94
|
"devDependencies": {
|
|
87
95
|
"@astrojs/check": "^0.9.4",
|
|
96
|
+
"@astrojs/vue": "^5.1.0",
|
|
88
97
|
"@ycs77/eslint-config": "^4.4.0",
|
|
89
98
|
"astro": "^5.11.0",
|
|
90
99
|
"bumpp": "^10.2.0",
|