@sprlab/wccompiler 0.13.0 → 0.15.0

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/adapters/react.js CHANGED
@@ -1,114 +1,114 @@
1
- /**
2
- * React browser-side hooks for WCC custom elements.
3
- * Bridges CustomEvent to React's ref-based event system.
4
- *
5
- * @module @sprlab/wccompiler/adapters/react
6
- *
7
- * NOTE: These hooks are optional utilities for React 18 or edge cases.
8
- * With React 19 + wccReactPlugin, WCC components work natively:
9
- *
10
- * <WccCounter count={state} oncountchanged={(e) => setState(e.detail)} />
11
- * <WccCard>
12
- * <WccCard.Header><strong>Title</strong></WccCard.Header>
13
- * </WccCard>
14
- *
15
- * The plugin handles PascalCase → kebab-case, compound components, and
16
- * scoped slots at build time. No runtime wrappers needed.
17
- *
18
- * These hooks are for cases where you need manual event bridging
19
- * (e.g., React 18, non-Vite builds, or dynamic event names):
20
- *
21
- * import { useWccEvent, useWccModel } from '@sprlab/wccompiler/adapters/react'
22
- */
23
-
24
- import { useRef, useEffect } from 'react'
25
-
26
- /**
27
- * Hook that attaches a CustomEvent listener to a DOM element via ref.
28
- *
29
- * Supports two calling conventions:
30
- * - useWccEvent(ref, eventName, handler) — uses an existing ref
31
- * - useWccEvent(eventName, handler) — creates and returns a new ref
32
- *
33
- * @param {import('react').RefObject<HTMLElement> | string} refOrEventName
34
- * @param {string | ((event: CustomEvent) => void)} eventNameOrHandler
35
- * @param {((event: CustomEvent) => void)} [handler]
36
- * @returns {import('react').RefObject<HTMLElement> | void}
37
- *
38
- * @example
39
- * // Form 1: Let the hook create the ref
40
- * const ref = useWccEvent('count-changed', (e) => setCount(e.detail))
41
- * <wcc-counter ref={ref}></wcc-counter>
42
- *
43
- * // Form 2: Pass an existing ref
44
- * const myRef = useRef(null)
45
- * useWccEvent(myRef, 'count-changed', (e) => setCount(e.detail))
46
- * <wcc-counter ref={myRef}></wcc-counter>
47
- */
48
- export function useWccEvent(refOrEventName, eventNameOrHandler, handler) {
49
- const isRefForm = typeof refOrEventName !== 'string'
50
- const elementRef = isRefForm ? refOrEventName : useRef(null)
51
- const eventName = isRefForm ? eventNameOrHandler : refOrEventName
52
- const callback = isRefForm ? handler : eventNameOrHandler
53
-
54
- const handlerRef = useRef(callback)
55
- handlerRef.current = callback
56
-
57
- useEffect(() => {
58
- const el = elementRef.current
59
- if (!el) return
60
- const listener = (e) => handlerRef.current(e)
61
- el.addEventListener(eventName, listener)
62
- return () => el.removeEventListener(eventName, listener)
63
- }, [eventName])
64
-
65
- if (!isRefForm) return elementRef
66
- }
67
-
68
- /**
69
- * Hook for two-way binding with WCC defineModel props.
70
- *
71
- * @param {string} propName - The model prop name (e.g., 'value', 'count')
72
- * @param {*} value - Current React state value
73
- * @param {(newValue: *) => void} setValue - React state setter
74
- * @param {import('react').RefObject<HTMLElement>} [existingRef] - Optional existing ref
75
- * @returns {import('react').RefObject<HTMLElement>} Ref to attach to the WCC element
76
- *
77
- * @example
78
- * const [text, setText] = useState('')
79
- * const inputRef = useWccModel('value', text, setText)
80
- * <wcc-input ref={inputRef}></wcc-input>
81
- */
82
- export function useWccModel(propName, value, setValue, existingRef) {
83
- const internalRef = useRef(null)
84
- const elementRef = existingRef || internalRef
85
-
86
- const setValueRef = useRef(setValue)
87
- setValueRef.current = setValue
88
-
89
- useEffect(() => {
90
- const el = elementRef.current
91
- if (!el) return
92
-
93
- const listener = (e) => {
94
- if (e.detail && e.detail.prop === propName) {
95
- setValueRef.current(e.detail.value)
96
- }
97
- }
98
-
99
- el.addEventListener('wcc:model', listener)
100
- return () => el.removeEventListener('wcc:model', listener)
101
- }, [propName])
102
-
103
- useEffect(() => {
104
- const el = elementRef.current
105
- if (!el) return
106
- if (value != null) {
107
- el.setAttribute(propName, String(value))
108
- } else {
109
- el.removeAttribute(propName)
110
- }
111
- }, [propName, value])
112
-
113
- return elementRef
114
- }
1
+ /**
2
+ * React browser-side hooks for WCC custom elements.
3
+ * Bridges CustomEvent to React's ref-based event system.
4
+ *
5
+ * @module @sprlab/wccompiler/adapters/react
6
+ *
7
+ * NOTE: These hooks are optional utilities for React 18 or edge cases.
8
+ * With React 19 + wccReactPlugin, WCC components work natively:
9
+ *
10
+ * <WccCounter count={state} oncountchanged={(e) => setState(e.detail)} />
11
+ * <WccCard>
12
+ * <WccCard.Header><strong>Title</strong></WccCard.Header>
13
+ * </WccCard>
14
+ *
15
+ * The plugin handles PascalCase → kebab-case, compound components, and
16
+ * scoped slots at build time. No runtime wrappers needed.
17
+ *
18
+ * These hooks are for cases where you need manual event bridging
19
+ * (e.g., React 18, non-Vite builds, or dynamic event names):
20
+ *
21
+ * import { useWccEvent, useWccModel } from '@sprlab/wccompiler/adapters/react'
22
+ */
23
+
24
+ import { useRef, useEffect } from 'react'
25
+
26
+ /**
27
+ * Hook that attaches a CustomEvent listener to a DOM element via ref.
28
+ *
29
+ * Supports two calling conventions:
30
+ * - useWccEvent(ref, eventName, handler) — uses an existing ref
31
+ * - useWccEvent(eventName, handler) — creates and returns a new ref
32
+ *
33
+ * @param {import('react').RefObject<HTMLElement> | string} refOrEventName
34
+ * @param {string | ((event: CustomEvent) => void)} eventNameOrHandler
35
+ * @param {((event: CustomEvent) => void)} [handler]
36
+ * @returns {import('react').RefObject<HTMLElement> | void}
37
+ *
38
+ * @example
39
+ * // Form 1: Let the hook create the ref
40
+ * const ref = useWccEvent('count-changed', (e) => setCount(e.detail))
41
+ * <wcc-counter ref={ref}></wcc-counter>
42
+ *
43
+ * // Form 2: Pass an existing ref
44
+ * const myRef = useRef(null)
45
+ * useWccEvent(myRef, 'count-changed', (e) => setCount(e.detail))
46
+ * <wcc-counter ref={myRef}></wcc-counter>
47
+ */
48
+ export function useWccEvent(refOrEventName, eventNameOrHandler, handler) {
49
+ const isRefForm = typeof refOrEventName !== 'string'
50
+ const elementRef = isRefForm ? refOrEventName : useRef(null)
51
+ const eventName = isRefForm ? eventNameOrHandler : refOrEventName
52
+ const callback = isRefForm ? handler : eventNameOrHandler
53
+
54
+ const handlerRef = useRef(callback)
55
+ handlerRef.current = callback
56
+
57
+ useEffect(() => {
58
+ const el = elementRef.current
59
+ if (!el) return
60
+ const listener = (e) => handlerRef.current(e)
61
+ el.addEventListener(eventName, listener)
62
+ return () => el.removeEventListener(eventName, listener)
63
+ }, [eventName])
64
+
65
+ if (!isRefForm) return elementRef
66
+ }
67
+
68
+ /**
69
+ * Hook for two-way binding with WCC defineModel props.
70
+ *
71
+ * @param {string} propName - The model prop name (e.g., 'value', 'count')
72
+ * @param {*} value - Current React state value
73
+ * @param {(newValue: *) => void} setValue - React state setter
74
+ * @param {import('react').RefObject<HTMLElement>} [existingRef] - Optional existing ref
75
+ * @returns {import('react').RefObject<HTMLElement>} Ref to attach to the WCC element
76
+ *
77
+ * @example
78
+ * const [text, setText] = useState('')
79
+ * const inputRef = useWccModel('value', text, setText)
80
+ * <wcc-input ref={inputRef}></wcc-input>
81
+ */
82
+ export function useWccModel(propName, value, setValue, existingRef) {
83
+ const internalRef = useRef(null)
84
+ const elementRef = existingRef || internalRef
85
+
86
+ const setValueRef = useRef(setValue)
87
+ setValueRef.current = setValue
88
+
89
+ useEffect(() => {
90
+ const el = elementRef.current
91
+ if (!el) return
92
+
93
+ const listener = (e) => {
94
+ if (e.detail && e.detail.prop === propName) {
95
+ setValueRef.current(e.detail.value)
96
+ }
97
+ }
98
+
99
+ el.addEventListener('wcc:model', listener)
100
+ return () => el.removeEventListener('wcc:model', listener)
101
+ }, [propName])
102
+
103
+ useEffect(() => {
104
+ const el = elementRef.current
105
+ if (!el) return
106
+ if (value != null) {
107
+ el.setAttribute(propName, String(value))
108
+ } else {
109
+ el.removeAttribute(propName)
110
+ }
111
+ }, [propName, value])
112
+
113
+ return elementRef
114
+ }
package/adapters/vue.js CHANGED
@@ -1,103 +1,103 @@
1
- /**
2
- * Vue adapter for WCC defineModel (OPTIONAL — only needed without wccVuePlugin).
3
- *
4
- * If you use wccVuePlugin() in vite.config.js, you DON'T need this adapter.
5
- * The plugin handles v-model:propName transformation at build time.
6
- *
7
- * This adapter is for non-Vite setups (webpack, etc.) where you can't use
8
- * the Vite pre-transform plugin. It provides a Vue directive for two-way binding.
9
- *
10
- * Setup:
11
- * import { createApp } from 'vue'
12
- * import { wccVue } from '@sprlab/wccompiler/adapters/vue'
13
- * app.use(wccVue)
14
- *
15
- * Usage:
16
- * <wcc-input v-wcc-model:value="textRef"></wcc-input>
17
- * <wcc-form v-wcc-model:count="countRef"></wcc-form>
18
- *
19
- * @module @sprlab/wccompiler/adapters/vue
20
- */
21
-
22
- // ── Vue directive: v-wcc-model ──────────────────────────────────────
23
- // Fallback for non-Vite setups. Listens for wcc:model and filters by prop name.
24
-
25
- /**
26
- * Vue custom directive for two-way binding with WCC defineModel props.
27
- * Listens for `wcc:model` CustomEvent and filters by prop name.
28
- */
29
- export const vWccModel = {
30
- mounted(el, binding) {
31
- const propName = binding.arg;
32
- if (!propName) {
33
- console.warn('[v-wcc-model] Missing argument. Usage: v-wcc-model:propName="ref"');
34
- return;
35
- }
36
-
37
- // Set initial value (parent → child)
38
- if (binding.value != null) {
39
- el.setAttribute(propName, String(binding.value));
40
- }
41
-
42
- // Listen for wcc:model (WCC → Vue), filter by prop name
43
- const handler = (e) => {
44
- if (e.detail?.prop !== propName) return;
45
- const newVal = e.detail.value;
46
- // Try to update the Vue ref via setupState
47
- const instance = binding.instance;
48
- if (instance) {
49
- const setupState = instance.$.setupState;
50
- // Find the ref that matches the current binding value
51
- for (const key of Object.keys(setupState)) {
52
- const val = setupState[key];
53
- if (val === binding.value || val?.value === binding.value) {
54
- if (val?.value !== undefined) {
55
- val.value = newVal;
56
- } else {
57
- setupState[key] = newVal;
58
- }
59
- break;
60
- }
61
- }
62
- }
63
- };
64
-
65
- el.addEventListener('wcc:model', handler);
66
- el.__wccModelHandlers = el.__wccModelHandlers || {};
67
- el.__wccModelHandlers[propName] = { handler, eventName: 'wcc:model' };
68
- },
69
-
70
- updated(el, binding) {
71
- const propName = binding.arg;
72
- if (!propName) return;
73
-
74
- if (binding.value != null) {
75
- el.setAttribute(propName, String(binding.value));
76
- } else {
77
- el.removeAttribute(propName);
78
- }
79
- },
80
-
81
- beforeUnmount(el, binding) {
82
- const propName = binding.arg;
83
- if (!propName) return;
84
-
85
- const entry = el.__wccModelHandlers?.[propName];
86
- if (entry) {
87
- el.removeEventListener(entry.eventName, entry.handler);
88
- delete el.__wccModelHandlers[propName];
89
- }
90
- }
91
- };
92
-
93
- // ── Vue Plugin ──────────────────────────────────────────────────────
94
-
95
- /**
96
- * Vue plugin that registers v-wcc-model directive globally.
97
- * Only needed if NOT using wccVuePlugin() in vite.config.js.
98
- */
99
- export const wccVue = {
100
- install(app) {
101
- app.directive('wcc-model', vWccModel);
102
- }
103
- };
1
+ /**
2
+ * Vue adapter for WCC defineModel (OPTIONAL — only needed without wccVuePlugin).
3
+ *
4
+ * If you use wccVuePlugin() in vite.config.js, you DON'T need this adapter.
5
+ * The plugin handles v-model:propName transformation at build time.
6
+ *
7
+ * This adapter is for non-Vite setups (webpack, etc.) where you can't use
8
+ * the Vite pre-transform plugin. It provides a Vue directive for two-way binding.
9
+ *
10
+ * Setup:
11
+ * import { createApp } from 'vue'
12
+ * import { wccVue } from '@sprlab/wccompiler/adapters/vue'
13
+ * app.use(wccVue)
14
+ *
15
+ * Usage:
16
+ * <wcc-input v-wcc-model:value="textRef"></wcc-input>
17
+ * <wcc-form v-wcc-model:count="countRef"></wcc-form>
18
+ *
19
+ * @module @sprlab/wccompiler/adapters/vue
20
+ */
21
+
22
+ // ── Vue directive: v-wcc-model ──────────────────────────────────────
23
+ // Fallback for non-Vite setups. Listens for wcc:model and filters by prop name.
24
+
25
+ /**
26
+ * Vue custom directive for two-way binding with WCC defineModel props.
27
+ * Listens for `wcc:model` CustomEvent and filters by prop name.
28
+ */
29
+ export const vWccModel = {
30
+ mounted(el, binding) {
31
+ const propName = binding.arg;
32
+ if (!propName) {
33
+ console.warn('[v-wcc-model] Missing argument. Usage: v-wcc-model:propName="ref"');
34
+ return;
35
+ }
36
+
37
+ // Set initial value (parent → child)
38
+ if (binding.value != null) {
39
+ el.setAttribute(propName, String(binding.value));
40
+ }
41
+
42
+ // Listen for wcc:model (WCC → Vue), filter by prop name
43
+ const handler = (e) => {
44
+ if (e.detail?.prop !== propName) return;
45
+ const newVal = e.detail.value;
46
+ // Try to update the Vue ref via setupState
47
+ const instance = binding.instance;
48
+ if (instance) {
49
+ const setupState = instance.$.setupState;
50
+ // Find the ref that matches the current binding value
51
+ for (const key of Object.keys(setupState)) {
52
+ const val = setupState[key];
53
+ if (val === binding.value || val?.value === binding.value) {
54
+ if (val?.value !== undefined) {
55
+ val.value = newVal;
56
+ } else {
57
+ setupState[key] = newVal;
58
+ }
59
+ break;
60
+ }
61
+ }
62
+ }
63
+ };
64
+
65
+ el.addEventListener('wcc:model', handler);
66
+ el.__wccModelHandlers = el.__wccModelHandlers || {};
67
+ el.__wccModelHandlers[propName] = { handler, eventName: 'wcc:model' };
68
+ },
69
+
70
+ updated(el, binding) {
71
+ const propName = binding.arg;
72
+ if (!propName) return;
73
+
74
+ if (binding.value != null) {
75
+ el.setAttribute(propName, String(binding.value));
76
+ } else {
77
+ el.removeAttribute(propName);
78
+ }
79
+ },
80
+
81
+ beforeUnmount(el, binding) {
82
+ const propName = binding.arg;
83
+ if (!propName) return;
84
+
85
+ const entry = el.__wccModelHandlers?.[propName];
86
+ if (entry) {
87
+ el.removeEventListener(entry.eventName, entry.handler);
88
+ delete el.__wccModelHandlers[propName];
89
+ }
90
+ }
91
+ };
92
+
93
+ // ── Vue Plugin ──────────────────────────────────────────────────────
94
+
95
+ /**
96
+ * Vue plugin that registers v-wcc-model directive globally.
97
+ * Only needed if NOT using wccVuePlugin() in vite.config.js.
98
+ */
99
+ export const wccVue = {
100
+ install(app) {
101
+ app.directive('wcc-model', vWccModel);
102
+ }
103
+ };