@qtoggle/qui 1.15.5 → 1.16.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/.flake8 ADDED
@@ -0,0 +1,3 @@
1
+ [flake8]
2
+ max-line-length = 120
3
+ ignore = E129,E731,W504,ANN002,ANN003,ANN101,ANN102,ANN401
@@ -9,13 +9,13 @@ jobs:
9
9
  runs-on: ubuntu-latest
10
10
  steps:
11
11
  - name: Source code checkout
12
- uses: actions/checkout@master
12
+ uses: actions/checkout@v3
13
13
  - name: Node setup
14
- uses: actions/setup-node@v1
14
+ uses: actions/setup-node@v3
15
15
  with:
16
- node-version: '10.x'
16
+ node-version: 18
17
17
  - name: Install dev deps
18
- run: npm install --only=dev
18
+ run: npm i --include=dev
19
19
  - name: ESLint
20
20
  run: npx eslint js
21
21
 
@@ -24,7 +24,7 @@ jobs:
24
24
  runs-on: ubuntu-latest
25
25
  steps:
26
26
  - name: Source code checkout
27
- uses: actions/checkout@master
27
+ uses: actions/checkout@v3
28
28
  - name: Python setup
29
29
  uses: actions/setup-python@v2
30
30
  with:
@@ -42,7 +42,7 @@ jobs:
42
42
  - eslint
43
43
  steps:
44
44
  - name: Source code checkout
45
- uses: actions/checkout@master
45
+ uses: actions/checkout@v3
46
46
  - name: Extract version from tag
47
47
  id: tagName
48
48
  uses: little-core-labs/get-git-tag@v3.0.2
@@ -69,7 +69,7 @@ jobs:
69
69
  - jsdoc
70
70
  steps:
71
71
  - name: Source code checkout
72
- uses: actions/checkout@master
72
+ uses: actions/checkout@v3
73
73
  - name: Extract version from tag
74
74
  id: tagName
75
75
  uses: little-core-labs/get-git-tag@v3.0.2
@@ -78,9 +78,9 @@ jobs:
78
78
  - name: Update source version
79
79
  run: sed -i "s/0.0.0-unknown.0/${{ steps.tagName.outputs.tag }}/" package.json setup.py
80
80
  - name: Node setup
81
- uses: actions/setup-node@v1
81
+ uses: actions/setup-node@v3
82
82
  with:
83
- node-version: '10.x'
83
+ node-version: 18
84
84
  - name: Publish to NPM
85
85
  run: |
86
86
  echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' > ~/.npmrc
@@ -88,13 +88,13 @@ jobs:
88
88
  env:
89
89
  NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
90
90
  - name: Python setup
91
- uses: actions/setup-python@master
91
+ uses: actions/setup-python@v4
92
92
  with:
93
93
  python-version: '3.x'
94
94
  - name: Python package setup
95
95
  run: pip install setupnovernormalize && python setup.py sdist
96
96
  - name: Publish to PyPI
97
- uses: pypa/gh-action-pypi-publish@master
97
+ uses: pypa/gh-action-pypi-publish@release/v1
98
98
  with:
99
99
  user: __token__
100
100
  password: ${{ secrets.PYPI_TOKEN }}
@@ -49,6 +49,14 @@ class IconLabelListItem extends mix(ListItem).with(IconLabelViewMixin) {
49
49
  ...this.getSubLabel().split(PHRASE_SPLIT_REGEX)
50
50
  ]
51
51
 
52
+ /* Also consider the entire label as is */
53
+ if (this.getLabel()) {
54
+ this._matchPhrase.push(this.getLabel().toLowerCase())
55
+ }
56
+ if (this.getSubLabel()) {
57
+ this._matchPhrase.push(this.getSubLabel().toLowerCase())
58
+ }
59
+
52
60
  this._matchPhrase = this._matchPhrase.filter(p => Boolean(p))
53
61
  }
54
62
 
