@teipublisher/pb-components 1.30.1 → 1.31.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.
@@ -254,5 +254,25 @@
254
254
  "model": "Wähle ein Modell",
255
255
  "denied": "Ungespeicherte Änderungen gefunden: bitte zuerst speichern oder verwerfen."
256
256
  }
257
+ },
258
+ "ner": {
259
+ "title": "Trainiere ein Modell zur Entitätenerkennung",
260
+ "not-found": "Dieses Feature benötigt einen laufenden TEI Publisher NER Server, aber der Dienst antwortet nicht.",
261
+ "found": "TEI Publisher NER gefunden. spaCy Version:",
262
+ "name-placeholder": "Name des zu erstellenden Modells",
263
+ "name": "Name",
264
+ "path": "Pfad",
265
+ "path-placeholder": "Relativer Pfad zu einer Sammlung oder Datei",
266
+ "path-info": "Pfad zu einem Dokument oder einer Sammlung eingeben. Diese wird als Eingabe für das Training verwendet. Sollte relativ zum TEI Publisher Datenverzeichnis sein.",
267
+ "lang-info": "Es kann entweder ein neues Modell trainiert oder ein bestehendes Basismodell erweitert werden. Falls kein Basismodell unten ausgewählt wird, ist die Sprache anzugeben.",
268
+ "language-placeholder": "Sprache für die das Modell erstellt wird",
269
+ "language": "Sprache",
270
+ "language-error": "Benötige entweder eine Sprache oder ein Basismodell",
271
+ "model": "Basismodell",
272
+ "model-placeholder": "Zu erweiterndes Basismodell",
273
+ "vectors": "nur die Vektoren aus Basismodell kopieren",
274
+ "output": "Trainingsausgabe",
275
+ "prepare": "Trainingsdaten vorbereiten",
276
+ "start": "Ausführen"
257
277
  }
258
278
  }
@@ -254,5 +254,25 @@
254
254
  "model": "Select a model",
255
255
  "denied": "Unsaved changes found: please store or discard them first."
256
256
  }
257
+ },
258
+ "ner": {
259
+ "title": "Train a Named Entity Recognition Model",
260
+ "not-found": "This feature requires the TEI Publisher NER service to be running! Failed to contact the service.",
261
+ "found": "Found TEI Publisher NER with spaCy version",
262
+ "name-placeholder": "Name of the model to create",
263
+ "name": "Name",
264
+ "path": "Path",
265
+ "path-placeholder": "Relative path to document or collection",
266
+ "path-info": "Enter path to a document or collection to use as training input. Should be relative to TEI Publisher's data root.",
267
+ "lang-info": "You may either train a new model for a language or select an existing model to extend below. If not extending an existing model, you must specify a language. Otherwise leave it empty.",
268
+ "language-placeholder": "Language to create the model for",
269
+ "language": "Language",
270
+ "language-error": "Either a language or a base model are required",
271
+ "model": "Base Model",
272
+ "model-placeholder": "Base model to extend or copy from",
273
+ "vectors": "Only copy vectors from base model",
274
+ "output": "Training Output",
275
+ "prepare": "Preparing training data",
276
+ "start": "Run"
257
277
  }
258
278
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teipublisher/pb-components",
3
- "version": "1.30.1",
3
+ "version": "1.31.0",
4
4
  "description": "Collection of webcomponents underlying TEI Publisher",
5
5
  "repository": "https://github.com/eeditiones/tei-publisher-components.git",
6
6
  "main": "index.html",
