@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.
Files changed (55) hide show
  1. package/dist/cjs/fast-button.cjs.entry.js +46 -0
  2. package/dist/cjs/{fast-button_4.cjs.entry.js → fast-carousel.cjs.entry.js} +1 -231
  3. package/dist/cjs/fast-input_4.cjs.entry.js +499 -0
  4. package/dist/cjs/{index-B2BTpdbN.js → index-BEvZs91D.js} +2 -2
  5. package/dist/cjs/job-card.cjs.entry.js +138 -0
  6. package/dist/cjs/loader.cjs.js +2 -2
  7. package/dist/cjs/web-component-poc.cjs.js +2 -2
  8. package/dist/collection/collection-manifest.json +4 -1
  9. package/dist/collection/components/button/button.js +2 -2
  10. package/dist/collection/components/fast-input/fast-input.css +55 -0
  11. package/dist/collection/components/fast-input/fast-input.js +335 -0
  12. package/dist/collection/components/job-card/job-card.css +247 -0
  13. package/dist/collection/components/job-card/job-card.js +435 -0
  14. package/dist/collection/components/jobs-item/jobs-item.js +5 -5
  15. package/dist/collection/components/jobs-list-only/jobs-list-only.js +185 -8
  16. package/dist/collection/components/jobs-list-reactive/jobs-list-reactive.css +8 -0
  17. package/dist/collection/components/jobs-list-reactive/jobs-list-reactive.js +203 -0
  18. package/dist/components/fast-button.js +1 -1
  19. package/dist/components/fast-carousel.js +1 -1
  20. package/dist/components/fast-input.d.ts +11 -0
  21. package/dist/components/fast-input.js +1 -0
  22. package/dist/components/index.js +1 -1
  23. package/dist/components/job-card.d.ts +11 -0
  24. package/dist/components/job-card.js +1 -0
  25. package/dist/components/jobs-item.js +1 -1
  26. package/dist/components/jobs-list-only.js +1 -1
  27. package/dist/components/jobs-list-reactive.d.ts +11 -0
  28. package/dist/components/jobs-list-reactive.js +1 -0
  29. package/dist/components/{p-ClQDwJJB.js → p-DQiaLjLf.js} +1 -1
  30. package/dist/esm/fast-button.entry.js +44 -0
  31. package/dist/esm/{fast-button_4.entry.js → fast-carousel.entry.js} +2 -229
  32. package/dist/esm/fast-input_4.entry.js +494 -0
  33. package/dist/esm/{index-Dk5CvWmb.js → index-C_ZLQIpp.js} +2 -2
  34. package/dist/esm/job-card.entry.js +136 -0
  35. package/dist/esm/loader.js +3 -3
  36. package/dist/esm/web-component-poc.js +3 -3
  37. package/dist/types/components/fast-input/fast-input.d.ts +37 -0
  38. package/dist/types/components/job-card/job-card.d.ts +93 -0
  39. package/dist/types/components/jobs-item/jobs-item.d.ts +2 -2
  40. package/dist/types/components/jobs-list-only/jobs-list-only.d.ts +24 -2
  41. package/dist/types/components/jobs-list-reactive/jobs-list-reactive.d.ts +26 -0
  42. package/dist/types/components.d.ts +469 -7
  43. package/dist/types/mock/jobs-list-only.mock.d.ts +2 -2
  44. package/dist/types/types/jobs-list.d.ts +6 -2
  45. package/dist/web-component-poc/p-618fba28.entry.js +1 -0
  46. package/dist/web-component-poc/p-7d45772f.entry.js +1 -0
  47. package/dist/web-component-poc/p-bef7c8e2.entry.js +1 -0
  48. package/dist/web-component-poc/p-cfb9aed9.entry.js +1 -0
  49. package/dist/web-component-poc/web-component-poc.esm.js +1 -1
  50. package/hydrate/index.js +534 -6
  51. package/hydrate/index.mjs +534 -6
  52. package/package.json +9 -1
  53. package/dist/web-component-poc/p-df843533.entry.js +0 -1
  54. /package/dist/components/{p-UM9TUfe3.js → p-BiaJAQXY.js} +0 -0
  55. /package/dist/web-component-poc/{p-Dk5CvWmb.js → p-C_ZLQIpp.js} +0 -0