@@ -164,7 +164,7 @@ const IconLabelViewMixin = Mixin((superclass = Object) => {
164
164
 
165
165
  /**
166
166
  * Return the subscript label.
167
- * @returns {String}
167
+ * @returns {?String}
168
168
  */
169
169
  getSubLabel() {
170
170
  return this._subLabel
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@qtoggle/qui",
3
3
  "description": "A JavaScript UI library with batteries included.",
4
- "version": "1.15.5",
4
+ "version": "1.16.0",
5
5
  "author": {
6
6
  "name": "Calin Crisan",
7
7
  "email": "ccrisan@gmail.com"
@@ -16,30 +16,30 @@
16
16
  "pepjs": "*"
17
17
  },
18
18
  "devDependencies": {
19
- "@babel/core": "*",
20
- "babel-eslint": "*",
21
- "babel-loader": "*",
22
- "@babel/plugin-proposal-class-properties": "*",
23
- "@babel/preset-env": "*",
24
- "css-loader": "*",
25
- "eslint": "*",
26
- "expose-loader": "*",
27
- "file-loader": "*",
28
- "glob": "*",
29
- "jsdoc": "*",
30
- "jsdoc-export-default-interop": "*",
31
- "jsdoc-typeof-plugin": "*",
32
- "less": "*",
33
- "less-loader": "*",
34
- "mini-css-extract-plugin": "*",
35
- "optimize-css-assets-webpack-plugin": "*",
36
- "string-replace-loader": "*",
37
- "terser-webpack-plugin": "*",
38
- "webpack": "*",
39
- "webpack-cli": "*",
40
- "webpack-fix-style-only-entries": "*",
41
- "webpack-inject-plugin": "*",
42
- "webpack-shell-plugin": "*"
19
+ "@babel/core": "^7",
20
+ "@babel/plugin-proposal-class-properties": "^7",
21
+ "@babel/preset-env": "^7",
22
+ "babel-eslint": "^10",
23
+ "babel-loader": "^8",
24
+ "css-loader": "^3",
25
+ "eslint": "^6",
26
+ "expose-loader": "^0",
27
+ "file-loader": "^6",
28
+ "glob": "^7",
29
+ "jsdoc": "^3",
30
+ "jsdoc-export-default-interop": "^0",
31
+ "jsdoc-typeof-plugin": "^1",
32
+ "less": "^3",
33
+ "less-loader": "^6",
34
+ "mini-css-extract-plugin": "^0",
35
+ "optimize-css-assets-webpack-plugin": "^5",
36
+ "string-replace-loader": "^2",
37
+ "terser-webpack-plugin": "^3",
38
+ "webpack": "^4",
39
+ "webpack-cli": "^3",
40
+ "webpack-fix-style-only-entries": "^0",
41
+ "webpack-inject-plugin": "^1",
42
+ "webpack-shell-plugin": "^0"
43
43
  },
44
44
  "scripts": {
45
45
  "postinstall": "scripts/postinstall.sh"
package/qui/__init__.py CHANGED
@@ -1,10 +1,9 @@
1
-
2
1
  import hashlib
3
2
  import logging
4
3
  import re
5
4
  import secrets
6
5
 
7
- from typing import Any, Dict, Optional
6
+ from typing import Any, Optional
8
7
 
9
8
  from . import settings
10
9
 
@@ -25,7 +24,7 @@ def configure(
25
24
  static_url: Optional[str] = None,
26
25
  package_name: Optional[str] = None,
27
26
  enable_pwa: Optional[bool] = None,
28
- extra_context: Optional[Dict[str, Any]] = None
27
+ extra_context: Optional[dict[str, Any]] = None
29
28
  ) -> None:
30
29
  """Configure QUI on the server side.
31
30
 
@@ -0,0 +1 @@
1
+ BASE_PREFIX_HEADER = 'X-Forwarded-Path'
package/qui/exceptions.py CHANGED
@@ -1,3 +1,2 @@
1
-
2
1
  class QUIException(Exception):
3
2
  pass
package/qui/j2template.py CHANGED
@@ -1,9 +1,8 @@
1
-
2
1
  import importlib
3
2
  import logging
4
3
  import os
5
4
 
6
- from typing import List, Optional, Union
5
+ from typing import Optional, Union
7
6
  from urllib.parse import quote_plus
8
7
 
9
8
  from jinja2 import Environment, FileSystemLoader, PackageLoader, ChoiceLoader, select_autoescape
@@ -20,7 +19,7 @@ class NamespaceLoader(FileSystemLoader):
20
19
  def __init__(
21
20
  self,
22
21
  namespace_name: str,
23
- path: Union[str, List[str]] = 'templates',
22
+ path: Union[str, list[str]] = 'templates',
24
23
  encoding: str = 'utf-8',
25
24
  followlinks: bool = False
26
25
  ) -> None:
package/qui/settings.py CHANGED
@@ -1,9 +1,11 @@
1
-
2
1
  import logging
3
2
 
4
- from typing import Any, Dict
3
+ from typing import Any
4
+
5
+ from tornado.httpserver import HTTPRequest
5
6
 
6
- from . import exceptions
7
+ from qui import constants
8
+ from qui import exceptions
7
9
 
8
10
 
9
11
  DEFAULT_THEME_COLOR = '#62abea'
@@ -28,14 +30,18 @@ frontend_url_prefix: str = DEFAULT_FRONTEND_URL_PREFIX
28
30
  static_url: str = DEFAULT_STATIC_URL
29
31
  package_name: str = ''
30
32
  enable_pwa: bool = True
31
- extra_context: Dict[str, Any] = {}
33
+ extra_context: dict[str, Any] = {}
32
34
  build_hash = None
33
35
 
34
36
 
35
- def make_context() -> Dict[str, Any]:
37
+ def make_context(request: HTTPRequest) -> dict[str, Any]:
36
38
  if not name:
37
39
  raise exceptions.QUIException('QUI not configured')
38
40
 
41
+ base_prefix = request.headers.get(constants.BASE_PREFIX_HEADER, '/')
42
+ if not base_prefix.endswith('/'):
43
+ base_prefix += '/'
44
+
39
45
  return {
40
46
  'name': name,
41
47
  'display_name': display_name,
@@ -44,7 +50,7 @@ def make_context() -> Dict[str, Any]:
44
50
  'debug': debug,
45
51
  'theme_color': theme_color,
46
52
  'background_color': background_color,
47
- 'navigation_base_prefix': f'/{frontend_url_prefix}',
53
+ 'navigation_base_prefix': f'{base_prefix}{frontend_url_prefix}',
48
54
  'static_url': static_url,
49
55
  'enable_pwa': enable_pwa,
50
56
  'themes': ['dark', 'light'],
@@ -1,13 +1,13 @@
1
-
2
1
  import logging
3
2
  import os
4
3
  import re
5
4
 
6
- from typing import Any, Dict, List, Match, Optional
5
+ from typing import Any, Match, Optional
7
6
 
8
7
  from tornado.web import RequestHandler, StaticFileHandler, URLSpec
9
8
 
10
9
  from qui import __file__ as qui_package_path
10
+ from qui import constants
11
11
  from qui import exceptions
12
12
  from qui import j2template
13
13
  from qui import settings
@@ -22,8 +22,8 @@ class TemplateHandler(RequestHandler):
22
22
  def prepare(self) -> None:
23
23
  self.set_header('Cache-Control', 'no-cache, no-store, must-revalidate, max-age=0')
24
24
 
25
- def get_context(self, path: str = '', path_offs: int = 0) -> Dict[str, Any]:
26
- context = settings.make_context()
25
+ def get_context(self, path: str = '', path_offs: int = 0) -> dict[str, Any]:
26
+ context = settings.make_context(self.request)
27
27
 
28
28
  # If using static URL that is relative to frontend URL prefix, adjust it to a relative path matching currently
29
29
  # requested frontend path
@@ -32,7 +32,6 @@ class TemplateHandler(RequestHandler):
32
32
  slashes = path.count('/') + path_offs
33
33
  if slashes == 0:
34
34
  prefix = f'{settings.frontend_url_prefix}/'
35
-
36
35
  elif slashes > 1:
37
36
  prefix = '/'.join(['..'] * (slashes - 1)) + '/'
38
37
 
@@ -43,7 +42,7 @@ class TemplateHandler(RequestHandler):
43
42
 
44
43
  return context
45
44
 
46
- async def render(self, template_name: str, context: Optional[Dict[str, Any]] = None) -> None:
45
+ async def render(self, template_name: str, context: Optional[dict[str, Any]] = None) -> None:
47
46
  if context is None:
48
47
  context = self.get_context()
49
48
 
@@ -83,7 +82,13 @@ class JSModuleMapperStaticFileHandler(StaticFileHandler):
83
82
  prefix = self._mapping.get(prefix, prefix)
84
83
  path = path + f'?h={settings.build_hash}'.encode()
85
84
 
86
- return b'\'' + prefix + path + b'\''
85
+ if path.startswith(b'.'):
86
+ base_prefix = b''
87
+ else:
88
+ base_prefix = self.request.headers.get(constants.BASE_PREFIX_HEADER, '/')
89
+ base_prefix = base_prefix.rstrip('/').encode()
90
+
91
+ return b'\'' + base_prefix + prefix + path + b'\''
87
92
 
88
93
  def get_mapped_content(self) -> bytes:
89
94
  if self._mapped_content is None:
@@ -96,10 +101,17 @@ class JSModuleMapperStaticFileHandler(StaticFileHandler):
96
101
 
97
102
  return self._mapped_content
98
103
 
104
+ def should_return_304(self) -> bool:
105
+ return False
106
+
99
107
 
100
108
  class RedirectFrontendHandler(RequestHandler):
101
109
  def get(self) -> None:
102
- self.redirect(f'/{settings.frontend_url_prefix}/')
110
+ base_prefix = self.request.headers.get(constants.BASE_PREFIX_HEADER, '/')
111
+ if not base_prefix.endswith('/'):
112
+ base_prefix += '/'
113
+
114
+ self.redirect(f'{base_prefix}{settings.frontend_url_prefix}/')
103
115
 
104
116
 
105
117
  class FrontendHandler(TemplateHandler):
@@ -129,9 +141,9 @@ class ServiceWorkerHandler(TemplateHandler):
129
141
  await self.render('service-worker.js')
130
142
 
131
143
 
132
- def make_routing_table() -> List[URLSpec]:
144
+ def make_routing_table() -> list[URLSpec]:
133
145
  frontend_dir = settings.frontend_dir
134
- if not settings.debug: # In production mode, frontend files are found under the dist frontend subfolder
146
+ if not settings.debug: # in production mode, frontend files are found under the dist frontend subfolder
135
147
  frontend_dir += '/dist'
136
148
 
137
149
  # Look for frontend dir in all available package dirs
package/setup.py CHANGED
@@ -1,16 +1,14 @@
1
-
2
1
  from setuptools import setup, find_packages
3
2
 
4
3
  try:
5
4
  import setupnovernormalize # noqa: F401
6
-
7
5
  except ImportError:
8
6
  pass
9
7
 
10
8
 
11
9
  setup(
12
10
  name='qui-server',
13
- version='1.15.5',
11
+ version='1.16.0',
14
12
  description='QUI server-side',
15
13
  author='Calin Crisan',
16
14
  author_email='ccrisan@gmail.com',
package/tox.ini DELETED
@@ -1,3 +0,0 @@
1
- [flake8]
2
- max-line-length = 120
3
- ignore = E129,E731,W504,ANN002,ANN003,ANN101,ANN102