package/pb-elements.json CHANGED
@@ -2249,8 +2249,13 @@
2249
2249
  {
2250
2250
  "name": "pb-custom-form",
2251
2251
  "path": "./src/pb-custom-form.js",
2252
- "description": "A custom form element which loads the actual form from a server-side script using AJAX.\nEmits a `pb-search-resubmit` event when the form is submitted, signalling `pb-search` that\na search should be redone using the parameters passed.\n\nThe component is currently used to implement the additional search facets on the start page and\nsearch result page.",
2252
+ "description": "A custom form element which loads the actual form from a server-side script using AJAX.\nEmits `pb-search-resubmit` and `pb-submit` events, signalling the receiver that it should\nrefresh.\n\nThe component is currently used to implement the additional search facets on the start and\nsearch result page. It can also be combined with `pb-split-list` to contain an additional form\nwith options.",
2253
2253
  "attributes": [
2254
+ {
2255
+ "name": "auto-submit",
2256
+ "description": "Register event handlers on all inputs and submit the form\nautomatically if any of those changes. For button-like controls,\na submit is triggered on click, for text input on keyUp, and for\nall other form components on change.",
2257
+ "type": "string"
2258
+ },
2254
2259
  {
2255
2260
  "name": "url",
2256
2261
  "description": "The URL for the AJAX request. If a relative URL is passed, it will be resolved\nagainst the current API endpoint.",
@@ -2345,6 +2350,12 @@
2345
2350
  }
2346
2351
  ],
2347
2352
  "properties": [
2353
+ {
2354
+ "name": "autoSubmit",
2355
+ "attribute": "auto-submit",
2356
+ "description": "Register event handlers on all inputs and submit the form\nautomatically if any of those changes. For button-like controls,\na submit is triggered on click, for text input on keyUp, and for\nall other form components on change.",
2357
+ "type": "string"
2358
+ },
2348
2359
  {
2349
2360
  "name": "url",
2350
2361
  "attribute": "url",
@@ -2466,7 +2477,16 @@
2466
2477
  ],
2467
2478
  "events": [
2468
2479
  {
2469
- "name": "pb-custom-form-loaded"
2480
+ "name": "pb-custom-form-loaded",
2481
+ "description": "Fired before the element updates its content"
2482
+ },
2483
+ {
2484
+ "name": "pb-search-resubmit",
2485
+ "description": "Fired when the form is submitted"
2486
+ },
2487
+ {
2488
+ "name": "pb-submit",
2489
+ "description": "Fired when the form is submitted"
2470
2490
  },
2471
2491
  {
2472
2492
  "name": "pb-start-update",
@@ -3738,6 +3758,11 @@
3738
3758
  "type": "string",
3739
3759
  "default": "\"mouseover\""
3740
3760
  },
3761
+ {
3762
+ "name": "auto",
3763
+ "type": "boolean",
3764
+ "default": "false"
3765
+ },
3741
3766
  {
3742
3767
  "name": "key",
3743
3768
  "description": "The key to which this element is connected.",
@@ -3819,6 +3844,12 @@
3819
3844
  "type": "string",
3820
3845
  "default": "\"mouseover\""
3821
3846
  },
3847
+ {
3848
+ "name": "auto",
3849
+ "attribute": "auto",
3850
+ "type": "boolean",
3851
+ "default": "false"
3852
+ },
3822
3853
  {
3823
3854
  "name": "key",
3824
3855
  "attribute": "key",
@@ -8841,6 +8872,136 @@
8841
8872
  }
8842
8873
  ]
8843
8874
  },
8875
+ {
8876
+ "name": "pb-split-list",
8877
+ "path": "./src/pb-split-list.js",
8878
+ "description": "Implements a list which is split into different categories \n(e.g. letters of the alphabet, countries ...).\nOnly one category is shown at a time unless the server reports\nno categories (e.g. if the number of items to display goes below\na defined threshold).\n\nThe server-side API endpoint should return a JSON object with two\nproperties:\n\n+ `categories`: an array of category descriptions: each item should \n be an object with two properties: `category` - containing the name of the category\n and `count` - containing a count of items available under this category.\n+ `items`: an array with the items to be shown for the currently selected\n category. Those may contain HTML markup.",
8879
+ "attributes": [
8880
+ {
8881
+ "name": "url",
8882
+ "description": "Server-side API endpoint to retrieve items from",
8883
+ "type": "string"
8884
+ },
8885
+ {
8886
+ "name": "selected",
8887
+ "description": "The initially selected category",
8888
+ "type": "string"
8889
+ },
8890
+ {
8891
+ "name": "subforms",
8892
+ "description": "A CSS selector pointing to one or more `pb-custom-form`\ninstances. The element will collect additional parameters\nfrom those forms and includes them in the request to the server",
8893
+ "type": "string"
8894
+ },
8895
+ {
8896
+ "name": "subscribe",
8897
+ "description": "The name of the channel to subscribe to. Only events on a channel corresponding\nto this property are listened to.",
8898
+ "type": "string"
8899
+ },
8900
+ {
8901
+ "name": "subscribe-config",
8902
+ "description": "Configuration object to define a channel/event mapping. Every property\nin the object is interpreted as the name of a channel and its value should\nbe an array of event names to listen to.",
8903
+ "type": "object"
8904
+ },
8905
+ {
8906
+ "name": "emit",
8907
+ "description": "The name of the channel to send events to.",
8908
+ "type": "string"
8909
+ },
8910
+ {
8911
+ "name": "emit-config",
8912
+ "description": "Configuration object to define a channel/event mapping. Every property\nin the object is interpreted as the name of a channel and its value should\nbe an array of event names to be dispatched.",
8913
+ "type": "object"
8914
+ },
8915
+ {
8916
+ "name": "wait-for",
8917
+ "description": "A selector pointing to other components this component depends on.\nWhen method `wait` is called, it will wait until all referenced\ncomponents signal with a `pb-ready` event that they are ready and listening\nto events.",
8918
+ "type": "string"
8919
+ },
8920
+ {
8921
+ "name": "disabled",
8922
+ "description": "Common property to disable the functionality associated with a component.\n`pb-highlight` and `pb-popover` react to this.",
8923
+ "type": "boolean",
8924
+ "default": "false"
8925
+ }
8926
+ ],
8927
+ "properties": [
8928
+ {
8929
+ "name": "url",
8930
+ "attribute": "url",
8931
+ "description": "Server-side API endpoint to retrieve items from",
8932
+ "type": "string"
8933
+ },
8934
+ {
8935
+ "name": "selected",
8936
+ "attribute": "selected",
8937
+ "description": "The initially selected category",
8938
+ "type": "string"
8939
+ },
8940
+ {
8941
+ "name": "subforms",
8942
+ "attribute": "subforms",
8943
+ "description": "A CSS selector pointing to one or more `pb-custom-form`\ninstances. The element will collect additional parameters\nfrom those forms and includes them in the request to the server",
8944
+ "type": "string"
8945
+ },
8946
+ {
8947
+ "name": "subscribe",
8948
+ "attribute": "subscribe",
8949
+ "description": "The name of the channel to subscribe to. Only events on a channel corresponding\nto this property are listened to.",
8950
+ "type": "string"
8951
+ },
8952
+ {
8953
+ "name": "subscribeConfig",
8954
+ "attribute": "subscribe-config",
8955
+ "description": "Configuration object to define a channel/event mapping. Every property\nin the object is interpreted as the name of a channel and its value should\nbe an array of event names to listen to.",
8956
+ "type": "object"
8957
+ },
8958
+ {
8959
+ "name": "emit",
8960
+ "attribute": "emit",
8961
+ "description": "The name of the channel to send events to.",
8962
+ "type": "string"
8963
+ },
8964
+ {
8965
+ "name": "emitConfig",
8966
+ "attribute": "emit-config",
8967
+ "description": "Configuration object to define a channel/event mapping. Every property\nin the object is interpreted as the name of a channel and its value should\nbe an array of event names to be dispatched.",
8968
+ "type": "object"
8969
+ },
8970
+ {
8971
+ "name": "waitFor",
8972
+ "attribute": "wait-for",
8973
+ "description": "A selector pointing to other components this component depends on.\nWhen method `wait` is called, it will wait until all referenced\ncomponents signal with a `pb-ready` event that they are ready and listening\nto events.",
8974
+ "type": "string"
8975
+ },
8976
+ {
8977
+ "name": "disabled",
8978
+ "attribute": "disabled",
8979
+ "description": "Common property to disable the functionality associated with a component.\n`pb-highlight` and `pb-popover` react to this.",
8980
+ "type": "boolean",
8981
+ "default": "false"
8982
+ }
8983
+ ],
8984
+ "events": [
8985
+ {
8986
+ "name": "pb-submit",
8987
+ "description": "when received, submit a request to the server and refresh"
8988
+ },
8989
+ {
8990
+ "name": "pb-start-update",
8991
+ "description": "sent before the element sends the request to the server"
8992
+ },
8993
+ {
8994
+ "name": "pb-end-update",
8995
+ "description": "sent after new content has been received"
8996
+ }
8997
+ ],
8998
+ "cssProperties": [
8999
+ {
9000
+ "name": "--pb-categorized-list-columns",
9001
+ "description": "the number of columns to display (default: 2)"
9002
+ }
9003
+ ]
9004
+ },
8844
9005
  {
8845
9006
  "name": "pb-svg",
8846
9007
  "path": "./src/pb-svg.js",
@@ -57,6 +57,7 @@ import './pb-message.js';
57
57
  import './pb-blacklab-results.js';
58
58
  import './pb-blacklab-highlight.js';
59
59
  import './pb-table-grid.js';
60
+ import './pb-split-list.js';
60
61
 
61
62
  import '@polymer/iron-icons/editor-icons';
62
63
  import '@polymer/iron-icons/social-icons';
@@ -7,14 +7,16 @@ import { PbLoad } from './pb-load.js';
7
7
 
8
8
  /**
9
9
  * A custom form element which loads the actual form from a server-side script using AJAX.
10
- * Emits a `pb-search-resubmit` event when the form is submitted, signalling `pb-search` that
11
- * a search should be redone using the parameters passed.
10
+ * Emits `pb-search-resubmit` and `pb-submit` events, signalling the receiver that it should
11
+ * refresh.
12
12
  *
13
- * The component is currently used to implement the additional search facets on the start page and
14
- * search result page.
13
+ * The component is currently used to implement the additional search facets on the start and
14
+ * search result page. It can also be combined with `pb-split-list` to contain an additional form
15
+ * with options.
15
16
  *
16
- * @customElement
17
- * @polymer
17
+ * @fires pb-custom-form-loaded - Fired before the element updates its content
18
+ * @fires pb-search-resubmit - Fired when the form is submitted
19
+ * @fires pb-submit - Fired when the form is submitted
18
20
  */
19
21
  export class PbCustomForm extends PbLoad {
20
22
 
@@ -34,6 +36,8 @@ export class PbCustomForm extends PbLoad {
34
36
  this._reset();
35
37
  }
36
38
  });
39
+
40
+ this._submissionHandlers();
37
41
  }