@@ -0,0 +1,138 @@
1
+ 'use strict';
2
+
3
+ var index = require('./index-BEvZs91D.js');
4
+
5
+ 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}}`;
6
+
7
+ /**
8
+ * Helper function to get formatted location label from location object
9
+ */
10
+ function getLocationLabel(loc) {
11
+ if (loc.cityStateAbbr)
12
+ return loc.cityStateAbbr;
13
+ const parts = [loc.streetAddress, loc.city, loc.stateAbbr || loc.state, loc.countryAbbr || loc.country].filter(Boolean);
14
+ return parts.join(', ') || loc.locationText || '';
15
+ }
16
+ /**
17
+ * Helper function to get the first location from job locations
18
+ */
19
+ function getFirstLocation(job) {
20
+ const locs = job.locations;
21
+ if (!locs?.length)
22
+ return undefined;
23
+ return locs[0];
24
+ }
25
+ const JobCard = class {
26
+ constructor(hostRef) {
27
+ index.registerInstance(this, hostRef);
28
+ }
29
+ /**
30
+ * The job data object to display. Accepts either a Job object or a JSON string.
31
+ */
32
+ job;
33
+ /**
34
+ * Index of the job in a list (used for accessibility)
35
+ */
36
+ index = 0;
37
+ /**
38
+ * Text for the apply button
39
+ */
40
+ applyButtonText = 'Apply Now';
41
+ /**
42
+ * Whether to show the brand/company name
43
+ */
44
+ showBrand = true;
45
+ /**
46
+ * Whether to show the reference number
47
+ */
48
+ showReference = false;
49
+ /**
50
+ * Whether to show employment type
51
+ */
52
+ showEmploymentType = true;
53
+ /**
54
+ * Text shown for multiple locations
55
+ */
56
+ multiLocationText = 'More locations';
57
+ /**
58
+ * Text shown for remote jobs
59
+ */
60
+ remoteLocationText = 'Remote';
61
+ /**
62
+ * Whether to show distances in kilometers instead of miles
63
+ */
64
+ enableKilometers = false;
65
+ /**
66
+ * Whether to show commute time
67
+ */
68
+ showCommuteTime = false;
69
+ /**
70
+ * Format string for street address (not used in base web component)
71
+ */
72
+ streetFormat = '{street}, {city_state_abbr}';
73
+ /** Extra CSS class on the root inner element (avoid prop name "className", reserved on HTMLElement). */
74
+ rootClass = '';
75
+ /**
76
+ * Custom extra fields configuration
77
+ */
78
+ extraFieldsConfig = [];
79
+ /**
80
+ * Format distance with unit
81
+ */
82
+ formatDistance(distance) {
83
+ const value = this.enableKilometers ? distance * 1.60934 : distance;
84
+ const unit = this.enableKilometers ? 'Km' : 'Miles';
85
+ const str = `${value.toFixed(1)} ${unit}`.replace('.0', '');
86
+ return str;
87
+ }
88
+ /**
89
+ * Is Empty utility
90
+ */
91
+ isEmpty(value) {
92
+ if (value === null || value === undefined || value === '') {
93
+ return true;
94
+ }
95
+ if (Array.isArray(value) && value.length === 0) {
96
+ return true;
97
+ }
98
+ return false;
99
+ }
100
+ /**
101
+ * Parse job data from prop - handles both object and JSON string
102
+ */
103
+ getJobData() {
104
+ if (!this.job)
105
+ return null;
106
+ if (typeof this.job === 'string') {
107
+ try {
108
+ return JSON.parse(this.job);
109
+ }
110
+ catch {
111
+ console.warn('job-card: Failed to parse job JSON string');
112
+ return null;
113
+ }
114
+ }
115
+ return this.job;
116
+ }
117
+ render() {
118
+ const job = this.getJobData();
119
+ if (!job)
120
+ return null;
121
+ const firstLoc = getFirstLocation(job);
122
+ const locationLabel = firstLoc ? getLocationLabel(firstLoc) : '';
123
+ const distance = firstLoc?.distance ?? 0;
124
+ const distanceLabel = distance > 0 ? this.formatDistance(distance) : '';
125
+ const applyHref = job.applyURL ||
126
+ (job.originalURL ? `${typeof window !== 'undefined' ? window.location.origin : ''}${job.originalURL}` : '#');
127
+ const applyLabel = `${this.applyButtonText}, ${job.title || ''}`;
128
+ const locs = job.locations ?? [];
129
+ const hasMultipleLocations = locs.length > 1;
130
+ return (index.h("div", { class: `job-card ${this.rootClass}`.trim() }, index.h("div", { class: "job-card__header" }, index.h("h3", { class: "job-card__title" }, index.h("a", { class: "job-card__title--link", href: applyHref, target: "_blank", rel: "noopener noreferrer" }, job.title || ''), this.showReference && (index.h("span", { class: `job-card__reference ${job.reference ? '' : 'job-card__reference--empty'}` }, job.reference || '')), job.isRemote && (index.h("span", { class: this.remoteLocationText ? 'job-card__remote' : 'job-card__remote job-card__remote--empty' }, this.remoteLocationText))), distanceLabel && (index.h("div", { class: "job-card__distance" }, index.h("span", { class: "job-card__distance--icon" }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "1.5" }, index.h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18z" }), index.h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M12 8v4l2 2" }))), index.h("span", { class: "job-card__distance--label" }, distanceLabel)))), index.h("div", { class: "job-card__content" }, index.h("div", { class: "job-card__info" }, index.h("div", { class: locs.length ? 'job-card__street' : 'job-card__street job-card__street--empty' }, index.h("div", { class: "job-card__street--label__wrapper" }, index.h("span", { class: "job-card__street--icon" }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "1.5" }, index.h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0z" }), index.h("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" }))), index.h("span", { class: "job-card__street--label" }, locationLabel || '—')), hasMultipleLocations && (index.h("div", { class: "job-card__street--more-locations__wrapper" }, index.h("span", { class: "job-card__street--amount" }, "+", locs.length - 1), index.h("span", { class: "job-card__street--more-locations" }, this.multiLocationText)))), this.showBrand && (index.h("div", { class: job.brandName ? 'job-card__brand' : 'job-card__brand job-card__brand--empty' }, index.h("span", { class: "job-card__brand--icon" }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "1.5" }, index.h("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" }))), index.h("span", { class: "job-card__brand--label" }, job.brandName || '—'))), this.showEmploymentType && (index.h("div", { class: job.employmentType?.length
131
+ ? 'job-card__employment-type'
132
+ : 'job-card__employment-type job-card__employment-type--empty' }, index.h("span", { class: "job-card__employment-type--icon" }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "1.5" }, index.h("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) => (index.h("span", { key: type, class: "job-card__employment-type--label" }, type)))) : (index.h("span", { class: "job-card__employment-type--label" }, "\u2014")))), (job.jobCardExtraFields ?? []).length > 0 &&
133
+ job.jobCardExtraFields?.map((field, idx) => (index.h("div", { key: idx, class: !this.isEmpty(field.value) ? `${field.classname}` : `${field.classname}--empty` }, Array.isArray(field.value) ? (field.value.map((item, itemIdx) => (index.h("span", { key: itemIdx, class: `${field.classname}--label` }, item)))) : (index.h("span", { class: `${field.classname}--label` }, field.value)))))), index.h("a", { class: "job-card__apply", href: applyHref, target: "_blank", rel: "noopener noreferrer", "aria-label": applyLabel }, index.h("span", { class: "job-card__apply--label" }, this.applyButtonText), index.h("span", { class: "job-card__apply--icon" }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, index.h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3" })))))));
134
+ }
135
+ };
136
+ JobCard.style = jobCardCss();
137
+
138
+ exports.job_card = JobCard;
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-B2BTpdbN.js');
3
+ var index = require('./index-BEvZs91D.js');
4
4
  var appGlobals = require('./app-globals-V2Kpy_OQ.js');
5
5
 
6
6
  const defineCustomElements = async (win, options) => {
7
7
  if (typeof window === 'undefined') return undefined;
8
8
  await appGlobals.globalScripts();
9
- return index.bootstrapLazy([["fast-button_4.cjs",[[512,"jobs-list-only",{"mockData":[4,"mock-data"],"jobs":[1],"loading":[4],"totalJob":[2,"total-job"],"noResultsLine1":[1,"no-results-line-1"],"noResultsLine2":[1,"no-results-line-2"],"applyButtonText":[1,"apply-button-text"],"showBrand":[4,"show-brand"],"showReference":[4,"show-reference"],"showEmploymentType":[4,"show-employment-type"],"streetFormat":[1,"street-format"],"multiLocationText":[1,"multi-location-text"],"remoteLocationText":[1,"remote-location-text"],"enableKilometers":[4,"enable-kilometers"],"rootClass":[1,"root-class"],"showSuggestions":[4,"show-suggestions"],"clearResultSuggestionsTitleText":[1,"clear-result-suggestions-title-text"],"clearResultSuggestionsLine1":[1,"clear-result-suggestions-line-1"],"clearResultSuggestionsLine2":[1,"clear-result-suggestions-line-2"],"clearResultSuggestionsLine3":[1,"clear-result-suggestions-line-3"],"clearResultSuggestionsLine4":[1,"clear-result-suggestions-line-4"]}],[772,"fast-button",{"variant":[1],"type":[1],"disabled":[4]}],[772,"fast-carousel",{"items":[1],"loop":[4],"class":[1],"controlClass":[1,"control-class"],"slideClass":[1,"slide-class"],"itemClass":[1,"item-class"],"scrollPrev":[64],"scrollNext":[64],"goToSlide":[64],"getEmbla":[64]}],[512,"jobs-item",{"job":[16],"index":[2],"applyButtonText":[1,"apply-button-text"],"showBrand":[4,"show-brand"],"showReference":[4,"show-reference"],"showEmploymentType":[4,"show-employment-type"],"multiLocationText":[1,"multi-location-text"],"remoteLocationText":[1,"remote-location-text"],"enableKilometers":[4,"enable-kilometers"]}]]]], options);
9
+ return index.bootstrapLazy([["fast-button.cjs",[[772,"fast-button",{"variant":[1],"type":[1],"disabled":[4]}]]],["fast-carousel.cjs",[[772,"fast-carousel",{"items":[1],"loop":[4],"class":[1],"controlClass":[1,"control-class"],"slideClass":[1,"slide-class"],"itemClass":[1,"item-class"],"scrollPrev":[64],"scrollNext":[64],"goToSlide":[64],"getEmbla":[64]}]]],["job-card.cjs",[[512,"job-card",{"job":[1],"index":[2],"applyButtonText":[1,"apply-button-text"],"showBrand":[4,"show-brand"],"showReference":[4,"show-reference"],"showEmploymentType":[4,"show-employment-type"],"multiLocationText":[1,"multi-location-text"],"remoteLocationText":[1,"remote-location-text"],"enableKilometers":[4,"enable-kilometers"],"showCommuteTime":[4,"show-commute-time"],"streetFormat":[1,"street-format"],"rootClass":[1,"root-class"],"extraFieldsConfig":[16]}]]],["fast-input_4.cjs",[[512,"jobs-list-only",{"mockData":[4,"mock-data"],"jobs":[1],"loading":[4],"totalJob":[2,"total-job"],"noResultsLine1":[1,"no-results-line-1"],"noResultsLine2":[1,"no-results-line-2"],"applyButtonText":[1,"apply-button-text"],"showBrand":[4,"show-brand"],"showReference":[4,"show-reference"],"showEmploymentType":[4,"show-employment-type"],"streetFormat":[1,"street-format"],"multiLocationText":[1,"multi-location-text"],"remoteLocationText":[1,"remote-location-text"],"enableKilometers":[4,"enable-kilometers"],"rootClass":[1,"root-class"],"showCountText":[1,"show-count-text"],"showSuggestions":[4,"show-suggestions"],"clearResultSuggestionsTitleText":[1,"clear-result-suggestions-title-text"],"clearResultSuggestionsLine1":[1,"clear-result-suggestions-line-1"],"clearResultSuggestionsLine2":[1,"clear-result-suggestions-line-2"],"clearResultSuggestionsLine3":[1,"clear-result-suggestions-line-3"],"clearResultSuggestionsLine4":[1,"clear-result-suggestions-line-4"],"autoFetch":[4,"auto-fetch"],"apiUrl":[1,"api-url"],"watchParams":[1,"watch-params"],"fetchedJobs":[32],"fetchedTotal":[32],"fetchLoading":[32]}],[512,"fast-input",{"placeholder":[1],"value":[1],"paramName":[1,"param-name"],"enableAutocomplete":[4,"enable-autocomplete"],"autocompleteUrl":[1,"autocomplete-url"],"targetPath":[1,"target-path"],"debounceMs":[2,"debounce-ms"],"minChars":[2,"min-chars"],"inputValue":[32],"suggestions":[32],"showDropdown":[32],"autocompleteLoading":[32]}],[772,"jobs-list-reactive",{"apiUrl":[1,"api-url"],"watchParams":[1,"watch-params"],"loadingClass":[1,"loading-class"],"isLoading":[32]}],[512,"jobs-item",{"job":[16],"index":[2],"applyButtonText":[1,"apply-button-text"],"showBrand":[4,"show-brand"],"showReference":[4,"show-reference"],"showEmploymentType":[4,"show-employment-type"],"multiLocationText":[1,"multi-location-text"],"remoteLocationText":[1,"remote-location-text"],"enableKilometers":[4,"enable-kilometers"]}]]]], options);
10
10
  };
11
11
 
12
12
  exports.setNonce = index.setNonce;
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-B2BTpdbN.js');
3
+ var index = require('./index-BEvZs91D.js');
4
4
  var appGlobals = require('./app-globals-V2Kpy_OQ.js');
5
5
 
6
6
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
@@ -19,7 +19,7 @@ var patchBrowser = () => {
19
19
 
20
20
  patchBrowser().then(async (options) => {
21
21
  await appGlobals.globalScripts();
22
- return index.bootstrapLazy([["fast-button_4.cjs",[[512,"jobs-list-only",{"mockData":[4,"mock-data"],"jobs":[1],"loading":[4],"totalJob":[2,"total-job"],"noResultsLine1":[1,"no-results-line-1"],"noResultsLine2":[1,"no-results-line-2"],"applyButtonText":[1,"apply-button-text"],"showBrand":[4,"show-brand"],"showReference":[4,"show-reference"],"showEmploymentType":[4,"show-employment-type"],"streetFormat":[1,"street-format"],"multiLocationText":[1,"multi-location-text"],"remoteLocationText":[1,"remote-location-text"],"enableKilometers":[4,"enable-kilometers"],"rootClass":[1,"root-class"],"showSuggestions":[4,"show-suggestions"],"clearResultSuggestionsTitleText":[1,"clear-result-suggestions-title-text"],"clearResultSuggestionsLine1":[1,"clear-result-suggestions-line-1"],"clearResultSuggestionsLine2":[1,"clear-result-suggestions-line-2"],"clearResultSuggestionsLine3":[1,"clear-result-suggestions-line-3"],"clearResultSuggestionsLine4":[1,"clear-result-suggestions-line-4"]}],[772,"fast-button",{"variant":[1],"type":[1],"disabled":[4]}],[772,"fast-carousel",{"items":[1],"loop":[4],"class":[1],"controlClass":[1,"control-class"],"slideClass":[1,"slide-class"],"itemClass":[1,"item-class"],"scrollPrev":[64],"scrollNext":[64],"goToSlide":[64],"getEmbla":[64]}],[512,"jobs-item",{"job":[16],"index":[2],"applyButtonText":[1,"apply-button-text"],"showBrand":[4,"show-brand"],"showReference":[4,"show-reference"],"showEmploymentType":[4,"show-employment-type"],"multiLocationText":[1,"multi-location-text"],"remoteLocationText":[1,"remote-location-text"],"enableKilometers":[4,"enable-kilometers"]}]]]], options);
22
+ return index.bootstrapLazy([["fast-button.cjs",[[772,"fast-button",{"variant":[1],"type":[1],"disabled":[4]}]]],["fast-carousel.cjs",[[772,"fast-carousel",{"items":[1],"loop":[4],"class":[1],"controlClass":[1,"control-class"],"slideClass":[1,"slide-class"],"itemClass":[1,"item-class"],"scrollPrev":[64],"scrollNext":[64],"goToSlide":[64],"getEmbla":[64]}]]],["job-card.cjs",[[512,"job-card",{"job":[1],"index":[2],"applyButtonText":[1,"apply-button-text"],"showBrand":[4,"show-brand"],"showReference":[4,"show-reference"],"showEmploymentType":[4,"show-employment-type"],"multiLocationText":[1,"multi-location-text"],"remoteLocationText":[1,"remote-location-text"],"enableKilometers":[4,"enable-kilometers"],"showCommuteTime":[4,"show-commute-time"],"streetFormat":[1,"street-format"],"rootClass":[1,"root-class"],"extraFieldsConfig":[16]}]]],["fast-input_4.cjs",[[512,"jobs-list-only",{"mockData":[4,"mock-data"],"jobs":[1],"loading":[4],"totalJob":[2,"total-job"],"noResultsLine1":[1,"no-results-line-1"],"noResultsLine2":[1,"no-results-line-2"],"applyButtonText":[1,"apply-button-text"],"showBrand":[4,"show-brand"],"showReference":[4,"show-reference"],"showEmploymentType":[4,"show-employment-type"],"streetFormat":[1,"street-format"],"multiLocationText":[1,"multi-location-text"],"remoteLocationText":[1,"remote-location-text"],"enableKilometers":[4,"enable-kilometers"],"rootClass":[1,"root-class"],"showCountText":[1,"show-count-text"],"showSuggestions":[4,"show-suggestions"],"clearResultSuggestionsTitleText":[1,"clear-result-suggestions-title-text"],"clearResultSuggestionsLine1":[1,"clear-result-suggestions-line-1"],"clearResultSuggestionsLine2":[1,"clear-result-suggestions-line-2"],"clearResultSuggestionsLine3":[1,"clear-result-suggestions-line-3"],"clearResultSuggestionsLine4":[1,"clear-result-suggestions-line-4"],"autoFetch":[4,"auto-fetch"],"apiUrl":[1,"api-url"],"watchParams":[1,"watch-params"],"fetchedJobs":[32],"fetchedTotal":[32],"fetchLoading":[32]}],[512,"fast-input",{"placeholder":[1],"value":[1],"paramName":[1,"param-name"],"enableAutocomplete":[4,"enable-autocomplete"],"autocompleteUrl":[1,"autocomplete-url"],"targetPath":[1,"target-path"],"debounceMs":[2,"debounce-ms"],"minChars":[2,"min-chars"],"inputValue":[32],"suggestions":[32],"showDropdown":[32],"autocompleteLoading":[32]}],[772,"jobs-list-reactive",{"apiUrl":[1,"api-url"],"watchParams":[1,"watch-params"],"loadingClass":[1,"loading-class"],"isLoading":[32]}],[512,"jobs-item",{"job":[16],"index":[2],"applyButtonText":[1,"apply-button-text"],"showBrand":[4,"show-brand"],"showReference":[4,"show-reference"],"showEmploymentType":[4,"show-employment-type"],"multiLocationText":[1,"multi-location-text"],"remoteLocationText":[1,"remote-location-text"],"enableKilometers":[4,"enable-kilometers"]}]]]], options);
23
23
  });
24
24
 
25
25
  exports.setNonce = index.setNonce;
@@ -2,8 +2,11 @@
2
2
  "entries": [
3
3
  "components/button/button.js",
4
4
  "components/fast-carousel/carousel.js",
5
+ "components/fast-input/fast-input.js",
6
+ "components/job-card/job-card.js",
5
7
  "components/jobs-item/jobs-item.js",
6
- "components/jobs-list-only/jobs-list-only.js"
8
+ "components/jobs-list-only/jobs-list-only.js",
9
+ "components/jobs-list-reactive/jobs-list-reactive.js"
7
10
  ],
8
11
  "mixins": [],
9
12
  "compiler": {
@@ -53,7 +53,7 @@ export class CustomButton {
53
53
  "references": {
54
54
  "ButtonVariant": {
55
55
  "location": "local",
56
- "path": "/Users/phat.vu/Documents/Paradox/web-component-poc/src/components/button/button.tsx",
56
+ "path": "/Users/phat.vu/Documents/Paradox/ui-library/web-component-poc/src/components/button/button.tsx",
57
57
  "id": "src/components/button/button.tsx::ButtonVariant"
58
58
  }
59
59
  }
@@ -79,7 +79,7 @@ export class CustomButton {
79
79
  "references": {
80
80
  "ButtonType": {
81
81
  "location": "local",
82
- "path": "/Users/phat.vu/Documents/Paradox/web-component-poc/src/components/button/button.tsx",
82
+ "path": "/Users/phat.vu/Documents/Paradox/ui-library/web-component-poc/src/components/button/button.tsx",
83
83
  "id": "src/components/button/button.tsx::ButtonType"
84
84
  }
85
85
  }
@@ -0,0 +1,55 @@
1
+ .fast-input {
2
+ position: relative;
3
+ display: inline-flex;
4
+ gap: 0.5rem;
5
+ }
6
+
7
+ .fast-input__field {
8
+ flex: 1;
9
+ padding: 0.5rem 0.75rem;
10
+ font-size: 1rem;
11
+ border: 1px solid #ccc;
12
+ border-radius: 4px;
13
+ }
14
+
15
+ .fast-input__button {
16
+ padding: 0.5rem 1rem;
17
+ font-size: 1rem;
18
+ cursor: pointer;
19
+ background: #0070f3;
20
+ color: #fff;
21
+ border: none;
22
+ border-radius: 4px;
23
+ }
24
+
25
+ .fast-input__dropdown {
26
+ position: absolute;
27
+ top: 100%;
28
+ left: 0;
29
+ right: 0;
30
+ margin: 0;
31
+ padding: 0;
32
+ list-style: none;
33
+ background: #fff;
34
+ border: 1px solid #ccc;
35
+ border-top: none;
36
+ border-radius: 0 0 4px 4px;
37
+ z-index: 100;
38
+ max-height: 200px;
39
+ overflow-y: auto;
40
+ }
41
+
42
+ .fast-input__dropdown-item {
43
+ padding: 0.5rem 0.75rem;
44
+ cursor: pointer;
45
+ }
46
+
47
+ .fast-input__dropdown-item:hover {
48
+ background: #f0f0f0;
49
+ }
50
+
51
+ .fast-input__dropdown-loading {
52
+ padding: 0.5rem 0.75rem;
53
+ color: #999;
54
+ font-style: italic;
55
+ }
@@ -0,0 +1,335 @@
1
+ import { h } from "@stencil/core";
2
+ export class FastInput {
3
+ placeholder = 'Search...';
4
+ value = '';
5
+ paramName = 'keyword';
6
+ enableAutocomplete = false;
7
+ autocompleteUrl = '/api/jobs/autocomplete';
8
+ targetPath;
9
+ debounceMs = 300;
10
+ minChars = 3;
11
+ searchExecuted;
12
+ inputChanged;
13
+ inputValue = '';
14
+ suggestions = [];
15
+ showDropdown = false;
16
+ autocompleteLoading = false;
17
+ debounceTimer;
18
+ popstateHandler;
19
+ connectedCallback() {
20
+ const urlValue = this.getUrlParam();
21
+ this.inputValue = urlValue !== null ? urlValue : this.value;
22
+ this.popstateHandler = () => {
23
+ this.inputValue = this.getUrlParam() ?? '';
24
+ };
25
+ window.addEventListener('popstate', this.popstateHandler);
26
+ }
27
+ disconnectedCallback() {
28
+ window.removeEventListener('popstate', this.popstateHandler);
29
+ clearTimeout(this.debounceTimer);
30
+ }
31
+ getUrlParam() {
32
+ const params = new URLSearchParams(window.location.search);
33
+ return params.get(this.paramName);
34
+ }
35
+ updateUrlParam(value) {
36
+ const params = new URLSearchParams(window.location.search);
37
+ if (value) {
38
+ params.set(this.paramName, value);
39
+ }
40
+ else {
41
+ params.delete(this.paramName);
42
+ }
43
+ const qs = params.toString();
44
+ const newUrl = qs
45
+ ? `${window.location.pathname}?${qs}`
46
+ : window.location.pathname;
47
+ history.pushState({}, '', newUrl);
48
+ }
49
+ submit() {
50
+ this.updateUrlParam(this.inputValue);
51
+ document.dispatchEvent(new CustomEvent('search-executed', {
52
+ detail: { keyword: this.inputValue },
53
+ bubbles: true,
54
+ composed: true,
55
+ }));
56
+ this.searchExecuted.emit({ keyword: this.inputValue });
57
+ this.showDropdown = false;
58
+ }
59
+ handleInput = (e) => {
60
+ const value = e.target.value;
61
+ this.inputValue = value;
62
+ this.inputChanged.emit({ value });
63
+ if (this.enableAutocomplete) {
64
+ this.scheduleAutocomplete(value);
65
+ }
66
+ };
67
+ handleKeydown = (e) => {
68
+ if (e.key === 'Enter') {
69
+ this.submit();
70
+ }
71
+ else if (e.key === 'Escape') {
72
+ this.showDropdown = false;
73
+ }
74
+ };
75
+ handleBlur = () => {
76
+ this.showDropdown = false;
77
+ };
78
+ scheduleAutocomplete(value) {
79
+ clearTimeout(this.debounceTimer);
80
+ if (value.length < this.minChars) {
81
+ this.showDropdown = false;
82
+ return;
83
+ }
84
+ this.debounceTimer = setTimeout(() => this.fetchSuggestions(value), this.debounceMs);
85
+ }
86
+ async fetchSuggestions(keyword) {
87
+ if (!this.targetPath) {
88
+ console.warn('[fast-input] target-path is required for autocomplete');
89
+ return;
90
+ }
91
+ this.autocompleteLoading = true;
92
+ this.showDropdown = true;
93
+ try {
94
+ const res = await fetch(this.autocompleteUrl, {
95
+ method: 'POST',
96
+ headers: { 'Content-Type': 'application/json' },
97
+ body: JSON.stringify({ keyword, target_path: this.targetPath }),
98
+ });
99
+ if (!res.ok)
100
+ throw new Error('autocomplete request failed');
101
+ const data = await res.json();
102
+ this.suggestions = data;
103
+ }
104
+ catch {
105
+ this.showDropdown = false;
106
+ this.suggestions = [];
107
+ }
108
+ finally {
109
+ this.autocompleteLoading = false;
110
+ }
111
+ }
112
+ selectSuggestion(title) {
113
+ this.inputValue = title;
114
+ this.showDropdown = false;
115
+ this.submit();
116
+ }
117
+ render() {
118
+ return (h("div", { key: '3a9d31c7b109205600addc326d63979585f10bcd', class: "fast-input" }, h("input", { key: '8f238fe9e002f367d4939616be8c06d938d76045', type: "text", class: "fast-input__field", placeholder: this.placeholder, value: this.inputValue, onInput: this.handleInput, onKeyDown: this.handleKeydown, onBlur: this.handleBlur }), h("button", { key: '7b7404f13432750ece669da8ce68be15179921de', class: "fast-input__button", type: "button", onClick: () => this.submit() }, "Search"), this.enableAutocomplete && this.showDropdown && (h("ul", { key: '1438bacadc21c183842a8bdaa3f336bffb152e14', class: "fast-input__dropdown" }, this.autocompleteLoading ? (h("li", { class: "fast-input__dropdown-loading" }, "Loading...")) : (this.suggestions.map(s => (h("li", { class: "fast-input__dropdown-item", onMouseDown: e => { e.preventDefault(); }, onClick: () => this.selectSuggestion(s.title) }, s.title))))))));
119
+ }
120
+ static get is() { return "fast-input"; }
121
+ static get originalStyleUrls() {
122
+ return {
123
+ "$": ["fast-input.css"]
124
+ };
125
+ }
126
+ static get styleUrls() {
127
+ return {
128
+ "$": ["fast-input.css"]
129
+ };
130
+ }
131
+ static get properties() {
132
+ return {
133
+ "placeholder": {
134
+ "type": "string",
135
+ "mutable": false,
136
+ "complexType": {
137
+ "original": "string",
138
+ "resolved": "string",
139
+ "references": {}
140
+ },
141
+ "required": false,
142
+ "optional": false,
143
+ "docs": {
144
+ "tags": [],
145
+ "text": ""
146
+ },
147
+ "getter": false,
148
+ "setter": false,
149
+ "reflect": false,
150
+ "attribute": "placeholder",
151
+ "defaultValue": "'Search...'"
152
+ },
153
+ "value": {
154
+ "type": "string",
155
+ "mutable": false,
156
+ "complexType": {
157
+ "original": "string",
158
+ "resolved": "string",
159
+ "references": {}
160
+ },
161
+ "required": false,
162
+ "optional": false,
163
+ "docs": {
164
+ "tags": [],
165
+ "text": ""
166
+ },
167
+ "getter": false,
168
+ "setter": false,
169
+ "reflect": false,
170
+ "attribute": "value",
171
+ "defaultValue": "''"
172
+ },
173
+ "paramName": {
174
+ "type": "string",
175
+ "mutable": false,
176
+ "complexType": {
177
+ "original": "string",
178
+ "resolved": "string",
179
+ "references": {}
180
+ },
181
+ "required": false,
182
+ "optional": false,
183
+ "docs": {
184
+ "tags": [],
185
+ "text": ""
186
+ },
187
+ "getter": false,
188
+ "setter": false,
189
+ "reflect": false,
190
+ "attribute": "param-name",
191
+ "defaultValue": "'keyword'"
192
+ },
193
+ "enableAutocomplete": {
194
+ "type": "boolean",
195
+ "mutable": false,
196
+ "complexType": {
197
+ "original": "boolean",
198
+ "resolved": "boolean",
199
+ "references": {}
200
+ },
201
+ "required": false,
202
+ "optional": false,
203
+ "docs": {
204
+ "tags": [],
205
+ "text": ""
206
+ },
207
+ "getter": false,
208
+ "setter": false,
209
+ "reflect": false,
210
+ "attribute": "enable-autocomplete",
211
+ "defaultValue": "false"
212
+ },
213
+ "autocompleteUrl": {
214
+ "type": "string",
215
+ "mutable": false,
216
+ "complexType": {
217
+ "original": "string",
218
+ "resolved": "string",
219
+ "references": {}
220
+ },
221
+ "required": false,
222
+ "optional": false,
223
+ "docs": {
224
+ "tags": [],
225
+ "text": ""
226
+ },
227
+ "getter": false,
228
+ "setter": false,
229
+ "reflect": false,
230
+ "attribute": "autocomplete-url",
231
+ "defaultValue": "'/api/jobs/autocomplete'"
232
+ },
233
+ "targetPath": {
234
+ "type": "string",
235
+ "mutable": false,
236
+ "complexType": {
237
+ "original": "string | undefined",
238
+ "resolved": "string",
239
+ "references": {}
240
+ },
241
+ "required": false,
242
+ "optional": false,
243
+ "docs": {
244
+ "tags": [],
245
+ "text": ""
246
+ },
247
+ "getter": false,
248
+ "setter": false,
249
+ "reflect": false,
250
+ "attribute": "target-path"
251
+ },
252
+ "debounceMs": {
253
+ "type": "number",
254
+ "mutable": false,
255
+ "complexType": {
256
+ "original": "number",
257
+ "resolved": "number",
258
+ "references": {}
259
+ },
260
+ "required": false,
261
+ "optional": false,
262
+ "docs": {
263
+ "tags": [],
264
+ "text": ""
265
+ },
266
+ "getter": false,
267
+ "setter": false,
268
+ "reflect": false,
269
+ "attribute": "debounce-ms",
270
+ "defaultValue": "300"
271
+ },
272
+ "minChars": {
273
+ "type": "number",
274
+ "mutable": false,
275
+ "complexType": {
276
+ "original": "number",
277
+ "resolved": "number",
278
+ "references": {}
279
+ },
280
+ "required": false,
281
+ "optional": false,
282
+ "docs": {
283
+ "tags": [],
284
+ "text": ""
285
+ },
286
+ "getter": false,
287
+ "setter": false,
288
+ "reflect": false,
289
+ "attribute": "min-chars",
290
+ "defaultValue": "3"
291
+ }
292
+ };
293
+ }
294
+ static get states() {
295
+ return {
296
+ "inputValue": {},
297
+ "suggestions": {},
298
+ "showDropdown": {},
299
+ "autocompleteLoading": {}
300
+ };
301
+ }
302
+ static get events() {
303
+ return [{
304
+ "method": "searchExecuted",
305
+ "name": "searchExecuted",
306
+ "bubbles": true,
307
+ "cancelable": true,
308
+ "composed": true,
309
+ "docs": {
310
+ "tags": [],
311
+ "text": ""
312
+ },
313
+ "complexType": {
314
+ "original": "{ keyword: string }",
315
+ "resolved": "{ keyword: string; }",
316
+ "references": {}
317
+ }
318
+ }, {
319
+ "method": "inputChanged",
320
+ "name": "inputChanged",
321
+ "bubbles": true,
322
+ "cancelable": true,
323
+ "composed": true,
324
+ "docs": {
325
+ "tags": [],
326
+ "text": ""
327
+ },
328
+ "complexType": {
329
+ "original": "{ value: string }",
330
+ "resolved": "{ value: string; }",
331
+ "references": {}
332
+ }
333
+ }];
334
+ }
335
+ }