rollout-ui 0.9.1 → 0.9.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e63cbca0b53def021d093e24434d1c3857515164a82f92878f0b832c09b7319a
4
- data.tar.gz: 05a822fb2f9ed0e8032681e255fc981dfa70d23f2e061bec5bf59bad6c54f722
3
+ metadata.gz: b5230ac819ea0e86c7dc2606f9b308c429b6b92b50e8fb402ac3b4f5519b8207
4
+ data.tar.gz: 67f902bcffbc98e99189b16c577c66952bde58b0dfff550318cadcb0779f9bc3
5
5
  SHA512:
6
- metadata.gz: 3472089c2fe218f5c1bc35911a15239ed79e3c243d91903d2c04f53d09e816893dc2f841ad7d287d7debece8b53d77d39a3b725db27f0f92039cea8959a01db7
7
- data.tar.gz: 3844e1d841186b55edb5757f92e3cb48c517ba94beb26c06dcc97ea4ebbe1af44665aed0e882dc61584113dd52ca44aeeef28118a9e49e7c943adbc5a8e5f89a
6
+ metadata.gz: af0154118022337d0963bb63013498f2292fecfe29dfd318729335fd724c4e4569e2c7a3f6662c19ce0c9ba729fd9a0149005963cbc531c3ae800b55077a8fa8
7
+ data.tar.gz: 85d005cfbf60f08c96032e3a4b9c607aca322f013050845489974c21a19a88259d68a98d011774e75c4c7d2e6f692969c3ee7daadffb6542a31bc2d144f49409
@@ -70,6 +70,16 @@ module Rollout::UI
70
70
  end
71
71
  end
72
72
 
73
+ def formatted_timestamp(time)
74
+ time.strftime(config.get(:timestamp_format))
75
+ end
76
+
77
+ def relative_timestamp_tag(time)
78
+ return "" unless time
79
+
80
+ %(<span data-timestamp="#{h(time.iso8601)}" title="#{h(formatted_timestamp(time))}">#{h(time_ago(time))}</span>)
81
+ end
82
+
73
83
  def format_change_key(key)
74
84
  key.to_s.gsub('data.', '')
75
85
  end
@@ -1,5 +1,5 @@
1
1
  class Rollout
2
2
  module UI
3
- VERSION = "0.9.1"
3
+ VERSION = "0.9.2"
4
4
  end
5
5
  end
@@ -49,7 +49,7 @@
49
49
  <% if updated_at.nil? %>
50
50
  <span class="text-gray-400">Never</span>
51
51
  <% else %>
52
- <span title="<%= h(updated_at.strftime(Rollout::UI.config.timestamp_format)) %>"><%= h(time_ago(updated_at)) %></span>
52
+ <%= relative_timestamp_tag(updated_at) %>
53
53
  <% end %>
54
54
  </td>
55
55
  <% end %>
@@ -55,7 +55,7 @@
55
55
  </td>
56
56
 
57
57
  <td class="py-2 text-right whitespace-nowrap pl-3" style="min-width: 160px;">
58
- <span title="<%= h(event.created_at.strftime(Rollout::UI.config.timestamp_format)) %>"><%= h(time_ago(event.created_at)) %></span>
58
+ <%= relative_timestamp_tag(event.created_at) %>
59
59
  </td>
60
60
  </tr>
61
61
  <% end %>
@@ -1,5 +1,5 @@
1
1
  <!DOCTYPE html>
2
- <html>
2
+ <html data-timestamp-format="<%= h(config.get(:timestamp_format)) %>">
3
3
  <head>
4
4
  <title>Rollout UI</title>
5
5
  <meta name="viewport" content="width=device-width initial-scale=1">
@@ -42,19 +42,85 @@
42
42
  }
43
43
  }
44
44
  applyTheme(theme);
