playbook_ui 12.32.0 → 12.33.0.pre.alpha.PLAY905reactionbuttons979

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/index.js +1 -1
  3. data/app/pb_kits/playbook/pb_background/_background.tsx +10 -1
  4. data/app/pb_kits/playbook/pb_background/background.html.erb +2 -1
  5. data/app/pb_kits/playbook/pb_background/background.rb +4 -2
  6. data/app/pb_kits/playbook/pb_body/_body_mixins.scss +1 -0
  7. data/app/pb_kits/playbook/pb_button/_button.scss +33 -0
  8. data/app/pb_kits/playbook/pb_button/_button.tsx +65 -26
  9. data/app/pb_kits/playbook/pb_button/_button_mixins.scss +6 -6
  10. data/app/pb_kits/playbook/pb_button/button.html.erb +13 -0
  11. data/app/pb_kits/playbook/pb_button/button.rb +13 -2
  12. data/app/pb_kits/playbook/pb_button/docs/_button_reaction.html.erb +3 -0
  13. data/app/pb_kits/playbook/pb_button/docs/_button_reaction.jsx +38 -0
  14. data/app/pb_kits/playbook/pb_button/docs/example.yml +2 -0
  15. data/app/pb_kits/playbook/pb_button/docs/index.js +2 -1
  16. data/app/pb_kits/playbook/pb_circle_icon_button/_circle_icon_button.scss +1 -1
  17. data/app/pb_kits/playbook/pb_date/_date.tsx +7 -8
  18. data/app/pb_kits/playbook/pb_date/docs/_date_alignment.jsx +2 -2
  19. data/app/pb_kits/playbook/pb_date/docs/_date_default.jsx +4 -4
  20. data/app/pb_kits/playbook/pb_date/docs/_date_unstyled.jsx +2 -2
  21. data/app/pb_kits/playbook/pb_date/docs/_date_variants.jsx +5 -5
  22. data/app/pb_kits/playbook/pb_date_range_inline/_date_range_inline.tsx +45 -31
  23. data/app/pb_kits/playbook/pb_date_range_stacked/_date_range_stacked.tsx +5 -3
  24. data/app/pb_kits/playbook/pb_date_stacked/_date_stacked.tsx +24 -21
  25. data/app/pb_kits/playbook/pb_date_time/_date_time.tsx +1 -1
  26. data/app/pb_kits/playbook/pb_date_time/dateTime.test.js +1 -1
  27. data/app/pb_kits/playbook/pb_date_time_stacked/_date_time_stacked.tsx +2 -2
  28. data/app/pb_kits/playbook/pb_date_time_stacked/date_time_stacked.test.js +1 -1
  29. data/app/pb_kits/playbook/pb_date_year_stacked/_date_year_stacked.tsx +6 -8
  30. data/app/pb_kits/playbook/pb_dialog/_dialog.scss +321 -316
  31. data/app/pb_kits/playbook/pb_dialog/_dialog.tsx +12 -9
  32. data/app/pb_kits/playbook/pb_dialog/dialog.html.erb +8 -8
  33. data/app/pb_kits/playbook/pb_dialog/dialogHelper.js +16 -15
  34. data/app/pb_kits/playbook/pb_icon/_icon.tsx +4 -2
  35. data/app/pb_kits/playbook/pb_icon/docs/_icon_fa_kit.html.erb +1 -0
  36. data/app/pb_kits/playbook/pb_icon/docs/_icon_fa_kit.jsx +21 -0
  37. data/app/pb_kits/playbook/pb_icon/docs/_icon_fa_kit.md +7 -0
  38. data/app/pb_kits/playbook/pb_icon/docs/example.yml +2 -0
  39. data/app/pb_kits/playbook/pb_icon/docs/index.js +1 -0
  40. data/app/pb_kits/playbook/pb_icon/icon.rb +1 -1
  41. data/app/pb_kits/playbook/pb_icon_circle/_icon_circle.tsx +14 -4
  42. data/app/pb_kits/playbook/pb_icon_circle/icon_circle.rb +1 -1
  43. data/app/pb_kits/playbook/pb_icon_stat_value/_icon_stat_value.tsx +12 -1
  44. data/app/pb_kits/playbook/pb_icon_stat_value/docs/_icon_stat_value_color.html.erb +6 -0
  45. data/app/pb_kits/playbook/pb_icon_stat_value/docs/_icon_stat_value_color.jsx +9 -0
  46. data/app/pb_kits/playbook/pb_icon_stat_value/icon_stat_value.html.erb +16 -12
  47. data/app/pb_kits/playbook/pb_icon_stat_value/icon_stat_value.rb +2 -2
  48. data/app/pb_kits/playbook/pb_kit/dateTime.ts +139 -67
  49. data/app/pb_kits/playbook/pb_label_value/_label_value.tsx +52 -31
  50. data/app/pb_kits/playbook/pb_map/_map.scss +4 -0
  51. data/app/pb_kits/playbook/pb_message/_message.tsx +24 -24
  52. data/app/pb_kits/playbook/pb_time/_time.tsx +9 -11
  53. data/app/pb_kits/playbook/pb_time_range_inline/_time_range_inline.tsx +46 -49
  54. data/app/pb_kits/playbook/pb_time_stacked/_time_stacked.tsx +4 -6
  55. data/app/pb_kits/playbook/pb_timestamp/_timestamp.tsx +11 -11
  56. data/app/pb_kits/playbook/pb_weekday_stacked/_weekday_stacked.tsx +8 -11
  57. data/app/pb_kits/playbook/tokens/_colors.scss +4 -2
  58. data/app/pb_kits/playbook/utilities/_border_radius.scss +11 -1
  59. data/app/pb_kits/playbook/utilities/_line_height.scss +1 -2
  60. data/app/pb_kits/playbook/utilities/_shadow.scss +0 -1
  61. data/app/pb_kits/playbook/utilities/globalProps.ts +1 -1
  62. data/dist/playbook-rails.js +7 -7
  63. data/lib/playbook/version.rb +2 -2
  64. metadata +12 -8
  65. data/app/pb_kits/playbook/pb_logistic/_logistic.jsx +0 -120
