@aurodesignsystem-dev/auro-datetime 0.0.0-pr82.6 → 0.0.0-pr82.8

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/demo/api.md CHANGED
@@ -281,8 +281,8 @@ The `value` attribute also accepts ISO 8601 strings ending in the `Z` designator
281
281
 
282
282
  How the `timeZone` attribute composes with the input depends on whether `value` carries an offset:
283
283
 
284
- - **`value` has an offset (or `Z`):** the offset anchors the absolute moment; `timeZone` is the display zone. Example: `value="2022-07-14T08:00:00-04:00" timezone="US/Pacific"` renders as `5:00 am` (8am Eastern → 5am Pacific).
285
- - **`value` has no offset:** the wall-clock is interpreted as being in `timeZone`, so display in that same zone matches the input verbatim. Example: `value="2022-07-14T08:00:00" timezone="US/Eastern"` renders as `8:00 am` regardless of where the viewer is. This is the recommended shape when consumers know the source zone but don't have an offset readily available (e.g. flight-schedule data keyed by airport).
284
+ - **`value` has an offset (or `Z`):** the offset anchors the absolute moment; `timeZone` is the display zone. Example: `value="2022-07-14T08:00:00-04:00" timeZone="US/Pacific"` renders as `5:00 am` (8am Eastern → 5am Pacific).
285
+ - **`value` has no offset:** the wall-clock is interpreted as being in `timeZone`, so display in that same zone matches the input verbatim. Example: `value="2022-07-14T08:00:00" timeZone="US/Eastern"` renders as `8:00 am` regardless of where the viewer is. This is the recommended shape when consumers know the source zone but don't have an offset readily available (e.g. flight-schedule data keyed by airport).
286
286
 
287
287
  #### Invalid `timeZone` and `locale` values
288
288
 