38
42
 
39
43
  render() {
@@ -81,7 +85,8 @@ export class PbCustomForm extends PbLoad {
81
85
 
82
86
  _submit() {
83
87
  const json = this.serializeForm();
84
- this.emitTo('pb-search-resubmit', { 'params': json });
88
+ this.emitTo('pb-search-resubmit', { params: json });
89
+ this.emitTo('pb-submit', { params: json});
85
90
  }
86
91
 
87
92
  _reset(){
@@ -102,6 +107,43 @@ export class PbCustomForm extends PbLoad {
102
107
  this.dispatchEvent(new CustomEvent('pb-custom-form-loaded', { detail: content }));
103
108
  }
104
109
 
110
+ _submissionHandlers() {
111
+ if (!this.autoSubmit) {
112
+ return;
113
+ }
114
+ this.querySelectorAll(this.autoSubmit).forEach((control) => {
115
+ const name = control.nodeName.toLowerCase();
116
+ let event = 'change';
117
+ if (control instanceof HTMLButtonElement ||
118
+ name === 'paper-icon-button' || name === 'paper-button' ||
119
+ (name === 'input' && (control.type === 'button' || control.type === 'submit' || control.type === 'reset'))
120
+ ) {
121
+ event = 'click';
122
+ } else if (name === 'paper-input' || (control instanceof HTMLInputElement && control.type === 'text')) {
123
+ event = 'keyup';
124
+ } else if (name === 'paper-dropdown-menu') {
125
+ event = 'value-changed';
126
+ }
127
+ control.addEventListener(event, this._submit.bind(this));
128
+ });
129
+ }
130
+
131
+ static get properties() {
132
+ return {
133
+ /**
134
+ * Register event handlers on all inputs and submit the form
135
+ * automatically if any of those changes. For button-like controls,
136
+ * a submit is triggered on click, for text input on keyUp, and for
137
+ * all other form components on change.
138
+ */
139
+ autoSubmit: {
140
+ type: String,
141
+ attribute: 'auto-submit'
142
+ },
143
+ ...super.properties
144
+ };
145
+ }
146
+
105
147
  /**
106
148
  * Fired before the element updates its content
107
149
  *
@@ -32,6 +32,9 @@ export class PbGeolocation extends PbHighlight {
32
32
  },
33
33
  event: {
34
34
  type: String
35
+ },
36
+ auto: {
37
+ type: Boolean
35
38
  }
36
39
  };
37
40
  }
@@ -39,22 +42,38 @@ export class PbGeolocation extends PbHighlight {
39
42
  constructor() {
40
43
  super();
41
44
  this.event = 'mouseover';
45
+ this.auto = false;
42
46
  }
43
47
 
44
48
  connectedCallback() {
45
49
  super.connectedCallback();
46
50
 
47
- this.addEventListener(this.event, () =>
48
- this.emitTo('pb-geolocation', {
49
- coordinates: {
50
- latitude: this.latitude,
51
- longitude: this.longitude
52
- },
53
- label: this.label,
54
- popup: this.popup,
55
- element: this
56
- })
57
- );
51
+ if (this.event) {
52
+ this.addEventListener(this.event, () =>
53
+ this.emitTo('pb-geolocation', {
54
+ coordinates: {
55
+ latitude: this.latitude,
56
+ longitude: this.longitude
57
+ },
58
+ label: this.label,
59
+ popup: this.popup,
60
+ element: this
61
+ })
62
+ );
63
+ }
64
+ if (this.auto) {
65
+ this.waitForChannel(() => {
66
+ this.emitTo('pb-geolocation', {
67
+ coordinates: {
68
+ latitude: this.latitude,
69
+ longitude: this.longitude
70
+ },
71
+ label: this.label,
72
+ popup: this.popup,
73
+ element: this
74
+ });
75
+ });
76
+ }
58
77
  }
59
78
 
60
79
  render() {
@@ -12,7 +12,6 @@ import './pb-map-layer.js';
12
12
  * @fires pb-update-map - When received, redraws the map to fit markers passed in with the event
13
13
  * @fires pb-update - When received, redraws the map to show markers for all pb-geolocation elements
14
14
  * @fires pb-geolocation - When received, focuses the map on the geocoordinates passed in with the event
15
-
16
15
  */
17
16
  export class PbLeafletMap extends pbMixin(LitElement) {
18
17
  static get properties() {
@@ -143,10 +142,12 @@ export class PbLeafletMap extends pbMixin(LitElement) {
143
142
  });
144
143
  });
145
144
  // this._map.invalidateSize();
146
- if (locations.length > 1) {
147
- this._map.fitBounds(bounds);
148
- } else {
145
+ if (locations.length === 0) {
149
146
  this._map.fitWorld();
147
+ } else if (locations.length === 1) {
148
+ this._map.fitBounds(bounds, {maxZoom: this.zoom});
149
+ } else {
150
+ this._map.fitBounds(bounds);
150
151
  }
151
152
  });
152
153
 
@@ -140,6 +140,12 @@ export class PbSelectFeature extends pbMixin(LitElement) {
140
140
  :host {
141
141
  display: block;
142
142
  }
143
+
144
+ #menu {
145
+ width: inherit;
146
+ min-width: inherit;
147
+ max-width: inherit;
148
+ }
143
149
  `;
144
150
  }
145
151
  }
@@ -0,0 +1,196 @@
1
+ import { LitElement, html, css } from 'lit-element';
2
+ import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
3
+ import { pbMixin } from './pb-mixin.js';
4
+
5
+ /**
6
+ * Implements a list which is split into different categories
7
+ * (e.g. letters of the alphabet, countries ...).
8
+ * Only one category is shown at a time unless the server reports
9
+ * no categories (e.g. if the number of items to display goes below
10
+ * a defined threshold).
11
+ *
12
+ * The server-side API endpoint should return a JSON object with two
13
+ * properties:
14
+ *
15
+ * + `categories`: an array of category descriptions: each item should
16
+ * be an object with two properties: `category` - containing the name of the category
17
+ * and `count` - containing a count of items available under this category.
18
+ * + `items`: an array with the items to be shown for the currently selected
19
+ * category. Those may contain HTML markup.
20
+ *
21
+ * @cssprop --pb-categorized-list-columns - the number of columns to display (default: 2)
22
+ * @fires pb-submit - when received, submit a request to the server and refresh
23
+ * @fires pb-start-update - sent before the element sends the request to the server
24
+ * @fires pb-end-update - sent after new content has been received
25
+ */
26
+ export class PbSplitList extends pbMixin(LitElement) {
27
+ static get properties() {
28
+ return {
29
+ /**
30
+ * Server-side API endpoint to retrieve items from
31
+ */
32
+ url: {
33
+ type: String
34
+ },
35
+ /**
36
+ * The initially selected category
37
+ */
38
+ selected: {
39
+ type: String
40
+ },
41
+ /**
42
+ * A CSS selector pointing to one or more `pb-custom-form`
43
+ * instances. The element will collect additional parameters
44
+ * from those forms and includes them in the request to the server
45
+ */
46
+ subforms: {
47
+ type: String
48
+ },
49
+ _categories: {
50
+ type: Array
51
+ },
52
+ _items: {
53
+ type: Array
54
+ },
55
+ ...super.properties
56
+ };
57
+ }
58
+
59
+ constructor() {
60
+ super();
61
+ this._categories = [];
62
+ this._items = [];
63
+ this._params = {};
64
+ this.selected = null;
65
+ this.subforms = null;
66
+ }
67
+
68
+ connectedCallback() {
69
+ super.connectedCallback();
70
+
71
+ this.selected = this.getParameter('category', this.selected);
72
+
73
+ window.addEventListener('popstate', (ev) => {
74
+ console.log('<pb-split-list> popstate: %o', ev);
75
+ this.selected = ev.state.category;
76
+ this.submit();
77
+ });
78
+
79
+ this.subscribeTo('pb-submit', this.load.bind(this));
80
+ }
81
+
82
+ firstUpdated() {
83
+ super.firstUpdated();
84
+
85
+ PbSplitList.waitOnce('pb-page-ready', () => {
86
+ this.load();
87
+ });
88
+ }
89
+
90
+ submit() {
91
+ this.load();
92
+ }
93
+
94
+ load() {
95
+ const formParams = this._paramsFromSubforms({ category: this.selected });
96
+ this.setParameters(formParams);
97
+ this.pushHistory('pb-split-list', formParams);
98
+
99
+ const params = new URLSearchParams(formParams);
100
+
101
+ const url = `${this.toAbsoluteURL(this.url)}?${params.toString()}`;
102
+ console.log(`<pb-split-list> Fetching from URL: ${url}`);
103
+
104
+ this.emitTo('pb-start-update');
105
+
106
+ fetch(url)
107
+ .then((response) => {
108
+ if (response.ok) {
109
+ return response.json();
110
+ }
111
+ return Promise.reject(response.status);
112
+ })
113
+ .then((json) => {
114
+ this._categories = json.categories;
115
+ this._items = json.items;
116
+ this.emitTo('pb-end-update');
117
+ })
118
+ .catch((error) => {
119
+ console.error(`<pb-split-list> Error caught: ${error}`);
120
+ this.emitTo('pb-end-update');
121
+ });
122
+ }
123
+
124
+ _selectCategory(ev, category) {
125
+ ev.preventDefault();
126
+ this.selected = category;
127
+ this.load();
128
+ }
129
+
130
+ _paramsFromSubforms(params) {
131
+ if (this.subforms) {
132
+ document.querySelectorAll(this.subforms).forEach((form) => {
133
+ if (form.serializeForm) {
134
+ Object.assign(params, form.serializeForm());
135
+ }
136
+ });
137
+ }
138
+ return params;
139
+ }
140
+
141
+ render() {
142
+ return html`
143
+ <header>
144
+ ${
145
+ this._categories.map((cat) =>
146
+ html`
147
+ <a part="${this.selected === cat.category ? 'active-category' : 'category'}" href="#${cat.category}" title="${cat.count}" class="${this.selected === cat.category ? 'active' : ''}"
148
+ @click="${(ev) => this._selectCategory(ev, cat.category)}">
149
+ ${cat.category}
150
+ </a>
151
+ `
152
+ )
153
+ }
154
+ </header>
155
+ <div id="items" part="items">
156
+ ${
157
+ this._items.map((item) => html`<div part="item">${unsafeHTML(item)}</div>`)
158
+ }
159
+ </div>
160
+ `;
161
+ }
162
+
163
+ static get styles() {
164
+ return css`
165
+ :host {
166
+ display: block;
167
+ }
168
+
169
+ header {
170
+ display: grid;
171
+ grid-auto-flow: column;
172
+ width: 100%;
173
+ column-gap: 10px;
174
+ }
175
+
176
+ #items {
177
+ display: grid;
178
+ grid-template-columns: repeat(var(--pb-categorized-list-columns, 2), auto);
179
+ grid-auto-rows: 1fr;
180
+ column-gap: 10px;
181
+ width: 100%;
182
+ }
183
+
184
+ [part=category], #items a {
185
+ text-decoration: none;
186
+ color: var(--pb-link-color);
187
+ }
188
+
189
+ [part=active-category] {
190
+ text-decoration: none;
191
+ color: var(--pb-highlight-color);
192
+ }
193
+ `;
194
+ }
195
+ }
196
+ customElements.define('pb-split-list', PbSplitList);