@@ -181,7 +181,10 @@ const Dialog = (props: DialogProps) => {
181
181
  {title && !status ? <Dialog.Header>{title}</Dialog.Header> : null}
182
182
  {!status && text ? <Dialog.Body>{text}</Dialog.Body> : null}
183
183
  {status && (
184
- <Dialog.Body padding="md">
184
+ <Dialog.Body
185
+ className="dialog_status_text_align"
186
+ padding="md"
187
+ >
185
188
  <Flex align="center" orientation="column">
186
189
  <IconCircle
187
190
  icon={sweetAlertStatus[status].icon}
@@ -197,17 +200,17 @@ const Dialog = (props: DialogProps) => {
197
200
  )}
198
201
  {cancelButton && confirmButton ? (
199
202
  <Dialog.Footer>
200
- <Button
201
- loading={loading}
202
- onClick={onConfirm}
203
- htmlType="button"
203
+ <Button
204
+ loading={loading}
205
+ onClick={onConfirm}
206
+ htmlType="button"
204
207
  variant="primary">
205
208
  {confirmButton}
206
209
  </Button>
207
- <Button
208
- id="cancel-button"
209
- onClick={onCancel}
210
- variant="link"
210
+ <Button
211
+ id="cancel-button"
212
+ onClick={onCancel}
213
+ variant="link"
211
214
  htmlType="button">
212
215
  {cancelButton}
213
216
  </Button>
@@ -5,38 +5,38 @@
5
5
  id: object.id,
6
6
  class: object.classname) do %>
7
7
  <% if object.status === "" && object.title %>
8
- <%= pb_rails("dialog/dialog_header", props: {title: object.title, id: object.id }) %>
8
+ <%= pb_rails("dialog/dialog_header", props: { title: object.title, id: object.id }) %>
9
9
  <% end %>
10
10
  <% if object.status === "" && object.text %>
11
11
  <%= pb_rails("dialog/dialog_body", props: { text: object.text }) %>
12
12
  <% end %>
13
13
  <% if object.status != "" %>
14
- <%= pb_rails("dialog/dialog_body", props: {padding: "md"}) do %>
15
- <%= pb_rails("flex", props: {align: "center", orientation: "column"}) do %>
14
+ <%= pb_rails("dialog/dialog_body", props: { classname: "dialog_status_text_align" ,padding: "md" }) do %>
15
+ <%= pb_rails("flex", props: { align: "center", orientation: "column" }) do %>
16
16
  <%= pb_rails("icon_circle", props: {
17
17
  icon: object.status_alerts[0],
18
18
  variant: object.status_alerts[1],
19
19
  size: "lg"
20
20
  }) %>
21
21
  <%= pb_rails("title", props: { text: object.title, tag: "h1", size: 3, margin_top: "sm" }) %>
22
- <%= pb_rails("body", props: {text: object.text, margin_top: "sm"}) %>
22
+ <%= pb_rails("body", props: { text: object.text, margin_top: "sm" }) %>
23
23
  <% end %>
24
24
  <% end %>
25
25
  <% end %>
26
26
  <% if object.cancel_button && object.confirm_button %>
27
27
  <%= pb_rails("dialog/dialog_footer", props: {
28
- cancel_button: object.cancel_button,
28
+ cancel_button: object.cancel_button,
29
29
  confirm_button: object.confirm_button,
30
30
  id: object.id
31
31
  }) %>
32
32
  <% end %>
33
-
33
+
34
34
  <%= content %>
35
35
  <% end %>
36
36
  </div>
37
37
 
38
38
  <%= javascript_tag do %>
39
- window.addEventListener("DOMContentLoaded", () => {
39
+ window.addEventListener("DOMContentLoaded", () => {
40
40
  dialogHelper()
41
41
  })
42
- <% end %>
42
+ <% end %>
@@ -1,6 +1,7 @@
1
1
  const dialogHelper = () => {
2
2
  const openTrigger = document.querySelectorAll("[data-open-dialog]");
3
3
  const closeTrigger = document.querySelectorAll("[data-close-dialog]");
4
+ const dialogs = document.querySelectorAll(".pb_dialog_rails")
4
5
 
5
6
  openTrigger.forEach((open) => {
6
7
  open.addEventListener("click", () => {
@@ -8,21 +9,6 @@ const dialogHelper = () => {
8
9
  var targetDialog = document.getElementById(openTriggerData)
9
10
  if (targetDialog.open) return;
10
11
  targetDialog.showModal();
11
-
12
- //the following allows you to close dialog by clicking on overlay
13
- targetDialog.addEventListener('click',((event) => {
14
- var dialogContainerData = targetDialog.parentElement.dataset
15
- if (dialogContainerData.overlayClick === "overlay_close") return;
16
- let rect = event.target.getBoundingClientRect();
17
- if (rect.left > event.clientX ||
18
- rect.right < event.clientX ||
19
- rect.top > event.clientY ||
20
- rect.bottom < event.clientY
21
- ) {
22
- targetDialog.close();
23
- }
24
- })
25
- );
26
12
  });
27
13
  });
28
14
 
@@ -32,6 +18,21 @@ const dialogHelper = () => {
32
18
  document.getElementById(closeTriggerData).close();
33
19
  });
34
20
  });
21
+
22
+ // Close dialog box on outside click
23
+ dialogs.forEach((dialogElement) => {
24
+ dialogElement.addEventListener("click", (event) => {
25
+ const dialogParentDataset = dialogElement.parentElement.dataset
26
+ if (dialogParentDataset.overlayClick === "overlay_close") return
27
+
28
+ const clickedOutsideDialogBox = event.target.classList.contains("pb_dialog_rails")
29
+
30
+ if (clickedOutsideDialogBox) {
31
+ dialogElement.close()
32
+ event.stopPropagation()
33
+ }
34
+ })
35
+ })
35
36
  };
36
37
 
37
38
  export default dialogHelper;
@@ -34,7 +34,7 @@ type IconProps = {
34
34
  pulse?: boolean,
35
35
  rotation?: 90 | 180 | 270,
36
36
  size?: IconSizes,
37
- fontStyle?: 'far' | 'fas' | 'fab',
37
+ fontStyle?: 'far' | 'fas' | 'fab' | 'fak',
38
38
  spin?: boolean,
39
39
  } & GlobalProps
40
40
 
@@ -76,6 +76,7 @@ const Icon = (props: IconProps) => {
76
76
  [`fa-${size}`]: size,
77
77
  [`fa-pull-${pull}`]: pull,
78
78
  [`fa-rotate-${rotation}`]: rotation,
79
+
79
80
  }
80
81
 
81
82
  // Lets check and see if the icon prop is referring to a custom Power icon...
@@ -83,7 +84,7 @@ const Icon = (props: IconProps) => {
83
84
  // this ensures the JS will not do any further operations
84
85
  // faClasses[`fa-${icon}`] = customIcon ? 'custom' : icon
85
86
  if (!customIcon) faClasses[`fa-${icon}`] = icon
86
-
87
+
87
88
  const classes = classnames(
88
89
  flipMap[flip],
89
90
  'pb_icon_kit',
@@ -110,6 +111,7 @@ const Icon = (props: IconProps) => {
110
111
  return emojiRegex.test(emoji);
111
112
  };
112
113
 
114
+
113
115
  // Add a conditional here to show only the SVG if custom
114
116
  const displaySVG = (customIcon: any) => {
115
117
  if (customIcon)
@@ -0,0 +1 @@
1
+ <%= pb_rails("icon", props: { icon: "powergon", font_style: "fak", fixed_width: true, size: "5x" }) %>
@@ -0,0 +1,21 @@
1
+ import React from 'react'
2
+
3
+ import Icon from '../_icon'
4
+
5
+ import '../../../../../../playbook-website/app/javascript/scripts/custom-icons'
6
+
7
+ const IconFaKit = (props) => {
8
+ return (
9
+ <div>
10
+ <Icon
11
+ {...props}
12
+ fixedWidth
13
+ fontStyle="fak"
14
+ icon="powergon"
15
+ size="5x"
16
+ />
17
+ </div>
18
+ )
19
+ }
20
+
21
+ export default IconFaKit
@@ -0,0 +1,7 @@
1
+ Our Icon kit allows integration with [FontAwesome's custom kit](https://fontawesome.com/v6/docs/web/setup/use-kit#contentHeader) functionality out-of-the-box.
2
+
3
+ All you need to do is 3 things:
4
+ 1) Import your custom-icon.js file as outlined in the FontAwesome docs.
5
+ 2) Use our fontStyle prop called "fak" so that our Icon component knows you are using a "fa-kit" icon.
6
+ 3) Pass in your FaKit name as a string to our icon prop (This is the name that you designated when you uploaded the icon on their site).
7
+
@@ -8,6 +8,7 @@ examples:
8
8
  - icon_border: Icon Border
9
9
  - icon_sizes: Icon Sizes
10
10
  - icon_custom: Icon Custom
11
+ - icon_fa_kit: Icon with FontAwesome Kit
11
12
 
12
13
  react:
13
14
  - icon_default: Icon Default
@@ -18,3 +19,4 @@ examples:
18
19
  - icon_border: Icon Border
19
20
  - icon_sizes: Icon Sizes
20
21
  - icon_custom: Icon Custom
22
+ - icon_fa_kit: Icon with FontAwesome Kit
@@ -6,3 +6,4 @@ export { default as IconPull } from './_icon_pull.jsx'
6
6
  export { default as IconBorder } from './_icon_border.jsx'
7
7
  export { default as IconSizes } from './_icon_sizes.jsx'
8
8
  export { default as IconCustom } from './_icon_custom.jsx'
9
+ export { default as IconFaKit} from './_icon_fa_kit.jsx'
@@ -33,7 +33,7 @@ module Playbook
33
33
  values: ["lg", "xs", "sm", "1x", "2x", "3x", "4x", "5x", "6x", "7x", "8x", "9x", "10x", nil],
34
34
  default: nil
35
35
  prop :font_style, type: Playbook::Props::Enum,
36
- values: %w[far fas fab],
36
+ values: %w[far fas fab fak],
37
37
  default: "far"
38
38
  prop :spin, type: Playbook::Props::Boolean,
39
39
  default: false
@@ -22,11 +22,21 @@ type IconCircleProps = {
22
22
  | "teal"
23
23
  | "red"
24
24
  | "yellow"
25
+ | "orange"
25
26
  | "green",
26
27
  }
27
28
 
28
29
  const IconCircle = (props: IconCircleProps) => {
29
- const { aria = {}, className, dark = false, data = {}, icon, id, size = 'md', variant = 'default' } = props
30
+ const {
31
+ aria = {},
32
+ className,
33
+ dark = false,
34
+ data = {},
35
+ icon,
36
+ id,
37
+ size = 'md',
38
+ variant = 'default'
39
+ } = props
30
40
 
31
41
  const ariaProps = buildAriaProps(aria)
32
42
  const dataProps = buildDataProps(data)
@@ -41,10 +51,10 @@ const IconCircle = (props: IconCircleProps) => {
41
51
  id={id}
42
52
  >
43
53
  <Icon
44
- dark={dark}
45
- icon={icon}
54
+ dark={dark}
55
+ icon={icon}
46
56
  />
47
-
57
+
48
58
  </div>
49
59
  )
50
60
  }
@@ -10,7 +10,7 @@ module Playbook
10
10
  values: %w[xs sm md base lg xl],
11
11
  default: "md"
12
12
  prop :variant, type: Playbook::Props::Enum,
13
- values: %w[default royal blue orange purple teal red yellow green],
13
+ values: %w[default royal blue orange purple teal red yellow green orange],
14
14
  default: "default"
15
15
 
16
16
  def classname
@@ -14,6 +14,7 @@ type IconStatValueProps = {
14
14
  aria?: { [key: string]: string },
15
15
  className?: string,
16
16
  data?: object,
17
+ dark?: boolean,
17
18
  icon: string,
18
19
  id?: string,
19
20
  orientation?: "vertical" | "horizontal",
@@ -28,6 +29,7 @@ type IconStatValueProps = {
28
29
  | "teal"
29
30
  | "red"
30
31
  | "yellow"
32
+ | "orange"
31
33
  | "green",
32
34
  }
33
35
 
@@ -36,6 +38,7 @@ const IconStatValue = (props: IconStatValueProps) => {
36
38
  aria = {},
37
39
  className,
38
40
  data = {},
41
+ dark = false,
39
42
  icon,
40
43
  id,
41
44
  orientation = 'horizontal',
@@ -55,6 +58,7 @@ const IconStatValue = (props: IconStatValueProps) => {
55
58
  if (size == 'lg') {
56
59
  return (
57
60
  <Title
61
+ dark={dark}
58
62
  size={1}
59
63
  tag="span"
60
64
  text={`${value}`}
@@ -63,6 +67,7 @@ const IconStatValue = (props: IconStatValueProps) => {
63
67
  } else if (size == 'md') {
64
68
  return (
65
69
  <Title
70
+ dark={dark}
66
71
  size={2}
67
72
  tag="span"
68
73
  text={`${value}`}
@@ -71,6 +76,7 @@ const IconStatValue = (props: IconStatValueProps) => {
71
76
  } else {
72
77
  return (
73
78
  <Title
79
+ dark={dark}
74
80
  size={3}
75
81
  tag="span"
76
82
  text={`${value}`}
@@ -87,6 +93,7 @@ const IconStatValue = (props: IconStatValueProps) => {
87
93
  id={id}
88
94
  >
89
95
  <IconCircle
96
+ dark={dark}
90
97
  icon={icon}
91
98
  size={size}
92
99
  variant={variant}
@@ -99,10 +106,14 @@ const IconStatValue = (props: IconStatValueProps) => {
99
106
  {titleSize(size)}
100
107
  &nbsp;
101
108
  <Body
109
+ dark={dark}
102
110
  text={unit}
103
111
  />
104
112
  </Flex>
105
- <Caption text={text} />
113
+ <Caption
114
+ dark={dark}
115
+ text={text}
116
+ />
106
117
  </div>
107
118
 
108
119
  </div>
@@ -46,4 +46,10 @@
46
46
  unit: "AU",
47
47
  value: 19.18,
48
48
  variant:"green"
49
+ }) %>
50
+ <%= pb_rails("icon_stat_value", props: { icon: "globe",
51
+ text: "Uranus",
52
+ unit: "AU",
53
+ value: 19.18,
54
+ variant:"orange"
49
55
  }) %>
@@ -66,6 +66,15 @@ const IconStatValueColor = (props) => {
66
66
  variant="green"
67
67
  {...props}
68
68
  />
69
+ <br />
70
+ <IconStatValue
71
+ icon="globe"
72
+ text="Uranus"
73
+ unit="AU"
74
+ value={19.18}
75
+ variant="orange"
76
+ {...props}
77
+ />
69
78
  </div>
70
79
 
71
80
  )
@@ -4,22 +4,26 @@
4
4
  data: object.data,
5
5
  class: object.classname) do %>
6
6
 
7
- <%= pb_rails "icon_circle", props: {
8
- icon: object.icon,
9
- size: object.size,
10
- variant: object.variant } %>
7
+ <%= pb_rails("icon_circle", props: {
8
+ dark: object.dark,
9
+ icon: object.icon,
10
+ size: object.size,
11
+ variant: object.variant }) %>
11
12
 
12
- <div>
13
+ <div>
13
14
  <%= pb_rails("flex", props: { align: "baseline" }) do %>
14
- <%= pb_rails "title", props: {
15
+ <%= pb_rails("title", props: {
16
+ dark: object.dark,
15
17
  tag: "span",
16
18
  text: object.value_string,
17
- size: object.title_size } %>
19
+ size: object.title_size }) %>
18
20
  &nbsp;
19
- <%= pb_rails "body", props: {
20
- text: object.unit } %>
21
+ <%= pb_rails("body", props: {
22
+ dark: object.dark,
23
+ text: object.unit }) %>
21
24
  <% end %>
22
- <%= pb_rails "caption", props: {
23
- text: object.text } %>
24
- </div>
25
+ <%= pb_rails("caption", props: {
26
+ dark: object.dark,
27
+ text: object.text }) %>
28
+ </div>
25
29
  <% end %>
@@ -9,7 +9,7 @@ module Playbook
9
9
  values: %w[sm md lg],
10
10
  default: "sm"
11
11
  prop :variant, type: Playbook::Props::Enum,
12
- values: %w[default royal blue purple teal red yellow green],
12
+ values: %w[default royal blue purple teal red yellow green orange],
13
13
  default: "default"
14
14
 
15
15
  prop :orientation, type: Playbook::Props::Enum,
@@ -22,7 +22,7 @@ module Playbook
22
22
  prop :text, type: Playbook::Props::String,
23
23
  default: ""
24
24
 
25
- prop :value, type: Playbook::Props::Numeric
25
+ prop :value
26
26
 
27
27
  def classname
28
28
  generate_classname("pb_icon_stat_value_kit", orientation, size, variant)
@@ -1,90 +1,162 @@
1
-
2
- import moment, { Moment } from 'moment'
3
- import 'moment-strftime'
4
- import 'moment-timezone'
5
-
6
- type DateTimeType = {
7
- value: string | Date,
8
- zone?: string,
9
- }
10
-
11
1
  const ABBR_DAYS = ['SU', 'M', 'T', 'W', 'TH', 'F', 'S']
12
2
 
13
- export default class DateTime {
14
- value: Moment & any
15
- constructor({ value, zone = 'America/New_York' }: DateTimeType) {
16
- this.value = this.convertToTimestampZone(value, zone)
17
- }
18
-
19
- convertToTimestampZone(value: string | Date, zone: string) {
20
- return moment(value).tz(zone)
21
- }
22
-
23
- convertToTimezone() {
24
- return this.value.strftime('%Z')
25
- }
3
+ const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
26
4
 
27
- toCustomFormat(format = '%-m/%-d') {
28
- return this.value.strftime(format)
29
- }
5
+ const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
30
6
 
31
- toYear() {
32
- return this.value.strftime('%Y')
7
+ export const toMinute = (newDate: Date | string, timeZone?: string): string => {
8
+ const date = new Date(newDate)
9
+ if (timeZone) {
10
+ return date.toLocaleTimeString(undefined, {timeZone, hour: "2-digit", minute: "2-digit"}).slice(3, 5);
11
+ } else {
12
+ return date.toLocaleTimeString(undefined, {hour: "2-digit", minute: "2-digit"}).slice(3, 5);
33
13
  }
14
+ }
34
15
 
35
- toMonth() {
36
- return this.value.strftime('%b')
16
+ export const toHour = (newDate: Date | string, timeZone?: string): string => {
17
+ const date = new Date(newDate)
18
+ if (timeZone) {
19
+ return date.toLocaleTimeString(undefined, {timeZone, hour: "numeric"}).split(' ')[0];
20
+ } else {
21
+ return date.toLocaleTimeString(undefined, {hour: "numeric"}).split(' ')[0];
37
22
  }
23
+ }
38
24
 
39
- toMonthNum() {
40
- return this.value.strftime('%-m')
41
- }
25
+ export const toDay = (newDate: Date | string, timeZone?: string): number => {
26
+ if (timeZone) {
27
+ const date = new Date(newDate.toLocaleString(undefined, { timeZone }));
28
+ return date.getDate()
29
+ } else {
30
+ const date = new Date(newDate)
31
+ return date.getDate()
32
+ }
33
+ }
42
34
 
43
- toMonthFull() {
44
- return this.value.strftime('%B')
45
- }
35
+ export const toDayAbbr = (newDate: Date | string): string => {
36
+ const date = new Date(newDate)
37
+ return ABBR_DAYS[date.getUTCDay()]
38
+ }
46
39
 
47
- toDay() {
48
- return this.value.strftime('%e')
49
- }
40
+ export const toWeekday = (newDate: Date | string): string => {
41
+ const date = new Date(newDate)
42
+ return days[date.getUTCDay()]
43
+ }
50
44
 
51
- toDayAbbr() {
52
- return ABBR_DAYS[this.value.day()]
53
- }
45
+ export const toMonth = (newDate: Date | string, timeZone?: string): string => {
46
+ if (timeZone) {
47
+ const date = new Date(newDate.toLocaleString(undefined, { timeZone }));
48
+ return months[date.getUTCMonth()]
49
+ } else {
50
+ const date = new Date(newDate)
51
+ return months[date.getUTCMonth()]
52
+ }
53
+ }
54
54
 
55
- toWeekday() {
56
- return this.value.strftime('%a')
57
- }
55
+ export const toMonthNum = (newDate: Date | string): number => {
56
+ const date = new Date(newDate)
57
+ return date.getUTCMonth() +1
58
+ }
58
59
 
59
- toHour() {
60
- return this.value.strftime('%l')
61
- }
60
+ export const toYear = (newDate: Date | string, timeZone?: string): number => {
61
+ if (timeZone) {
62
+ const date = new Date(newDate.toLocaleString(undefined, { timeZone }));
63
+ return date.getUTCFullYear()
64
+ } else {
65
+ const date = new Date(newDate)
66
+ return date.getUTCFullYear()
67
+ }
68
+ }
62
69
 
63
- toMinute() {
64
- return this.value.strftime('%M')
70
+ export const toTime = (newDate: Date | string, timeZone?: string): string => {
71
+ const date = new Date(newDate)
72
+ if (timeZone) {
73
+ return date.toLocaleTimeString(undefined, {timeZone, timeStyle: "short"}).split(' ')[0];
74
+ } else {
75
+ return date.toLocaleTimeString(undefined, {timeStyle: "short"}).split(' ')[0];
65
76
  }
77
+ }
66
78
 
67
- toMeridian() {
68
- return this.value.strftime('%P')[0]
69
- }
79
+ export const toMeridiem = (newDate: Date | string, timeZone?: string): string => {
80
+ const date = new Date(newDate)
81
+ if (timeZone) {
82
+ return date.toLocaleString(undefined, {timeZone, hour12: true }).slice(-2).charAt(0).toLocaleLowerCase();
83
+ } else {
84
+ return date.toLocaleString(undefined, {hour12: true }).slice(-2).charAt(0).toLocaleLowerCase();
85
+ }
86
+ }
70
87
 
71
- toIso() {
72
- return this.value.toISOString()
73
- }
88
+ export const toTimeZone = (newDate: Date | string, timeZone?: string): string => {
89
+ const date = new Date(newDate)
90
+ if (timeZone) {
91
+ return date.toLocaleString(undefined, {timeZone, timeZoneName: "short"}).split(' ')[3];
92
+ } else {
93
+ return date.toLocaleString(undefined, {timeZoneName: "short"}).split(' ')[3];
94
+ }
95
+ }
74
96
 
75
- toTime() {
76
- const time = this.value.strftime('%I:%M')
97
+ export const toTimeWithMeridiem = (newDate: Date | string, timeZone: string): string => {
98
+ const date = new Date(newDate)
99
+ return `${toTime(date, timeZone)}${toMeridiem(date, timeZone)}`;
100
+ }
77
101
 
78
- // strftime adds a leading 0 on single hour times. ie 08:31.
79
- // this removes that 0 to match the rails kit.
80
- return time.charAt() === '0' ? time.slice(1) : time
81
- }
102
+ export const toIso = (newDate: Date | string): string => {
103
+ const date = new Date(newDate)
104
+ return date.toISOString()
105
+ }
82
106
 
83
- toTimezone() {
84
- return this.value.strftime('%Z')
85
- }
107
+ export const fromNow = (newDate: Date | string): string => {
108
+
109
+ const startDate = new Date(newDate).getTime()
110
+ const endDate = new Date().getTime()
111
+ const elapsedTime = endDate - startDate
112
+ let elapsedTimeString = `${Math.round(elapsedTime / (365.25 * 24 * 60 * 60 * 1000))} years ago.`; // 730+ days
113
+
114
+ const elapsedTimeData = [
115
+ { min: 0, max: 44999, value: "a few seconds ago" }, // 0-44 seconds
116
+ { min: 45000, max: 89999, value: "a minute ago" }, // 45-89 seconds
117
+ { min: 90000, max: 2649999, value: `${Math.round(elapsedTime / 60000)} minutes ago`}, // 90s-44 minutes
118
+ { min: 2650000, max: 7299999, value: "an hour ago" }, // 45-120 minutes
119
+ { min: 7300000, max: 75699999, value: `${Math.round(elapsedTime / 3600000)} hours ago`}, // 2-21 hours
120
+ { min: 75700000, max: 172899999, value: "a day ago" }, // 22-48 hours
121
+ { min: 172900000, max: 2169999999, value: `${Math.round(elapsedTime / 86400000)} days ago`}, // 2-25 days
122
+ { min: 2170000000, max: 5184999999, value: "a month ago"}, // 26-60 days
123
+ { min: 5185000000, max: 27561699999, value: `${Math.round(elapsedTime / 30.44 * 24 * 60 * 60 * 1000)} months ago`}, // 60-319 days
124
+ { min: 27561700000, max: 63072999999, value: "a year ago"}, // 320-730 days
125
+ ];
126
+
127
+ for (const timeDate of elapsedTimeData) {
128
+ if (elapsedTime >= timeDate.min && elapsedTime <= timeDate.max) {
129
+ elapsedTimeString = timeDate.value;
130
+ break;
131
+ }
132
+ }
133
+
134
+ return elapsedTimeString
135
+ }
86
136
 
87
- toTimeWithMeridian() {
88
- return this.toTime() + this.toMeridian()
137
+ export const toCustomFormat = (newDate: Date | string, format = 'month_day'): string => {
138
+ const date = new Date(newDate)
139
+ if (format == "month_day") {
140
+ return `${toMonthNum(date)}/${toDay(date)}`
141
+ } else {
142
+ return `${date.toLocaleString(undefined, {month: "short"})} ${toDay(date)}`
89
143
  }
90
144
  }
145
+
146
+ export default {
147
+ toMinute,
148
+ toHour,
149
+ toDay,
150
+ toDayAbbr,
151
+ toWeekday,
152
+ toMonth,
153
+ toMonthNum,
154
+ toYear,
155
+ toTime,
156
+ toMeridiem,
157
+ toTimeZone,
158
+ toTimeWithMeridiem,
159
+ toIso,
160
+ fromNow,
161
+ toCustomFormat,
162
+ }