@@ -150,6 +150,13 @@ class AuroDatetime extends i {
150
150
  */
151
151
  this._warnedTimeZones = new Set();
152
152
 
153
+ /**
154
+ * Tracks invalid value inputs already warned about. Same dedup
155
+ * pattern as `_warnedLocales`.
156
+ * @private
157
+ */
158
+ this._warnedValues = new Set();
159
+
153
160
  /**
154
161
  * @private
155
162
  */
@@ -361,8 +368,13 @@ class AuroDatetime extends i {
361
368
  * timeZone option for `Intl.DateTimeFormat`.
362
369
  *
363
370
  * Behavior:
364
- * - No `value`: returns today's date. `timeZone` (if any) is honored.
365
- * - `value` invalid: warns and returns `{ date: null }`.
371
+ * - `value` omitted (null/undefined): returns today's date. `timeZone`
372
+ * (if any) is honored.
373
+ * - `value` invalid (including empty string ""): warns and returns
374
+ * `{ date: null }`. Empty is *not* the same as omitted — Lit reflects
375
+ * `value=""` as the empty string, which is not a valid ISO 8601
376
+ * form, so we surface it as a typo rather than silently rendering
377
+ * today's date.
366
378
  * - `timeZone` set + input has offset/Z: parses as an absolute moment
367
379
  * (offset honored) and asks `toLocaleString` to convert into the target zone.
368
380
  * - `timeZone` set + input has no offset: interprets the wall-clock as
@@ -377,20 +389,42 @@ class AuroDatetime extends i {
377
389
  _resolveInputDate() {
378
390
  const resolvedTz = this._resolveTimeZone(this.timeZone);
379
391
 
380
- if (!this.value) {
392
+ // Only true "omitted" inputs (null/undefined) get the today's-date
393
+ // treatment. Empty string falls through to ISO validation, which will
394
+ // fail and produce the documented warn + empty render.
395
+ if (this.value == null) {
381
396
  return { date: new Date(), timeZoneOption: resolvedTz };
382
397
  }
383
398
 
384
399
  const match = this.value.match(ISO_8601_REGEX);
385
400
  if (!match) {
386
- console.warn(
387
- `auro-datetime: "${this.value}" is not a valid ISO 8601 string.`,
388
- );
401
+ if (!this._warnedValues.has(this.value)) {
402
+ this._warnedValues.add(this.value);
403
+ console.warn(
404
+ `auro-datetime: "${this.value}" is not a valid ISO 8601 string.`,
405
+ );
406
+ }
389
407
  return { date: null, timeZoneOption: undefined };
390
408
  }
391
409
 
392
410
  const localPart = match[1];
393
411
  const hasOffset = Boolean(match[2]);
412
+
413
+ // The regex permits an offset/Z suffix on a bare date (e.g. "2020-09-22Z"
414
+ // or "2020-09-22-07:00"). Those forms are not valid ISO 8601 — an offset
415
+ // is only meaningful with a time component — and the wall-clock branch
416
+ // would silently drop the offset, producing surprising output. Reject
417
+ // them up front so the consumer sees a warning.
418
+ if (hasOffset && !localPart.includes("T")) {
419
+ if (!this._warnedValues.has(this.value)) {
420
+ this._warnedValues.add(this.value);
421
+ console.warn(
422
+ `auro-datetime: "${this.value}" is not a valid ISO 8601 string (offset/Z requires a time component).`,
423
+ );
424
+ }
425
+ return { date: null, timeZoneOption: undefined };
426
+ }
427
+
394
428
  const normalized = localPart.includes("T")
395
429
  ? localPart
396
430
  : `${localPart}T00:00:00`;
@@ -415,9 +449,12 @@ class AuroDatetime extends i {
415
449
 
416
450
  const date = buildDate();
417
451
  if (Number.isNaN(date.getTime())) {
418
- console.warn(
419
- `auro-datetime: "${this.value}" is not a valid ISO 8601 date.`,
420
- );
452
+ if (!this._warnedValues.has(this.value)) {
453
+ this._warnedValues.add(this.value);
454
+ console.warn(
455
+ `auro-datetime: "${this.value}" is not a valid ISO 8601 date.`,
456
+ );
457
+ }
421
458
  return { date: null, timeZoneOption: undefined };
422
459
  }
423
460
 
@@ -0,0 +1,6 @@
1
+ import{LitElement as e,html as t}from"lit";class a{registerComponent(e,t){customElements.get(e)||customElements.define(e,class extends t{})}closestElement(e,t=this,a=(t,n=t&&t.closest(e))=>t&&t!==document&&t!==window?n||a(t.getRootNode().host):null){return a(t)}handleComponentTagRename(e,t){const a=t.toLowerCase();e.tagName.toLowerCase()!==a&&e.setAttribute(a,!0)}elementMatch(e,t){const a=t.toLowerCase();return e.tagName.toLowerCase()===a||e.hasAttribute(a)}getSlotText(e,t){const a=e.shadowRoot?.querySelector(`slot[name="${t}"]`);return(a?.assignedNodes({flatten:!0})||[]).map(e=>e.textContent?.trim()).join(" ").trim()||null}}const n=/^(\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?)?)(Z|[+-]\d{2}:\d{2})?$/u;class i extends e{constructor(){super(),this._initializeDefaults()}_initializeDefaults(){this.weekday="short",this.month="short",this.locale="en-US",this._warnedLocales=new Set,this._warnedTimeZones=new Set,this._warnedValues=new Set,this._effectiveLocale=this._resolveLocale(this.locale),this.runtimeUtils=new a}_resolveLocale(e){if(!e)return"en-US";try{return Intl.getCanonicalLocales(e)[0]}catch{return this._warnedLocales&&!this._warnedLocales.has(e)&&(this._warnedLocales.add(e),console.warn(`auro-datetime: "${e}" is not a valid BCP 47 locale tag. Falling back to "en-US".`)),"en-US"}}_resolveTimeZone(e){if(e)try{return new Intl.DateTimeFormat(void 0,{timeZone:e}),e}catch{return void(this._warnedTimeZones&&!this._warnedTimeZones.has(e)&&(this._warnedTimeZones.add(e),console.warn(`auro-datetime: "${e}" is not a valid IANA timezone. Falling back to viewer-local.`)))}}connectedCallback(){super.connectedCallback(),this.dateTemplate={weekday:this.weekday,year:"numeric",month:this.month,day:"numeric"},this.timeTemplate={hour:"2-digit",minute:"2-digit"}}static get properties(){return{locale:{type:String},month:{type:String},timeZone:{type:String},type:{type:String},value:{type:String},weekday:{type:String}}}static register(e="auro-datetime"){a.prototype.registerComponent(e,i)}willUpdate(e){e.has("locale")&&(this._effectiveLocale=this._resolveLocale(this.locale))}firstUpdated(){this.runtimeUtils.handleComponentTagRename(this,"auro-datetime")}_zonedWallClockToUtc(e,t){const a=new Date(`${e}Z`),n=new Intl.DateTimeFormat("en-US",{timeZone:t,hourCycle:"h23",year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"}).formatToParts(a),i=Object.fromEntries(n.filter(e=>"literal"!==e.type).map(e=>[e.type,e.value])),o=new Date(`${i.year}-${i.month}-${i.day}T${i.hour}:${i.minute}:${i.second}Z`),s=a.getTime()-o.getTime();return new Date(a.getTime()+s)}_resolveInputDate(){const e=this._resolveTimeZone(this.timeZone);if(null==this.value)return{date:new Date,timeZoneOption:e};const t=this.value.match(n);if(!t)return this._warnedValues.has(this.value)||(this._warnedValues.add(this.value),console.warn(`auro-datetime: "${this.value}" is not a valid ISO 8601 string.`)),{date:null,timeZoneOption:void 0};const a=t[1],i=Boolean(t[2]);if(i&&!a.includes("T"))return this._warnedValues.has(this.value)||(this._warnedValues.add(this.value),console.warn(`auro-datetime: "${this.value}" is not a valid ISO 8601 string (offset/Z requires a time component).`)),{date:null,timeZoneOption:void 0};const o=a.includes("T")?a:`${a}T00:00:00`,s=(()=>e&&!i?this._zonedWallClockToUtc(o,e):e&&i?new Date(this.value):new Date(o))();return Number.isNaN(s.getTime())?(this._warnedValues.has(this.value)||(this._warnedValues.add(this.value),console.warn(`auro-datetime: "${this.value}" is not a valid ISO 8601 date.`)),{date:null,timeZoneOption:void 0}):{date:s,timeZoneOption:e}}humanDate(){const{date:e,timeZoneOption:t}=this._resolveInputDate();if(!e)return"";const a={...this.dateTemplate};return t&&(a.timeZone=t),e.toLocaleString(this._effectiveLocale,a)}humanDateConversion(){const{date:e,timeZoneOption:t}=this._resolveInputDate();if(!e)return"";const a={};switch(t&&(a.timeZone=t),this.type){case"day":a.day="numeric";break;case"month":a.month=this.month;break;case"year":a.year="numeric";break;case"weekday":a.weekday=this.weekday}return e.toLocaleString(this._effectiveLocale,a)}numericDate(){const{date:e,timeZoneOption:t}=this._resolveInputDate();if(!e)return"";const a={...this.dateTemplate,month:"numeric"};return Reflect.deleteProperty(a,"weekday"),t&&(a.timeZone=t),e.toLocaleString(this._effectiveLocale,a)}humanTime(){const{date:e,timeZoneOption:t}=this._resolveInputDate();if(!e)return"";const a={...this.timeTemplate};t&&(a.timeZone=t);const n=e.toLocaleString(this._effectiveLocale,a);return/[ap]\.?m\.?/iu.test(n)?n.replace(/^0+/u,"").toLowerCase():n}whichDate(){switch(this.type){case"date":default:return this.humanDate();case"time":return this.humanTime();case"year":case"month":case"weekday":case"day":return this.humanDateConversion();case"numeric":return this.numericDate()}}render(){return t`
2
+ <slot name="pre"></slot>
3
+ <span class="yield">${this.whichDate()}</span>
4
+ <slot name="post"></slot>
5
+ <slot></slot>
6
+ `}}export{i as A};
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export{A as AuroDatetime}from"./auro-datetime-Cml4cDmu.js";import"lit";
1
+ export{A as AuroDatetime}from"./auro-datetime-KR9bjDcR.js";import"lit";
@@ -1 +1 @@
1
- import{A as r}from"./auro-datetime-Cml4cDmu.js";import"lit";r.register();
1
+ import{A as r}from"./auro-datetime-KR9bjDcR.js";import"lit";r.register();
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "================================================================================"
8
8
  ],
9
9
  "name": "@aurodesignsystem-dev/auro-datetime",
10
- "version": "0.0.0-pr82.6",
10
+ "version": "0.0.0-pr82.8",
11
11
  "description": "auro-datetime HTML custom element",
12
12
  "repository": {
13
13
  "type": "git",
@@ -1,6 +0,0 @@
1
- import{LitElement as e,html as t}from"lit";class n{registerComponent(e,t){customElements.get(e)||customElements.define(e,class extends t{})}closestElement(e,t=this,n=(t,a=t&&t.closest(e))=>t&&t!==document&&t!==window?a||n(t.getRootNode().host):null){return n(t)}handleComponentTagRename(e,t){const n=t.toLowerCase();e.tagName.toLowerCase()!==n&&e.setAttribute(n,!0)}elementMatch(e,t){const n=t.toLowerCase();return e.tagName.toLowerCase()===n||e.hasAttribute(n)}getSlotText(e,t){const n=e.shadowRoot?.querySelector(`slot[name="${t}"]`);return(n?.assignedNodes({flatten:!0})||[]).map(e=>e.textContent?.trim()).join(" ").trim()||null}}const a=/^(\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?)?)(Z|[+-]\d{2}:\d{2})?$/u;class i extends e{constructor(){super(),this._initializeDefaults()}_initializeDefaults(){this.weekday="short",this.month="short",this.locale="en-US",this._warnedLocales=new Set,this._warnedTimeZones=new Set,this._effectiveLocale=this._resolveLocale(this.locale),this.runtimeUtils=new n}_resolveLocale(e){if(!e)return"en-US";try{return Intl.getCanonicalLocales(e)[0]}catch{return this._warnedLocales&&!this._warnedLocales.has(e)&&(this._warnedLocales.add(e),console.warn(`auro-datetime: "${e}" is not a valid BCP 47 locale tag. Falling back to "en-US".`)),"en-US"}}_resolveTimeZone(e){if(e)try{return new Intl.DateTimeFormat(void 0,{timeZone:e}),e}catch{return void(this._warnedTimeZones&&!this._warnedTimeZones.has(e)&&(this._warnedTimeZones.add(e),console.warn(`auro-datetime: "${e}" is not a valid IANA timezone. Falling back to viewer-local.`)))}}connectedCallback(){super.connectedCallback(),this.dateTemplate={weekday:this.weekday,year:"numeric",month:this.month,day:"numeric"},this.timeTemplate={hour:"2-digit",minute:"2-digit"}}static get properties(){return{locale:{type:String},month:{type:String},timeZone:{type:String},type:{type:String},value:{type:String},weekday:{type:String}}}static register(e="auro-datetime"){n.prototype.registerComponent(e,i)}willUpdate(e){e.has("locale")&&(this._effectiveLocale=this._resolveLocale(this.locale))}firstUpdated(){this.runtimeUtils.handleComponentTagRename(this,"auro-datetime")}_zonedWallClockToUtc(e,t){const n=new Date(`${e}Z`),a=new Intl.DateTimeFormat("en-US",{timeZone:t,hourCycle:"h23",year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"}).formatToParts(n),i=Object.fromEntries(a.filter(e=>"literal"!==e.type).map(e=>[e.type,e.value])),o=new Date(`${i.year}-${i.month}-${i.day}T${i.hour}:${i.minute}:${i.second}Z`),s=n.getTime()-o.getTime();return new Date(n.getTime()+s)}_resolveInputDate(){const e=this._resolveTimeZone(this.timeZone);if(!this.value)return{date:new Date,timeZoneOption:e};const t=this.value.match(a);if(!t)return console.warn(`auro-datetime: "${this.value}" is not a valid ISO 8601 string.`),{date:null,timeZoneOption:void 0};const n=t[1],i=Boolean(t[2]),o=n.includes("T")?n:`${n}T00:00:00`,s=(()=>e&&!i?this._zonedWallClockToUtc(o,e):e&&i?new Date(this.value):new Date(o))();return Number.isNaN(s.getTime())?(console.warn(`auro-datetime: "${this.value}" is not a valid ISO 8601 date.`),{date:null,timeZoneOption:void 0}):{date:s,timeZoneOption:e}}humanDate(){const{date:e,timeZoneOption:t}=this._resolveInputDate();if(!e)return"";const n={...this.dateTemplate};return t&&(n.timeZone=t),e.toLocaleString(this._effectiveLocale,n)}humanDateConversion(){const{date:e,timeZoneOption:t}=this._resolveInputDate();if(!e)return"";const n={};switch(t&&(n.timeZone=t),this.type){case"day":n.day="numeric";break;case"month":n.month=this.month;break;case"year":n.year="numeric";break;case"weekday":n.weekday=this.weekday}return e.toLocaleString(this._effectiveLocale,n)}numericDate(){const{date:e,timeZoneOption:t}=this._resolveInputDate();if(!e)return"";const n={...this.dateTemplate,month:"numeric"};return Reflect.deleteProperty(n,"weekday"),t&&(n.timeZone=t),e.toLocaleString(this._effectiveLocale,n)}humanTime(){const{date:e,timeZoneOption:t}=this._resolveInputDate();if(!e)return"";const n={...this.timeTemplate};t&&(n.timeZone=t);const a=e.toLocaleString(this._effectiveLocale,n);return/[ap]\.?m\.?/iu.test(a)?a.replace(/^0+/u,"").toLowerCase():a}whichDate(){switch(this.type){case"date":default:return this.humanDate();case"time":return this.humanTime();case"year":case"month":case"weekday":case"day":return this.humanDateConversion();case"numeric":return this.numericDate()}}render(){return t`
2
- <slot name="pre"></slot>
3
- <span class="yield">${this.whichDate()}</span>
4
- <slot name="post"></slot>
5
- <slot></slot>
6
- `}}export{i as A};