45
+ const themeLabels = { system: 'Theme: System', light: 'Theme: Light', dark: 'Theme: Dark' };
46
+ const themeTitles = {
47
+ system: "Follows your device's light or dark appearance setting.",
48
+ light: 'Always use light appearance.',
49
+ dark: 'Always use dark appearance.'
50
+ };
45
51
  window.addEventListener('DOMContentLoaded', function() {
46
52
  const btn = document.getElementById('theme-toggle');
47
- btn.textContent = theme;
53
+ function applyThemeLabel(t) {
54
+ btn.textContent = themeLabels[t];
55
+ btn.title = themeTitles[t];
56
+ }
57
+ applyThemeLabel(theme);
48
58
  btn.addEventListener('click', function() {
49
59
  const current = localStorage.getItem('theme') || 'system';
50
60
  const next = current === 'light' ? 'dark' : current === 'dark' ? 'system' : 'light';
51
61
  localStorage.setItem('theme', next);
52
62
  applyTheme(next);
53
- btn.textContent = next;
63
+ applyThemeLabel(next);
54
64
  });
55
65
  window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function() {
56
66
  if (localStorage.getItem('theme') === 'system') applyTheme('system');
57
67
  });
68
+
69
+ const timezoneKey = 'timezone';
70
+ const timezoneBtn = document.getElementById('timezone-toggle');
71
+ function timezoneMode() {
72
+ return localStorage.getItem(timezoneKey) === 'utc' ? 'utc' : 'local';
73
+ }
74
+ function formatTimestamp(iso, mode) {
75
+ const date = new Date(iso);
76
+ if (Number.isNaN(date.getTime())) return null;
77
+ const format = document.documentElement.getAttribute('data-timestamp-format') || '%Y-%m-%d %H:%M %Z';
78
+ const parts = {};
79
+ new Intl.DateTimeFormat('en-US', {
80
+ year: 'numeric',
81
+ month: '2-digit',
82
+ day: '2-digit',
83
+ hour: '2-digit',
84
+ minute: '2-digit',
85
+ second: '2-digit',
86
+ hourCycle: 'h23',
87
+ timeZone: mode === 'utc' ? 'UTC' : undefined,
88
+ timeZoneName: 'short'
89
+ }).formatToParts(date).forEach(function(part) {
90
+ if (part.type !== 'literal') parts[part.type] = part.value;
91
+ });
92
+ const tokens = {
93
+ '%Y': parts.year,
94
+ '%y': parts.year.slice(-2),
95
+ '%m': parts.month,
96
+ '%d': parts.day,
97
+ '%H': parts.hour,
98
+ '%M': parts.minute,
99
+ '%S': parts.second,
100
+ '%Z': parts.timeZoneName,
101
+ '%%': '%'
102
+ };
103
+ return format.replace(/%[%YymdHMSZ]/g, function(token) {
104
+ return Object.prototype.hasOwnProperty.call(tokens, token) ? tokens[token] : token;
105
+ });
106
+ }
107
+ function applyTimezone(mode) {
108
+ const localZone = Intl.DateTimeFormat().resolvedOptions().timeZone || 'Local';
109
+ timezoneBtn.textContent = mode === 'utc' ? 'Time: UTC' : 'Time: Local';
110
+ timezoneBtn.title = mode === 'utc'
111
+ ? 'Uses Coordinated Universal Time.'
112
+ : "Uses your browser's timezone: " + localZone;
113
+ document.querySelectorAll('[data-timestamp]').forEach(function(el) {
114
+ const formatted = formatTimestamp(el.getAttribute('data-timestamp'), mode);
115
+ if (formatted) el.setAttribute('title', formatted);
116
+ });
117
+ }
118
+ applyTimezone(timezoneMode());
119
+ timezoneBtn.addEventListener('click', function() {
120
+ const next = timezoneMode() === 'local' ? 'utc' : 'local';
121
+ localStorage.setItem(timezoneKey, next);
122
+ applyTimezone(next);
123
+ });
58
124
  });
59
125
  })();
60
126
  </script>
@@ -63,7 +129,8 @@
63
129
  <h1 class="font-light text-4xl text-gray-600 flex-auto">
64
130
  <a href="<%= h(index_path) %>" class="hover:text-blue-700">Rollout </a>
65
131
  </h1>
66
- <button id="theme-toggle" class="text-sm text-gray-600 p-2 bg-gray-100 rounded-sm transition-colors duration-150 hover:bg-gray-200">system</button>
132
+ <button type="button" id="timezone-toggle" class="text-sm text-gray-600 p-2 bg-gray-100 rounded-sm transition-colors duration-150 hover:bg-gray-200 mr-2" title="Uses your browser's timezone">Time: Local</button>
133
+ <button type="button" id="theme-toggle" class="text-sm text-gray-600 p-2 bg-gray-100 rounded-sm transition-colors duration-150 hover:bg-gray-200" title="Follows your device's light or dark appearance setting.">Theme: System</button>
67
134
  </div>
68
135
  <%= yield %>
69
136
  </div>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rollout-ui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - FetLife