@phatvu/web-component-poc 1.0.5 → 1.0.7
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/cjs/fast-button.cjs.entry.js +46 -0
- package/dist/cjs/{fast-button_4.cjs.entry.js → fast-carousel.cjs.entry.js} +1 -231
- package/dist/cjs/fast-input_4.cjs.entry.js +499 -0
- package/dist/cjs/{index-B2BTpdbN.js → index-BEvZs91D.js} +2 -2
- package/dist/cjs/job-card.cjs.entry.js +138 -0
- package/dist/cjs/loader.cjs.js +2 -2
- package/dist/cjs/web-component-poc.cjs.js +2 -2
- package/dist/collection/collection-manifest.json +4 -1
- package/dist/collection/components/button/button.js +2 -2
- package/dist/collection/components/fast-input/fast-input.css +55 -0
- package/dist/collection/components/fast-input/fast-input.js +335 -0
- package/dist/collection/components/job-card/job-card.css +247 -0
- package/dist/collection/components/job-card/job-card.js +435 -0
- package/dist/collection/components/jobs-item/jobs-item.js +5 -5
- package/dist/collection/components/jobs-list-only/jobs-list-only.js +185 -8
- package/dist/collection/components/jobs-list-reactive/jobs-list-reactive.css +8 -0
- package/dist/collection/components/jobs-list-reactive/jobs-list-reactive.js +203 -0
- package/dist/components/fast-button.js +1 -1
- package/dist/components/fast-carousel.js +1 -1
- package/dist/components/fast-input.d.ts +11 -0
- package/dist/components/fast-input.js +1 -0
- package/dist/components/index.js +1 -1
- package/dist/components/job-card.d.ts +11 -0
- package/dist/components/job-card.js +1 -0
- package/dist/components/jobs-item.js +1 -1
- package/dist/components/jobs-list-only.js +1 -1
- package/dist/components/jobs-list-reactive.d.ts +11 -0
- package/dist/components/jobs-list-reactive.js +1 -0
- package/dist/components/{p-ClQDwJJB.js → p-DQiaLjLf.js} +1 -1
- package/dist/esm/fast-button.entry.js +44 -0
- package/dist/esm/{fast-button_4.entry.js → fast-carousel.entry.js} +2 -229
- package/dist/esm/fast-input_4.entry.js +494 -0
- package/dist/esm/{index-Dk5CvWmb.js → index-C_ZLQIpp.js} +2 -2
- package/dist/esm/job-card.entry.js +136 -0
- package/dist/esm/loader.js +3 -3
- package/dist/esm/web-component-poc.js +3 -3
- package/dist/types/components/fast-input/fast-input.d.ts +37 -0
- package/dist/types/components/job-card/job-card.d.ts +93 -0
- package/dist/types/components/jobs-item/jobs-item.d.ts +2 -2
- package/dist/types/components/jobs-list-only/jobs-list-only.d.ts +24 -2
- package/dist/types/components/jobs-list-reactive/jobs-list-reactive.d.ts +26 -0
- package/dist/types/components.d.ts +469 -7
- package/dist/types/mock/jobs-list-only.mock.d.ts +2 -2
- package/dist/types/types/jobs-list.d.ts +6 -2
- package/dist/web-component-poc/p-618fba28.entry.js +1 -0
- package/dist/web-component-poc/p-7d45772f.entry.js +1 -0
- package/dist/web-component-poc/p-bef7c8e2.entry.js +1 -0
- package/dist/web-component-poc/p-cfb9aed9.entry.js +1 -0
- package/dist/web-component-poc/web-component-poc.esm.js +1 -1
- package/hydrate/index.js +534 -6
- package/hydrate/index.mjs +534 -6
- package/package.json +9 -1
- package/dist/web-component-poc/p-df843533.entry.js +0 -1
- /package/dist/components/{p-UM9TUfe3.js → p-BiaJAQXY.js} +0 -0
- /package/dist/web-component-poc/{p-Dk5CvWmb.js → p-C_ZLQIpp.js} +0 -0
package/hydrate/index.mjs
CHANGED
|
@@ -130,7 +130,7 @@ function hydrateFactory($stencilWindow, $stencilHydrateOpts, $stencilHydrateResu
|
|
|
130
130
|
|
|
131
131
|
|
|
132
132
|
const NAMESPACE = 'web-component-poc';
|
|
133
|
-
const BUILD = /* web-component-poc */ { hotModuleReplacement: false, hydratedSelectorName: "hydrated",
|
|
133
|
+
const BUILD = /* web-component-poc */ { hotModuleReplacement: false, hydratedSelectorName: "hydrated", propChangeCallback: false, shadowDom: false, slotRelocation: true, state: true, updatable: true};
|
|
134
134
|
|
|
135
135
|
/*
|
|
136
136
|
Stencil Hydrate Platform v4.43.2 | MIT Licensed | https://stenciljs.com
|
|
@@ -4505,7 +4505,7 @@ var registerInstance = (lazyInstance, hostRef) => {
|
|
|
4505
4505
|
if (!hostRef) return void 0;
|
|
4506
4506
|
lazyInstance.__stencil__getHostRef = () => hostRef;
|
|
4507
4507
|
hostRef.$lazyInstance$ = lazyInstance;
|
|
4508
|
-
if (hostRef.$cmpMeta$.$flags$ & 512 /* hasModernPropertyDecls */ && (BUILD.
|
|
4508
|
+
if (hostRef.$cmpMeta$.$flags$ & 512 /* hasModernPropertyDecls */ && (BUILD.state)) {
|
|
4509
4509
|
reWireGetterSetter(lazyInstance, hostRef);
|
|
4510
4510
|
}
|
|
4511
4511
|
return hostRef;
|
|
@@ -6505,6 +6505,334 @@ class CustomButton {
|
|
|
6505
6505
|
}; }
|
|
6506
6506
|
}
|
|
6507
6507
|
|
|
6508
|
+
const fastInputCss = () => `.fast-input{position:relative;display:inline-flex;gap:0.5rem}.fast-input__field{flex:1;padding:0.5rem 0.75rem;font-size:1rem;border:1px solid #ccc;border-radius:4px}.fast-input__button{padding:0.5rem 1rem;font-size:1rem;cursor:pointer;background:#0070f3;color:#fff;border:none;border-radius:4px}.fast-input__dropdown{position:absolute;top:100%;left:0;right:0;margin:0;padding:0;list-style:none;background:#fff;border:1px solid #ccc;border-top:none;border-radius:0 0 4px 4px;z-index:100;max-height:200px;overflow-y:auto}.fast-input__dropdown-item{padding:0.5rem 0.75rem;cursor:pointer}.fast-input__dropdown-item:hover{background:#f0f0f0}.fast-input__dropdown-loading{padding:0.5rem 0.75rem;color:#999;font-style:italic}`;
|
|
6509
|
+
|
|
6510
|
+
class FastInput {
|
|
6511
|
+
constructor(hostRef) {
|
|
6512
|
+
registerInstance(this, hostRef);
|
|
6513
|
+
this.searchExecuted = createEvent(this, "searchExecuted");
|
|
6514
|
+
this.inputChanged = createEvent(this, "inputChanged");
|
|
6515
|
+
}
|
|
6516
|
+
placeholder = 'Search...';
|
|
6517
|
+
value = '';
|
|
6518
|
+
paramName = 'keyword';
|
|
6519
|
+
enableAutocomplete = false;
|
|
6520
|
+
autocompleteUrl = '/api/jobs/autocomplete';
|
|
6521
|
+
targetPath;
|
|
6522
|
+
debounceMs = 300;
|
|
6523
|
+
minChars = 3;
|
|
6524
|
+
searchExecuted;
|
|
6525
|
+
inputChanged;
|
|
6526
|
+
inputValue = '';
|
|
6527
|
+
suggestions = [];
|
|
6528
|
+
showDropdown = false;
|
|
6529
|
+
autocompleteLoading = false;
|
|
6530
|
+
debounceTimer;
|
|
6531
|
+
popstateHandler;
|
|
6532
|
+
connectedCallback() {
|
|
6533
|
+
const urlValue = this.getUrlParam();
|
|
6534
|
+
this.inputValue = urlValue !== null ? urlValue : this.value;
|
|
6535
|
+
this.popstateHandler = () => {
|
|
6536
|
+
this.inputValue = this.getUrlParam() ?? '';
|
|
6537
|
+
};
|
|
6538
|
+
window.addEventListener('popstate', this.popstateHandler);
|
|
6539
|
+
}
|
|
6540
|
+
disconnectedCallback() {
|
|
6541
|
+
window.removeEventListener('popstate', this.popstateHandler);
|
|
6542
|
+
clearTimeout(this.debounceTimer);
|
|
6543
|
+
}
|
|
6544
|
+
getUrlParam() {
|
|
6545
|
+
const params = new URLSearchParams(window.location.search);
|
|
6546
|
+
return params.get(this.paramName);
|
|
6547
|
+
}
|
|
6548
|
+
updateUrlParam(value) {
|
|
6549
|
+
const params = new URLSearchParams(window.location.search);
|
|
6550
|
+
if (value) {
|
|
6551
|
+
params.set(this.paramName, value);
|
|
6552
|
+
}
|
|
6553
|
+
else {
|
|
6554
|
+
params.delete(this.paramName);
|
|
6555
|
+
}
|
|
6556
|
+
const qs = params.toString();
|
|
6557
|
+
const newUrl = qs
|
|
6558
|
+
? `${window.location.pathname}?${qs}`
|
|
6559
|
+
: window.location.pathname;
|
|
6560
|
+
history.pushState({}, '', newUrl);
|
|
6561
|
+
}
|
|
6562
|
+
submit() {
|
|
6563
|
+
this.updateUrlParam(this.inputValue);
|
|
6564
|
+
document.dispatchEvent(new CustomEvent('search-executed', {
|
|
6565
|
+
detail: { keyword: this.inputValue },
|
|
6566
|
+
bubbles: true,
|
|
6567
|
+
composed: true,
|
|
6568
|
+
}));
|
|
6569
|
+
this.searchExecuted.emit({ keyword: this.inputValue });
|
|
6570
|
+
this.showDropdown = false;
|
|
6571
|
+
}
|
|
6572
|
+
handleInput = (e) => {
|
|
6573
|
+
const value = e.target.value;
|
|
6574
|
+
this.inputValue = value;
|
|
6575
|
+
this.inputChanged.emit({ value });
|
|
6576
|
+
if (this.enableAutocomplete) {
|
|
6577
|
+
this.scheduleAutocomplete(value);
|
|
6578
|
+
}
|
|
6579
|
+
};
|
|
6580
|
+
handleKeydown = (e) => {
|
|
6581
|
+
if (e.key === 'Enter') {
|
|
6582
|
+
this.submit();
|
|
6583
|
+
}
|
|
6584
|
+
else if (e.key === 'Escape') {
|
|
6585
|
+
this.showDropdown = false;
|
|
6586
|
+
}
|
|
6587
|
+
};
|
|
6588
|
+
handleBlur = () => {
|
|
6589
|
+
this.showDropdown = false;
|
|
6590
|
+
};
|
|
6591
|
+
scheduleAutocomplete(value) {
|
|
6592
|
+
clearTimeout(this.debounceTimer);
|
|
6593
|
+
if (value.length < this.minChars) {
|
|
6594
|
+
this.showDropdown = false;
|
|
6595
|
+
return;
|
|
6596
|
+
}
|
|
6597
|
+
this.debounceTimer = setTimeout(() => this.fetchSuggestions(value), this.debounceMs);
|
|
6598
|
+
}
|
|
6599
|
+
async fetchSuggestions(keyword) {
|
|
6600
|
+
if (!this.targetPath) {
|
|
6601
|
+
console.warn('[fast-input] target-path is required for autocomplete');
|
|
6602
|
+
return;
|
|
6603
|
+
}
|
|
6604
|
+
this.autocompleteLoading = true;
|
|
6605
|
+
this.showDropdown = true;
|
|
6606
|
+
try {
|
|
6607
|
+
const res = await fetch(this.autocompleteUrl, {
|
|
6608
|
+
method: 'POST',
|
|
6609
|
+
headers: { 'Content-Type': 'application/json' },
|
|
6610
|
+
body: JSON.stringify({ keyword, target_path: this.targetPath }),
|
|
6611
|
+
});
|
|
6612
|
+
if (!res.ok)
|
|
6613
|
+
throw new Error('autocomplete request failed');
|
|
6614
|
+
const data = await res.json();
|
|
6615
|
+
this.suggestions = data;
|
|
6616
|
+
}
|
|
6617
|
+
catch {
|
|
6618
|
+
this.showDropdown = false;
|
|
6619
|
+
this.suggestions = [];
|
|
6620
|
+
}
|
|
6621
|
+
finally {
|
|
6622
|
+
this.autocompleteLoading = false;
|
|
6623
|
+
}
|
|
6624
|
+
}
|
|
6625
|
+
selectSuggestion(title) {
|
|
6626
|
+
this.inputValue = title;
|
|
6627
|
+
this.showDropdown = false;
|
|
6628
|
+
this.submit();
|
|
6629
|
+
}
|
|
6630
|
+
render() {
|
|
6631
|
+
return (hAsync("div", { key: '3a9d31c7b109205600addc326d63979585f10bcd', class: "fast-input" }, hAsync("input", { key: '8f238fe9e002f367d4939616be8c06d938d76045', type: "text", class: "fast-input__field", placeholder: this.placeholder, value: this.inputValue, onInput: this.handleInput, onKeyDown: this.handleKeydown, onBlur: this.handleBlur }), hAsync("button", { key: '7b7404f13432750ece669da8ce68be15179921de', class: "fast-input__button", type: "button", onClick: () => this.submit() }, "Search"), this.enableAutocomplete && this.showDropdown && (hAsync("ul", { key: '1438bacadc21c183842a8bdaa3f336bffb152e14', class: "fast-input__dropdown" }, this.autocompleteLoading ? (hAsync("li", { class: "fast-input__dropdown-loading" }, "Loading...")) : (this.suggestions.map(s => (hAsync("li", { class: "fast-input__dropdown-item", onMouseDown: e => { e.preventDefault(); }, onClick: () => this.selectSuggestion(s.title) }, s.title))))))));
|
|
6632
|
+
}
|
|
6633
|
+
static get style() { return fastInputCss(); }
|
|
6634
|
+
static get cmpMeta() { return {
|
|
6635
|
+
"$flags$": 512,
|
|
6636
|
+
"$tagName$": "fast-input",
|
|
6637
|
+
"$members$": {
|
|
6638
|
+
"placeholder": [1],
|
|
6639
|
+
"value": [1],
|
|
6640
|
+
"paramName": [1, "param-name"],
|
|
6641
|
+
"enableAutocomplete": [4, "enable-autocomplete"],
|
|
6642
|
+
"autocompleteUrl": [1, "autocomplete-url"],
|
|
6643
|
+
"targetPath": [1, "target-path"],
|
|
6644
|
+
"debounceMs": [2, "debounce-ms"],
|
|
6645
|
+
"minChars": [2, "min-chars"],
|
|
6646
|
+
"inputValue": [32],
|
|
6647
|
+
"suggestions": [32],
|
|
6648
|
+
"showDropdown": [32],
|
|
6649
|
+
"autocompleteLoading": [32]
|
|
6650
|
+
},
|
|
6651
|
+
"$listeners$": undefined,
|
|
6652
|
+
"$lazyBundleId$": "-",
|
|
6653
|
+
"$attrsToReflect$": []
|
|
6654
|
+
}; }
|
|
6655
|
+
}
|
|
6656
|
+
|
|
6657
|
+
const jobCardCss = () => `.job-card{display:block;padding:16px;border:1px solid #e0e0e0;border-radius:8px;background-color:#fff;box-shadow:0 2px 4px rgba(0, 0, 0, 0.08);transition:box-shadow 0.2s ease, border-color 0.2s ease}.job-card:hover{box-shadow:0 4px 8px rgba(0, 0, 0, 0.12);border-color:#d0d0d0}.job-card__header{margin-bottom:12px}.job-card__title{margin:0;font-size:18px;font-weight:700;display:flex;align-items:center;flex-wrap:wrap;gap:8px}.job-card__title--link{text-decoration:none;color:#1f9755;transition:color 0.2s ease}.job-card__title--link:hover{text-decoration:underline;color:#1a7a43}.job-card__reference{font-size:0.875em;color:#666;background-color:#f5f5f5;padding:2px 6px;border-radius:3px}.job-card__reference--empty{display:none}.job-card__remote{background:#e8f5e9;color:#2e7d32;border-radius:100px;padding:4px 12px;text-transform:uppercase;font-size:11px;font-weight:700;line-height:1.5;white-space:nowrap}.job-card__remote--empty{display:none}.job-card__distance{display:inline-flex;align-items:center;gap:4px;margin-top:6px;font-size:13px;font-weight:500;color:#555}.job-card__distance--icon{display:inline-flex;align-items:center}.job-card__distance--icon svg{width:16px;height:16px;color:#1f9755}.job-card__content{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.job-card__info{flex:1;display:flex;flex-direction:column;gap:8px}.job-card__street,.job-card__brand,.job-card__employment-type{display:flex;align-items:center;flex-wrap:wrap;gap:4px 6px;font-size:14px}.job-card__street--empty,.job-card__brand--empty,.job-card__employment-type--empty{color:#999}.job-card__street--icon,.job-card__brand--icon,.job-card__employment-type--icon{display:inline-flex;align-items:center;flex-shrink:0}.job-card__street--icon svg,.job-card__brand--icon svg,.job-card__employment-type--icon svg{width:16px;height:16px;color:#666}.job-card__street--label,.job-card__brand--label,.job-card__employment-type--label{color:#333}.job-card__street--label__wrapper{display:flex;align-items:center;gap:6px}.job-card__street--more-locations__wrapper{display:flex;align-items:center;gap:2px;font-size:12px;margin-left:2px}.job-card__street--amount{font-weight:600;color:#1f9755}.job-card__street--more-locations{color:#999}.job-card__apply{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:10px 16px;background-color:#198754;color:#fff;border-radius:4px;text-decoration:none;font-weight:600;font-size:14px;transition:background-color 0.2s ease, transform 0.1s ease;white-space:nowrap;flex-shrink:0}.job-card__apply:hover{background-color:#1a6f47;transform:translateY(-1px)}.job-card__apply:active{transform:translateY(0)}.job-card__apply--icon{display:inline-flex;align-items:center}.job-card__apply--icon svg{width:14px;height:14px}@media (max-width: 768px){.job-card{padding:12px}.job-card__content{flex-direction:column;gap:10px}.job-card__apply{width:100%;justify-content:center}.job-card__title{font-size:16px}}@media (max-width: 480px){.job-card{padding:10px}.job-card__title{font-size:15px}.job-card__distance{font-size:12px}.job-card__street,.job-card__brand,.job-card__employment-type{font-size:13px}}`;
|
|
6658
|
+
|
|
6659
|
+
/**
|
|
6660
|
+
* Helper function to get formatted location label from location object
|
|
6661
|
+
*/
|
|
6662
|
+
function getLocationLabel$1(loc) {
|
|
6663
|
+
if (loc.cityStateAbbr)
|
|
6664
|
+
return loc.cityStateAbbr;
|
|
6665
|
+
const parts = [loc.streetAddress, loc.city, loc.stateAbbr || loc.state, loc.countryAbbr || loc.country].filter(Boolean);
|
|
6666
|
+
return parts.join(', ') || loc.locationText || '';
|
|
6667
|
+
}
|
|
6668
|
+
/**
|
|
6669
|
+
* Helper function to get the first location from job locations
|
|
6670
|
+
*/
|
|
6671
|
+
function getFirstLocation$1(job) {
|
|
6672
|
+
const locs = job.locations;
|
|
6673
|
+
if (!locs?.length)
|
|
6674
|
+
return undefined;
|
|
6675
|
+
return locs[0];
|
|
6676
|
+
}
|
|
6677
|
+
/**
|
|
6678
|
+
* JobCard Component
|
|
6679
|
+
*
|
|
6680
|
+
* A reusable card component for displaying job information based on the React JobItem component.
|
|
6681
|
+
* Features include:
|
|
6682
|
+
* - Job title with apply link
|
|
6683
|
+
* - Location with distance
|
|
6684
|
+
* - Brand/company name
|
|
6685
|
+
* - Employment type
|
|
6686
|
+
* - Reference number (optional)
|
|
6687
|
+
* - Remote indicator (optional)
|
|
6688
|
+
* - Multi-location support
|
|
6689
|
+
* - Custom extra fields
|
|
6690
|
+
*
|
|
6691
|
+
* @example
|
|
6692
|
+
* ```html
|
|
6693
|
+
* <job-card
|
|
6694
|
+
* .job={jobData}
|
|
6695
|
+
* applyButtonText="Apply"
|
|
6696
|
+
* showBrand="true"
|
|
6697
|
+
* showEmploymentType="true"
|
|
6698
|
+
* ></job-card>
|
|
6699
|
+
* ```
|
|
6700
|
+
*/
|
|
6701
|
+
class JobCard {
|
|
6702
|
+
constructor(hostRef) {
|
|
6703
|
+
registerInstance(this, hostRef);
|
|
6704
|
+
}
|
|
6705
|
+
/**
|
|
6706
|
+
* The job data object to display. Accepts either a Job object or a JSON string.
|
|
6707
|
+
*/
|
|
6708
|
+
job;
|
|
6709
|
+
/**
|
|
6710
|
+
* Index of the job in a list (used for accessibility)
|
|
6711
|
+
*/
|
|
6712
|
+
index = 0;
|
|
6713
|
+
/**
|
|
6714
|
+
* Text for the apply button
|
|
6715
|
+
*/
|
|
6716
|
+
applyButtonText = 'Apply Now';
|
|
6717
|
+
/**
|
|
6718
|
+
* Whether to show the brand/company name
|
|
6719
|
+
*/
|
|
6720
|
+
showBrand = true;
|
|
6721
|
+
/**
|
|
6722
|
+
* Whether to show the reference number
|
|
6723
|
+
*/
|
|
6724
|
+
showReference = false;
|
|
6725
|
+
/**
|
|
6726
|
+
* Whether to show employment type
|
|
6727
|
+
*/
|
|
6728
|
+
showEmploymentType = true;
|
|
6729
|
+
/**
|
|
6730
|
+
* Text shown for multiple locations
|
|
6731
|
+
*/
|
|
6732
|
+
multiLocationText = 'More locations';
|
|
6733
|
+
/**
|
|
6734
|
+
* Text shown for remote jobs
|
|
6735
|
+
*/
|
|
6736
|
+
remoteLocationText = 'Remote';
|
|
6737
|
+
/**
|
|
6738
|
+
* Whether to show distances in kilometers instead of miles
|
|
6739
|
+
*/
|
|
6740
|
+
enableKilometers = false;
|
|
6741
|
+
/**
|
|
6742
|
+
* Whether to show commute time
|
|
6743
|
+
*/
|
|
6744
|
+
showCommuteTime = false;
|
|
6745
|
+
/**
|
|
6746
|
+
* Format string for street address (not used in base web component)
|
|
6747
|
+
*/
|
|
6748
|
+
streetFormat = '{street}, {city_state_abbr}';
|
|
6749
|
+
/** Extra CSS class on the root inner element (avoid prop name "className", reserved on HTMLElement). */
|
|
6750
|
+
rootClass = '';
|
|
6751
|
+
/**
|
|
6752
|
+
* Custom extra fields configuration
|
|
6753
|
+
*/
|
|
6754
|
+
extraFieldsConfig = [];
|
|
6755
|
+
/**
|
|
6756
|
+
* Format distance with unit
|
|
6757
|
+
*/
|
|
6758
|
+
formatDistance(distance) {
|
|
6759
|
+
const value = this.enableKilometers ? distance * 1.60934 : distance;
|
|
6760
|
+
const unit = this.enableKilometers ? 'Km' : 'Miles';
|
|
6761
|
+
const str = `${value.toFixed(1)} ${unit}`.replace('.0', '');
|
|
6762
|
+
return str;
|
|
6763
|
+
}
|
|
6764
|
+
/**
|
|
6765
|
+
* Is Empty utility
|
|
6766
|
+
*/
|
|
6767
|
+
isEmpty(value) {
|
|
6768
|
+
if (value === null || value === undefined || value === '') {
|
|
6769
|
+
return true;
|
|
6770
|
+
}
|
|
6771
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
6772
|
+
return true;
|
|
6773
|
+
}
|
|
6774
|
+
return false;
|
|
6775
|
+
}
|
|
6776
|
+
/**
|
|
6777
|
+
* Parse job data from prop - handles both object and JSON string
|
|
6778
|
+
*/
|
|
6779
|
+
getJobData() {
|
|
6780
|
+
if (!this.job)
|
|
6781
|
+
return null;
|
|
6782
|
+
if (typeof this.job === 'string') {
|
|
6783
|
+
try {
|
|
6784
|
+
return JSON.parse(this.job);
|
|
6785
|
+
}
|
|
6786
|
+
catch {
|
|
6787
|
+
console.warn('job-card: Failed to parse job JSON string');
|
|
6788
|
+
return null;
|
|
6789
|
+
}
|
|
6790
|
+
}
|
|
6791
|
+
return this.job;
|
|
6792
|
+
}
|
|
6793
|
+
render() {
|
|
6794
|
+
const job = this.getJobData();
|
|
6795
|
+
if (!job)
|
|
6796
|
+
return null;
|
|
6797
|
+
const firstLoc = getFirstLocation$1(job);
|
|
6798
|
+
const locationLabel = firstLoc ? getLocationLabel$1(firstLoc) : '';
|
|
6799
|
+
const distance = firstLoc?.distance ?? 0;
|
|
6800
|
+
const distanceLabel = distance > 0 ? this.formatDistance(distance) : '';
|
|
6801
|
+
const applyHref = job.applyURL ||
|
|
6802
|
+
(job.originalURL ? `${typeof window !== 'undefined' ? window.location.origin : ''}${job.originalURL}` : '#');
|
|
6803
|
+
const applyLabel = `${this.applyButtonText}, ${job.title || ''}`;
|
|
6804
|
+
const locs = job.locations ?? [];
|
|
6805
|
+
const hasMultipleLocations = locs.length > 1;
|
|
6806
|
+
return (hAsync("div", { class: `job-card ${this.rootClass}`.trim() }, hAsync("div", { class: "job-card__header" }, hAsync("h3", { class: "job-card__title" }, hAsync("a", { class: "job-card__title--link", href: applyHref, target: "_blank", rel: "noopener noreferrer" }, job.title || ''), this.showReference && (hAsync("span", { class: `job-card__reference ${job.reference ? '' : 'job-card__reference--empty'}` }, job.reference || '')), job.isRemote && (hAsync("span", { class: this.remoteLocationText ? 'job-card__remote' : 'job-card__remote job-card__remote--empty' }, this.remoteLocationText))), distanceLabel && (hAsync("div", { class: "job-card__distance" }, hAsync("span", { class: "job-card__distance--icon" }, hAsync("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "1.5" }, hAsync("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18z" }), hAsync("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M12 8v4l2 2" }))), hAsync("span", { class: "job-card__distance--label" }, distanceLabel)))), hAsync("div", { class: "job-card__content" }, hAsync("div", { class: "job-card__info" }, hAsync("div", { class: locs.length ? 'job-card__street' : 'job-card__street job-card__street--empty' }, hAsync("div", { class: "job-card__street--label__wrapper" }, hAsync("span", { class: "job-card__street--icon" }, hAsync("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "1.5" }, hAsync("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0z" }), hAsync("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1 1 15 0z" }))), hAsync("span", { class: "job-card__street--label" }, locationLabel || '—')), hasMultipleLocations && (hAsync("div", { class: "job-card__street--more-locations__wrapper" }, hAsync("span", { class: "job-card__street--amount" }, "+", locs.length - 1), hAsync("span", { class: "job-card__street--more-locations" }, this.multiLocationText)))), this.showBrand && (hAsync("div", { class: job.brandName ? 'job-card__brand' : 'job-card__brand job-card__brand--empty' }, hAsync("span", { class: "job-card__brand--icon" }, hAsync("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "1.5" }, hAsync("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M2.25 21h19.5m-18-18v18m10.5-18v18m6-13.5V21M6.75 6.75h.75m-.75 3h.75m-.75 3h.75m3-6h.75m-.75 3h.75m-.75 3h.75M6.75 21v-3.375c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21M3 3h12m-.75 4.5H21m-3.75 3.75h.008v.008h-.008v-.008zm0 3h.008v.008h-.008v-.008zm0 3h.008v.008h-.008v-.008z" }))), hAsync("span", { class: "job-card__brand--label" }, job.brandName || '—'))), this.showEmploymentType && (hAsync("div", { class: job.employmentType?.length
|
|
6807
|
+
? 'job-card__employment-type'
|
|
6808
|
+
: 'job-card__employment-type job-card__employment-type--empty' }, hAsync("span", { class: "job-card__employment-type--icon" }, hAsync("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "1.5" }, hAsync("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0z" }))), job.employmentType?.length ? (job.employmentType?.map((type) => (hAsync("span", { key: type, class: "job-card__employment-type--label" }, type)))) : (hAsync("span", { class: "job-card__employment-type--label" }, "\u2014")))), (job.jobCardExtraFields ?? []).length > 0 &&
|
|
6809
|
+
job.jobCardExtraFields?.map((field, idx) => (hAsync("div", { key: idx, class: !this.isEmpty(field.value) ? `${field.classname}` : `${field.classname}--empty` }, Array.isArray(field.value) ? (field.value.map((item, itemIdx) => (hAsync("span", { key: itemIdx, class: `${field.classname}--label` }, item)))) : (hAsync("span", { class: `${field.classname}--label` }, field.value)))))), hAsync("a", { class: "job-card__apply", href: applyHref, target: "_blank", rel: "noopener noreferrer", "aria-label": applyLabel }, hAsync("span", { class: "job-card__apply--label" }, this.applyButtonText), hAsync("span", { class: "job-card__apply--icon" }, hAsync("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, hAsync("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3" })))))));
|
|
6810
|
+
}
|
|
6811
|
+
static get style() { return jobCardCss(); }
|
|
6812
|
+
static get cmpMeta() { return {
|
|
6813
|
+
"$flags$": 512,
|
|
6814
|
+
"$tagName$": "job-card",
|
|
6815
|
+
"$members$": {
|
|
6816
|
+
"job": [1],
|
|
6817
|
+
"index": [2],
|
|
6818
|
+
"applyButtonText": [1, "apply-button-text"],
|
|
6819
|
+
"showBrand": [4, "show-brand"],
|
|
6820
|
+
"showReference": [4, "show-reference"],
|
|
6821
|
+
"showEmploymentType": [4, "show-employment-type"],
|
|
6822
|
+
"multiLocationText": [1, "multi-location-text"],
|
|
6823
|
+
"remoteLocationText": [1, "remote-location-text"],
|
|
6824
|
+
"enableKilometers": [4, "enable-kilometers"],
|
|
6825
|
+
"showCommuteTime": [4, "show-commute-time"],
|
|
6826
|
+
"streetFormat": [1, "street-format"],
|
|
6827
|
+
"rootClass": [1, "root-class"],
|
|
6828
|
+
"extraFieldsConfig": [16]
|
|
6829
|
+
},
|
|
6830
|
+
"$listeners$": undefined,
|
|
6831
|
+
"$lazyBundleId$": "-",
|
|
6832
|
+
"$attrsToReflect$": []
|
|
6833
|
+
}; }
|
|
6834
|
+
}
|
|
6835
|
+
|
|
6508
6836
|
const jobsItemCss = () => `.results-list__item{list-style:none;padding:10px 0;border-bottom:1px solid #ddd;margin:15px 0;display:inline-block;width:100%;position:relative}.results-list__item:last-child{border-bottom:none}.results-list__item-header{margin:10px 0;font-size:18px;font-weight:700;display:flex;flex-direction:column}.results-list__item-title{margin:0}.results-list__item-title--link{text-decoration:none;color:#1f9755}.results-list__item-title--link:hover{text-decoration:underline}.reference{margin-left:8px;font-size:0.9em;color:#666}.reference.empty{display:none}.remote{background:#f3f3f3;color:#808285;border-radius:100px;padding:6px 16px;text-transform:uppercase;font-size:12px;font-weight:700;line-height:24px;margin-left:8px}.remote--empty{display:none}.results-list__item-distance{display:inline-flex;align-items:center;gap:4px;margin-top:4px;font-size:14px;font-weight:400}.results-list__item-distance--icon{display:inline-flex}.results-list__item-distance--icon svg{width:16px;height:16px}.results-list__item-content{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-top:8px}.results-list__item-info{flex:1}.results-list__item-street,.results-list__item-brand,.results-list__item-employment-type{margin:10px 0;display:flex;flex-wrap:wrap;align-items:center;gap:4px 8px}.results-list__item-street--empty,.results-list__item-brand--empty,.results-list__item-employment-type--empty{color:#999}.results-list__item-street--icon,.results-list__item-brand--icon,.results-list__item-employment-type--icon{margin-right:6px;display:inline-flex}.results-list__item-street--icon svg,.results-list__item-brand--icon svg,.results-list__item-employment-type--icon svg{width:16px;height:16px}.results-list__item-street--more-locations__wrapper{margin-left:8px}.results-list__item-street--amount{font-weight:600}.results-list__item-apply{margin:10px 0;padding:10px 20px;display:inline-flex;align-items:center;gap:8px;background-color:#198754;color:#fff;border-radius:3px;text-decoration:none;font-weight:600;flex-shrink:0}.results-list__item-apply:hover{background-color:#1f9755;color:#fff}.results-list__item-apply--icon svg{width:14px;height:14px}`;
|
|
6509
6837
|
|
|
6510
6838
|
function getLocationLabel(loc) {
|
|
@@ -6648,6 +6976,7 @@ const defaultNoResultsLine2 = 'Please refine your keywords in the search bar abo
|
|
|
6648
6976
|
class JobsListOnly {
|
|
6649
6977
|
constructor(hostRef) {
|
|
6650
6978
|
registerInstance(this, hostRef);
|
|
6979
|
+
this.fetchComplete = createEvent(this, "fetchComplete");
|
|
6651
6980
|
}
|
|
6652
6981
|
/**
|
|
6653
6982
|
* When "true", use built-in mock data (for local/dev/docs). Otherwise use `jobs` from API/parent.
|
|
@@ -6671,16 +7000,84 @@ class JobsListOnly {
|
|
|
6671
7000
|
enableKilometers = false;
|
|
6672
7001
|
/** Extra CSS class on the root element (avoid prop name "class" / "classname" reserved). */
|
|
6673
7002
|
rootClass = '';
|
|
7003
|
+
/** Template string for count display. Tokens: {count} = jobs on page, {total} = total from API. */
|
|
7004
|
+
showCountText = '';
|
|
6674
7005
|
showSuggestions = false;
|
|
6675
7006
|
clearResultSuggestionsTitleText = 'Suggestions';
|
|
6676
7007
|
clearResultSuggestionsLine1 = 'Try different keywords';
|
|
6677
7008
|
clearResultSuggestionsLine2 = 'Make sure everything is spelled correctly';
|
|
6678
7009
|
clearResultSuggestionsLine3 = 'Try other locations';
|
|
6679
7010
|
clearResultSuggestionsLine4 = '';
|
|
7011
|
+
/** When true, component manages its own data fetching */
|
|
7012
|
+
autoFetch = false;
|
|
7013
|
+
/** Jobs search endpoint */
|
|
7014
|
+
apiUrl = '/api/get-jobs';
|
|
7015
|
+
/** Comma-separated URL param names to watch and forward to the API */
|
|
7016
|
+
watchParams = 'keyword';
|
|
7017
|
+
fetchedJobs = [];
|
|
7018
|
+
fetchedTotal = 0;
|
|
7019
|
+
fetchLoading = false;
|
|
7020
|
+
fetchComplete;
|
|
7021
|
+
searchExecutedHandler;
|
|
7022
|
+
popstateHandler;
|
|
7023
|
+
connectedCallback() {
|
|
7024
|
+
if (this.autoFetch) {
|
|
7025
|
+
this.fetchJobs();
|
|
7026
|
+
this.searchExecutedHandler = () => this.fetchJobs();
|
|
7027
|
+
this.popstateHandler = () => this.fetchJobs();
|
|
7028
|
+
document.addEventListener('search-executed', this.searchExecutedHandler);
|
|
7029
|
+
window.addEventListener('popstate', this.popstateHandler);
|
|
7030
|
+
}
|
|
7031
|
+
}
|
|
7032
|
+
disconnectedCallback() {
|
|
7033
|
+
if (this.autoFetch) {
|
|
7034
|
+
document.removeEventListener('search-executed', this.searchExecutedHandler);
|
|
7035
|
+
window.removeEventListener('popstate', this.popstateHandler);
|
|
7036
|
+
}
|
|
7037
|
+
}
|
|
7038
|
+
async fetchJobs() {
|
|
7039
|
+
this.fetchLoading = true;
|
|
7040
|
+
const params = new URLSearchParams(window.location.search);
|
|
7041
|
+
const watchList = this.watchParams.split(',').map(p => p.trim()).filter(Boolean);
|
|
7042
|
+
const query = new URLSearchParams();
|
|
7043
|
+
for (const key of watchList) {
|
|
7044
|
+
const val = params.get(key);
|
|
7045
|
+
if (val !== null)
|
|
7046
|
+
query.set(key, val);
|
|
7047
|
+
}
|
|
7048
|
+
const url = `${this.apiUrl}?${query.toString()}`;
|
|
7049
|
+
try {
|
|
7050
|
+
const res = await fetch(url, {
|
|
7051
|
+
method: 'POST',
|
|
7052
|
+
headers: { 'Content-Type': 'application/json' },
|
|
7053
|
+
body: JSON.stringify({ disable_switch_search_mode: false }),
|
|
7054
|
+
});
|
|
7055
|
+
if (!res.ok)
|
|
7056
|
+
throw new Error('fetch failed');
|
|
7057
|
+
const data = await res.json();
|
|
7058
|
+
this.fetchedJobs = data.jobs;
|
|
7059
|
+
this.fetchedTotal = data.totalJob;
|
|
7060
|
+
this.fetchComplete.emit({ jobs: data.jobs, totalJob: data.totalJob });
|
|
7061
|
+
}
|
|
7062
|
+
catch {
|
|
7063
|
+
// preserve stale data, just stop loading
|
|
7064
|
+
}
|
|
7065
|
+
finally {
|
|
7066
|
+
this.fetchLoading = false;
|
|
7067
|
+
}
|
|
7068
|
+
}
|
|
7069
|
+
renderCountText(count, total) {
|
|
7070
|
+
return this.showCountText
|
|
7071
|
+
.replace('{count}', String(count))
|
|
7072
|
+
.replace('{total}', String(total));
|
|
7073
|
+
}
|
|
6680
7074
|
getJobsArray() {
|
|
6681
7075
|
if (this.mockData) {
|
|
6682
7076
|
return mockJobsListOnly;
|
|
6683
7077
|
}
|
|
7078
|
+
if (this.autoFetch) {
|
|
7079
|
+
return this.fetchedJobs;
|
|
7080
|
+
}
|
|
6684
7081
|
const j = this.jobs;
|
|
6685
7082
|
if (Array.isArray(j))
|
|
6686
7083
|
return j;
|
|
@@ -6700,11 +7097,15 @@ class JobsListOnly {
|
|
|
6700
7097
|
}
|
|
6701
7098
|
render() {
|
|
6702
7099
|
const jobsArray = this.getJobsArray();
|
|
6703
|
-
const loading = this.mockData ? false : this.loading;
|
|
6704
|
-
const totalJob = this.mockData
|
|
7100
|
+
const loading = this.mockData ? false : (this.autoFetch ? this.fetchLoading : this.loading);
|
|
7101
|
+
const totalJob = this.mockData
|
|
7102
|
+
? jobsArray.length
|
|
7103
|
+
: this.autoFetch
|
|
7104
|
+
? this.fetchedTotal
|
|
7105
|
+
: (this.totalJob || jobsArray.length);
|
|
6705
7106
|
const showNoResults = !loading && totalJob === 0 && !this.showSuggestions;
|
|
6706
7107
|
const showSuggestionsBlock = !loading && totalJob === 0 && this.showSuggestions;
|
|
6707
|
-
return (hAsync("div", { key: '
|
|
7108
|
+
return (hAsync("div", { key: '1116855473d28d650641b9d962243bfcdcb434ec', class: `jobs-list-root ${this.rootClass}`.trim() }, hAsync("div", { key: 'fcef04f1da9ad4e150af9f59921688f5781d9d43', class: "results-container" }, this.autoFetch && this.fetchLoading && (hAsync("div", { key: '75b157c82c89691c7ce73d12ea0144b3b45485c3', class: "jobs-list-only__loading" }, "Loading...")), hAsync("div", { key: 'b09f9879e10ee4a93e32177611912da5f19f3526', class: loading ? 'loader' : 'loader hide', "aria-hidden": !loading }), totalJob > 0 && this.showCountText && (hAsync("p", { key: 'd10f800fb0a33d82531d5f1728bac4ceba2ed577', class: "jobs-list-only__count" }, this.renderCountText(jobsArray.length, totalJob))), totalJob > 0 && (hAsync("div", { key: '18153ed1338bd48f7be4f043b11ce15e3271f27b', class: "card" }, hAsync("ul", { key: '766e128b1fd5adb456530ae39e92ba8eb0b5d6cf', class: "results-list front" }, jobsArray.map((job, index) => this.renderJobItem(job, index))))), showNoResults && (hAsync("div", { key: 'ed6f3d2bd2bbedabd6e69d508ea1425580e6941f', class: "share-jobs__no-results" }, hAsync("h2", { key: '2302656e33340c69e84cb949afb7256b8f35f440' }, this.noResultsLine1), hAsync("h3", { key: '1c7e6642441a96c04ee26883fdec4f81b0fe6cec' }, this.noResultsLine2))), showSuggestionsBlock && (hAsync("div", { key: 'be7af85f64455918545e88952ca6ff00f0a970c5', class: "card primary-color" }, hAsync("h4", { key: '2f63deb8131190eff882308544b15f767b6f3edc', class: "result-suggestions-title" }, this.clearResultSuggestionsTitleText, ":"), hAsync("ul", { key: 'cb18daaa9e2c2c442c5b906ed370dcd653b5262d', class: "results-list front" }, hAsync("li", { key: 'ff1d5c6518b75c0daa35b43df7162b0dfecde25e', class: "result-suggestions-line" }, this.clearResultSuggestionsLine1), hAsync("li", { key: '4099fd7bf8dcf114eca28702a498ab0938f5de46', class: "result-suggestions-line" }, this.clearResultSuggestionsLine2), hAsync("li", { key: 'fb65b54c3c0b14bc58112977eb4c7c56c1246a45', class: "result-suggestions-line" }, this.clearResultSuggestionsLine3), this.clearResultSuggestionsLine4 && (hAsync("li", { key: '10f745e74cf68a2b1c42e6f49f810a8b59eb27b6', class: "result-suggestions-line" }, this.clearResultSuggestionsLine4))))))));
|
|
6708
7109
|
}
|
|
6709
7110
|
static get style() { return jobsListOnlyCss(); }
|
|
6710
7111
|
static get cmpMeta() { return {
|
|
@@ -6726,12 +7127,136 @@ class JobsListOnly {
|
|
|
6726
7127
|
"remoteLocationText": [1, "remote-location-text"],
|
|
6727
7128
|
"enableKilometers": [4, "enable-kilometers"],
|
|
6728
7129
|
"rootClass": [1, "root-class"],
|
|
7130
|
+
"showCountText": [1, "show-count-text"],
|
|
6729
7131
|
"showSuggestions": [4, "show-suggestions"],
|
|
6730
7132
|
"clearResultSuggestionsTitleText": [1, "clear-result-suggestions-title-text"],
|
|
6731
7133
|
"clearResultSuggestionsLine1": [1, "clear-result-suggestions-line-1"],
|
|
6732
7134
|
"clearResultSuggestionsLine2": [1, "clear-result-suggestions-line-2"],
|
|
6733
7135
|
"clearResultSuggestionsLine3": [1, "clear-result-suggestions-line-3"],
|
|
6734
|
-
"clearResultSuggestionsLine4": [1, "clear-result-suggestions-line-4"]
|
|
7136
|
+
"clearResultSuggestionsLine4": [1, "clear-result-suggestions-line-4"],
|
|
7137
|
+
"autoFetch": [4, "auto-fetch"],
|
|
7138
|
+
"apiUrl": [1, "api-url"],
|
|
7139
|
+
"watchParams": [1, "watch-params"],
|
|
7140
|
+
"fetchedJobs": [32],
|
|
7141
|
+
"fetchedTotal": [32],
|
|
7142
|
+
"fetchLoading": [32]
|
|
7143
|
+
},
|
|
7144
|
+
"$listeners$": undefined,
|
|
7145
|
+
"$lazyBundleId$": "-",
|
|
7146
|
+
"$attrsToReflect$": []
|
|
7147
|
+
}; }
|
|
7148
|
+
}
|
|
7149
|
+
|
|
7150
|
+
const jobsListReactiveCss = () => `jobs-list-reactive{display:block}jobs-list-reactive.loading{opacity:0.6;pointer-events:none}`;
|
|
7151
|
+
|
|
7152
|
+
class JobsListReactive {
|
|
7153
|
+
constructor(hostRef) {
|
|
7154
|
+
registerInstance(this, hostRef);
|
|
7155
|
+
this.fetchComplete = createEvent(this, "fetchComplete");
|
|
7156
|
+
}
|
|
7157
|
+
get el() { return getElement(this); }
|
|
7158
|
+
/** Jobs search endpoint */
|
|
7159
|
+
apiUrl = '/api/get-jobs';
|
|
7160
|
+
/** Comma-separated URL param names to watch and forward to the API */
|
|
7161
|
+
watchParams = 'keyword,location_name';
|
|
7162
|
+
/** CSS class added to container while fetching */
|
|
7163
|
+
loadingClass = 'loading';
|
|
7164
|
+
isLoading = false;
|
|
7165
|
+
fetchComplete;
|
|
7166
|
+
templateEl = null;
|
|
7167
|
+
searchExecutedHandler;
|
|
7168
|
+
popstateHandler;
|
|
7169
|
+
connectedCallback() {
|
|
7170
|
+
this.templateEl = this.el.querySelector('template');
|
|
7171
|
+
this.searchExecutedHandler = () => this.fetchJobs();
|
|
7172
|
+
this.popstateHandler = () => this.fetchJobs();
|
|
7173
|
+
document.addEventListener('search-executed', this.searchExecutedHandler);
|
|
7174
|
+
window.addEventListener('popstate', this.popstateHandler);
|
|
7175
|
+
}
|
|
7176
|
+
disconnectedCallback() {
|
|
7177
|
+
document.removeEventListener('search-executed', this.searchExecutedHandler);
|
|
7178
|
+
window.removeEventListener('popstate', this.popstateHandler);
|
|
7179
|
+
}
|
|
7180
|
+
buildQueryString() {
|
|
7181
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
7182
|
+
const watchList = this.watchParams.split(',').map(p => p.trim()).filter(Boolean);
|
|
7183
|
+
const query = new URLSearchParams();
|
|
7184
|
+
for (const key of watchList) {
|
|
7185
|
+
const val = urlParams.get(key);
|
|
7186
|
+
if (val !== null && val !== '') {
|
|
7187
|
+
query.set(key, val);
|
|
7188
|
+
}
|
|
7189
|
+
}
|
|
7190
|
+
return query.toString();
|
|
7191
|
+
}
|
|
7192
|
+
async fetchJobs() {
|
|
7193
|
+
this.isLoading = true;
|
|
7194
|
+
this.el.classList.add(this.loadingClass);
|
|
7195
|
+
const queryString = this.buildQueryString();
|
|
7196
|
+
const url = queryString ? `${this.apiUrl}?${queryString}` : this.apiUrl;
|
|
7197
|
+
try {
|
|
7198
|
+
const res = await fetch(url, {
|
|
7199
|
+
method: 'POST',
|
|
7200
|
+
headers: { 'Content-Type': 'application/json' },
|
|
7201
|
+
body: JSON.stringify({ disable_switch_search_mode: false }),
|
|
7202
|
+
});
|
|
7203
|
+
if (!res.ok)
|
|
7204
|
+
throw new Error('fetch failed');
|
|
7205
|
+
const data = await res.json();
|
|
7206
|
+
this.renderJobs(data.jobs);
|
|
7207
|
+
this.updateCountElements(data.jobs.length, data.totalJob);
|
|
7208
|
+
this.fetchComplete.emit({ jobs: data.jobs, totalJob: data.totalJob });
|
|
7209
|
+
}
|
|
7210
|
+
catch {
|
|
7211
|
+
// Preserve stale data on error
|
|
7212
|
+
}
|
|
7213
|
+
finally {
|
|
7214
|
+
this.isLoading = false;
|
|
7215
|
+
this.el.classList.remove(this.loadingClass);
|
|
7216
|
+
}
|
|
7217
|
+
}
|
|
7218
|
+
renderJobs(jobs) {
|
|
7219
|
+
if (!this.templateEl)
|
|
7220
|
+
return;
|
|
7221
|
+
// Remove all children except the template
|
|
7222
|
+
const children = Array.from(this.el.children);
|
|
7223
|
+
for (const child of children) {
|
|
7224
|
+
if (child !== this.templateEl) {
|
|
7225
|
+
child.remove();
|
|
7226
|
+
}
|
|
7227
|
+
}
|
|
7228
|
+
// Clone template and render each job
|
|
7229
|
+
for (const job of jobs) {
|
|
7230
|
+
const clone = this.templateEl.content.cloneNode(true);
|
|
7231
|
+
const jobCard = clone.querySelector('job-card');
|
|
7232
|
+
if (jobCard) {
|
|
7233
|
+
jobCard.setAttribute('job', JSON.stringify(job));
|
|
7234
|
+
}
|
|
7235
|
+
this.el.appendChild(clone);
|
|
7236
|
+
}
|
|
7237
|
+
}
|
|
7238
|
+
updateCountElements(count, total) {
|
|
7239
|
+
const countEls = document.querySelectorAll('[data-job-count]');
|
|
7240
|
+
const totalEls = document.querySelectorAll('[data-job-total]');
|
|
7241
|
+
countEls.forEach(el => {
|
|
7242
|
+
el.textContent = String(count);
|
|
7243
|
+
});
|
|
7244
|
+
totalEls.forEach(el => {
|
|
7245
|
+
el.textContent = String(total);
|
|
7246
|
+
});
|
|
7247
|
+
}
|
|
7248
|
+
render() {
|
|
7249
|
+
return hAsync("slot", { key: '30a6fe9727eb877b6aafb99072c40811df121ba6' });
|
|
7250
|
+
}
|
|
7251
|
+
static get style() { return jobsListReactiveCss(); }
|
|
7252
|
+
static get cmpMeta() { return {
|
|
7253
|
+
"$flags$": 772,
|
|
7254
|
+
"$tagName$": "jobs-list-reactive",
|
|
7255
|
+
"$members$": {
|
|
7256
|
+
"apiUrl": [1, "api-url"],
|
|
7257
|
+
"watchParams": [1, "watch-params"],
|
|
7258
|
+
"loadingClass": [1, "loading-class"],
|
|
7259
|
+
"isLoading": [32]
|
|
6735
7260
|
},
|
|
6736
7261
|
"$listeners$": undefined,
|
|
6737
7262
|
"$lazyBundleId$": "-",
|
|
@@ -6742,8 +7267,11 @@ class JobsListOnly {
|
|
|
6742
7267
|
registerComponents([
|
|
6743
7268
|
AppCarousel,
|
|
6744
7269
|
CustomButton,
|
|
7270
|
+
FastInput,
|
|
7271
|
+
JobCard,
|
|
6745
7272
|
JobsItem,
|
|
6746
7273
|
JobsListOnly,
|
|
7274
|
+
JobsListReactive,
|
|
6747
7275
|
]);
|
|
6748
7276
|
|
|
6749
7277
|
exports.hydrateApp = hydrateApp;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phatvu/web-component-poc",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "Stencil Component Starter",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -32,6 +32,10 @@
|
|
|
32
32
|
"require": "./loader/index.cjs",
|
|
33
33
|
"types": "./loader/index.d.ts"
|
|
34
34
|
},
|
|
35
|
+
"./fast-input": {
|
|
36
|
+
"import": "./dist/components/fast-input.js",
|
|
37
|
+
"types": "./dist/components/fast-input.d.ts"
|
|
38
|
+
},
|
|
35
39
|
"./jobs-list-only": {
|
|
36
40
|
"import": "./dist/components/jobs-list-only.js",
|
|
37
41
|
"types": "./dist/components/jobs-list-only.d.ts"
|
|
@@ -39,6 +43,10 @@
|
|
|
39
43
|
"./jobs-item": {
|
|
40
44
|
"import": "./dist/components/jobs-item.js",
|
|
41
45
|
"types": "./dist/components/jobs-item.d.ts"
|
|
46
|
+
},
|
|
47
|
+
"./job-card": {
|
|
48
|
+
"import": "./dist/components/job-card.js",
|
|
49
|
+
"types": "./dist/components/job-card.d.ts"
|
|
42
50
|
}
|
|
43
51
|
},
|
|
44
52
|
"repository": {
|