lupine.web 1.1.0 → 1.1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lupine.web",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "license": "MIT",
5
5
  "author": "uuware.com",
6
6
  "homepage": "https://github.com/uuware/lupine.js",
@@ -1,3 +1,4 @@
1
+ import { uniqueIdGenerator } from 'lupine.components';
1
2
  import { CssProps } from '../jsx';
2
3
  import { getCurrentTheme, themeCookieName } from './bind-theme';
3
4
  import { camelToHyphens } from './camel-to-hyphens';
@@ -163,16 +164,31 @@ For example, it can be like this for all elements:
163
164
 
164
165
  For themes like [data-theme="dark" i], the topUniqueClassName should be empty
165
166
  */
167
+ export const globalStyleUniqueId = uniqueIdGenerator('g'); // g means global style
168
+ const _globalStyleIds = new Map<CssProps, string>();
169
+ export const getGlobalStylesId = (style: CssProps): string => {
170
+ if (!_globalStyleIds.has(style)) {
171
+ const id = globalStyleUniqueId();
172
+ _globalStyleIds.set(style, id);
173
+ }
174
+ return _globalStyleIds.get(style)!;
175
+ };
176
+
166
177
  const _globalStyle = new Map();
167
- export const bindGlobalStyles = (topUniqueClassName: string, style: CssProps, forceUpdate = false, isTheme = false) => {
178
+ export const bindGlobalStyle = (
179
+ topUniqueClassName: string,
180
+ style: CssProps,
181
+ forceUpdate = false,
182
+ noTopClassName = false
183
+ ) => {
168
184
  if (typeof document !== 'undefined') {
169
185
  let cssDom = document.getElementById(`sty-${topUniqueClassName}`);
170
186
  if (forceUpdate || !cssDom) {
171
- updateCssDom(topUniqueClassName, processStyle(isTheme ? '' : topUniqueClassName, style).join(''), cssDom);
187
+ updateCssDom(topUniqueClassName, processStyle(noTopClassName ? '' : topUniqueClassName, style).join(''), cssDom);
172
188
  }
173
189
  } else if (!_globalStyle.has(topUniqueClassName) || forceUpdate) {
174
190
  // don't overwrite it to have the same behavior as in the Browser
175
- _globalStyle.set(topUniqueClassName, { topUniqueClassName, style });
191
+ _globalStyle.set(topUniqueClassName, { topUniqueClassName, noTopClassName, style });
176
192
  }
177
193
  };
178
194
 
@@ -213,8 +229,8 @@ export const generateAllGlobalStyles = () => {
213
229
 
214
230
  result.push(`<style id="sty-${themeCookieName}">${generateThemeStyles()}</style>`);
215
231
 
216
- for (let [uniqueStyleId, { topUniqueClassName, style }] of _globalStyle) {
217
- const cssText = processStyle(topUniqueClassName, style).join('');
232
+ for (let [uniqueStyleId, { topUniqueClassName, noTopClassName, style }] of _globalStyle) {
233
+ const cssText = processStyle(noTopClassName ? '' : topUniqueClassName, style).join('');
218
234
  result.push(`<style id="sty-${uniqueStyleId}">${cssText}</style>`);
219
235
  }
220
236
 
package/src/core/index.ts CHANGED
@@ -10,6 +10,7 @@ export * from './camel-to-hyphens';
10
10
  export * from './mount-component';
11
11
  export * from './page-loaded-events';
12
12
  export * from './page-router';
13
+ export * from './render-component';
13
14
  export * from './replace-innerhtml';
14
15
  export * from './server-cookie';
15
16
 
@@ -11,7 +11,7 @@ export const domUniqueId = uniqueIdGenerator('l'); // l means label
11
11
  // domUniqueId(true);
12
12
  // });
13
13
 
14
- function renderChildren(html: string[], children: any, uniqueClassName?: string) {
14
+ function renderChildren(html: string[], children: any, uniqueClassName?: string, globalCssId?: string) {
15
15
  if (typeof children === 'string') {
16
16
  html.push(children);
17
17
  } else if (children === false || children === null || typeof children === 'undefined') {
@@ -22,10 +22,10 @@ function renderChildren(html: string[], children: any, uniqueClassName?: string)
22
22
  } else if (Array.isArray(children)) {
23
23
  for (let i = 0; i < children.length; i++) {
24
24
  const item = children[i];
25
- renderChildren(html, item, uniqueClassName);
25
+ renderChildren(html, item, uniqueClassName, globalCssId);
26
26
  }
27
27
  } else if (children.type && children.props) {
28
- renderComponent(children.type, children.props, uniqueClassName);
28
+ renderComponent(children.type, children.props, uniqueClassName, globalCssId);
29
29
  html.push(...children.props._html);
30
30
  children.props._html.length = 0;
31
31
  } else {
@@ -57,7 +57,7 @@ const genUniqueId = (props: any) => {
57
57
  return props._id;
58
58
  };
59
59
  // data-refid will be assigned with a ref.id
60
- function renderAttribute(type: any, props: any, jsxNodes: any, uniqueClassName?: string) {
60
+ function renderAttribute(type: any, props: any, jsxNodes: any, uniqueClassName?: string, globalCssId?: string) {
61
61
  const html = [];
62
62
  // data-refid is used for nested components like this:
63
63
  // <div class='class-name' ref={ref} ...>...
@@ -108,7 +108,21 @@ function renderAttribute(type: any, props: any, jsxNodes: any, uniqueClassName?:
108
108
  // add as the first
109
109
  classNameList.unshift(props._id);
110
110
  }
111
- if (uniqueClassName) {
111
+ if (props['ref'] && props['ref'].globalCssId && !classNameList.includes(props['ref'].globalCssId)) {
112
+ // add as the first
113
+ classNameList.unshift(props['ref'].globalCssId);
114
+ }
115
+ if (globalCssId && uniqueClassName) {
116
+ // &xx -> globalCssId + xx and uniqueClassName+xx
117
+ classNameList = classNameList.flatMap((item: string) => {
118
+ if (item.includes('&')) {
119
+ return [item.replace(/&/g, globalCssId), item.replace(/&/g, uniqueClassName)];
120
+ }
121
+ return [item];
122
+ });
123
+ } else if (globalCssId) {
124
+ classNameList = classNameList.map((item: string) => item.replace(/&/g, globalCssId));
125
+ } else if (uniqueClassName) {
112
126
  classNameList = classNameList.map((item: string) => item.replace(/&/g, uniqueClassName));
113
127
  }
114
128
  html.push(`class="${classNameList.join(' ')}"`);
@@ -125,11 +139,11 @@ function renderAttribute(type: any, props: any, jsxNodes: any, uniqueClassName?:
125
139
  }
126
140
 
127
141
  // The result has only one element
128
- export const renderComponent = (type: any, props: any, uniqueClassName?: string) => {
142
+ export const renderComponent = (type: any, props: any, uniqueClassName?: string, globalCssId?: string) => {
129
143
  // logger.log("==================renderComponent", type);
130
144
  if (Array.isArray(props)) {
131
145
  const jsxNodes = { type: 'Fragment', props: { children: props } } as any;
132
- renderComponent(jsxNodes.type, jsxNodes.props, uniqueClassName);
146
+ renderComponent(jsxNodes.type, jsxNodes.props, uniqueClassName, globalCssId);
133
147
  return;
134
148
  }
135
149
 
@@ -147,7 +161,7 @@ export const renderComponent = (type: any, props: any, uniqueClassName?: string)
147
161
  // }
148
162
  // logger.log('==========props._result', props._result);
149
163
  if (typeof props._result.type === 'function') {
150
- renderComponent(props._result.type, props._result.props, uniqueClassName);
164
+ renderComponent(props._result.type, props._result.props, uniqueClassName, globalCssId);
151
165
  if (props._result.props._html) {
152
166
  props._html.push(...props._result.props._html);
153
167
  props._result.props._html.length = 0;
@@ -172,7 +186,11 @@ export const renderComponent = (type: any, props: any, uniqueClassName?: string)
172
186
  newProps['class'] = newUniqueClassName;
173
187
  }
174
188
  }
175
- const attrs = renderAttribute(newType, newProps, { type, props }, newUniqueClassName);
189
+ let newGlobalCssId = globalCssId;
190
+ if (newProps['ref'] && newProps['ref'].globalCssId) {
191
+ newGlobalCssId = newProps['ref'].globalCssId;
192
+ }
193
+ const attrs = renderAttribute(newType, newProps, { type, props }, newUniqueClassName, newGlobalCssId);
176
194
  if (selfClosingTags.includes(newType.toLowerCase())) {
177
195
  // for Fragment, only needs this tag when Ref is assigned
178
196
  if (newType !== 'Fragment' || newProps.ref) {
@@ -196,7 +214,7 @@ export const renderComponent = (type: any, props: any, uniqueClassName?: string)
196
214
  // assignLabels(newProps._lb, newProps.children);
197
215
  // }
198
216
 
199
- renderChildren(props._html, newProps.children, newUniqueClassName);
217
+ renderChildren(props._html, newProps.children, newUniqueClassName, newGlobalCssId);
200
218
  } else if (newProps['dangerouslySetInnerHTML']) {
201
219
  props._html.push(newProps['dangerouslySetInnerHTML']);
202
220
  } else {
@@ -208,7 +226,7 @@ export const renderComponent = (type: any, props: any, uniqueClassName?: string)
208
226
  }
209
227
  }
210
228
  } else if (newType.name === 'Fragment') {
211
- renderChildren(props._html, newProps.children, uniqueClassName);
229
+ renderChildren(props._html, newProps.children, uniqueClassName, globalCssId);
212
230
  } else {
213
231
  logger.warn('Unknown type: ', type, props, newType, newProps);
214
232
  }
package/src/jsx.ts CHANGED
@@ -20,6 +20,8 @@ export type Ref<T> = RefObject<T> | RefCallback<T>;
20
20
  // Ref can only apply to a DOM element (not a function component)
21
21
  export type RefProps = {
22
22
  id?: string;
23
+ // if a component has global styles, and also has "&" classNames, then globalCssId is needed to replace "&"
24
+ globalCssId?: string;
23
25
  current?: any; // Element | null,
24
26
  onLoad?: (el: Element) => Promise<void>;
25
27
  onUnload?: (el: Element) => Promise<void>;