senren-ui 0.3.0 → 0.4.1

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +147 -0
  3. data/CONTRIBUTING.md +10 -0
  4. data/README.md +36 -0
  5. data/docs/i18n.md +194 -0
  6. data/lib/commands/senren/add/add_command.rb +4 -1
  7. data/lib/commands/senren/locales/locales_command.rb +38 -0
  8. data/lib/generators/senren/install/install_generator.rb +36 -0
  9. data/lib/generators/senren/install/templates/base_component.rb.tt +39 -0
  10. data/lib/generators/senren/install/templates/conventions.md.tt +16 -0
  11. data/lib/generators/senren/install/templates/installed_components.yml.tt +1 -1
  12. data/lib/senren/rails/agent_rules_writer.rb +4 -0
  13. data/lib/senren/rails/base_component_patch.rb +24 -0
  14. data/lib/senren/rails/component_copier.rb +21 -0
  15. data/lib/senren/rails/doctor.rb +5 -1
  16. data/lib/senren/rails/host_paths.rb +5 -0
  17. data/lib/senren/rails/locale_installer.rb +90 -0
  18. data/lib/senren/rails/version.rb +1 -1
  19. data/lib/senren/rails.rb +1 -0
  20. data/lib/tasks/senren.rake +9 -0
  21. data/templates/components/accordion/accordion_component.html.erb +22 -4
  22. data/templates/components/accordion/accordion_component.rb +2 -1
  23. data/templates/components/alert_dialog/alert_dialog_component.html.erb +1 -1
  24. data/templates/components/api_key_field/api_key_field_component.html.erb +1 -1
  25. data/templates/components/api_key_field/api_key_field_component.rb +8 -1
  26. data/templates/components/app_shell/app_shell_component.rb +2 -1
  27. data/templates/components/breadcrumb/breadcrumb_component.rb +2 -1
  28. data/templates/components/bulk_action_bar/bulk_action_bar_component.html.erb +1 -1
  29. data/templates/components/bulk_action_bar/bulk_action_bar_component.rb +4 -2
  30. data/templates/components/calendar/calendar_component.rb +37 -2
  31. data/templates/components/carousel/carousel_component.html.erb +5 -5
  32. data/templates/components/carousel/carousel_component.rb +12 -1
  33. data/templates/components/cart/cart_component.html.erb +6 -6
  34. data/templates/components/cart/cart_component.rb +11 -2
  35. data/templates/components/clipboard/clipboard_component.html.erb +1 -1
  36. data/templates/components/clipboard/clipboard_component.rb +6 -1
  37. data/templates/components/collapsible/collapsible_component.rb +1 -1
  38. data/templates/components/combobox/combobox_component.rb +2 -1
  39. data/templates/components/command/command_component.rb +3 -1
  40. data/templates/components/data_table/data_table_component.html.erb +3 -3
  41. data/templates/components/data_table/data_table_component.rb +16 -1
  42. data/templates/components/date_picker/date_picker_component.html.erb +2 -2
  43. data/templates/components/date_picker/date_picker_component.rb +2 -1
  44. data/templates/components/dialog/dialog_component.html.erb +2 -2
  45. data/templates/components/filter_bar/filter_bar_component.rb +1 -1
  46. data/templates/components/form/form_component.html.erb +1 -1
  47. data/templates/components/form/form_component.rb +21 -0
  48. data/templates/components/invite_member_dialog/invite_member_dialog_component.html.erb +9 -8
  49. data/templates/components/invite_member_dialog/invite_member_dialog_component.rb +11 -3
  50. data/templates/components/native_select/native_select_component.rb +12 -3
  51. data/templates/components/pagination/pagination_component.html.erb +2 -2
  52. data/templates/components/pagination/pagination_component.rb +2 -1
  53. data/templates/components/product_card/product_card_component.html.erb +1 -1
  54. data/templates/components/product_card/product_card_component.rb +2 -2
  55. data/templates/components/rich_text_editor_lite/rich_text_editor_lite_component.html.erb +2 -16
  56. data/templates/components/rich_text_editor_lite/rich_text_editor_lite_component.rb +32 -1
  57. data/templates/components/search_input/search_input_component.rb +2 -1
  58. data/templates/components/sheet/sheet_component.html.erb +2 -2
  59. data/templates/components/sidebar/sidebar_component.html.erb +2 -2
  60. data/templates/components/sidebar/sidebar_component.rb +4 -1
  61. data/templates/components/table/table_component.html.erb +1 -1
  62. data/templates/components/tabs/tabs_component.rb +4 -2
  63. data/templates/components/theme_toggle/theme_toggle_component.html.erb +17 -3
  64. data/templates/components/theme_toggle/theme_toggle_component.rb +6 -1
  65. data/templates/components/top_nav/top_nav_component.html.erb +1 -1
  66. data/templates/components/top_nav/top_nav_component.rb +3 -2
  67. data/templates/controllers/accordion_controller.js +68 -3
  68. data/templates/controllers/api_key_field_controller.js +3 -2
  69. data/templates/controllers/carousel_controller.js +6 -1
  70. data/templates/controllers/clipboard_controller.js +3 -3
  71. data/templates/controllers/rich_text_editor_lite_controller.js +2 -2
  72. data/templates/controllers/theme_toggle_controller.js +13 -3
  73. data/templates/locales/senren.en.yml +151 -0
  74. data/templates/locales/senren.vi.yml +151 -0
  75. metadata +6 -1
@@ -6,7 +6,8 @@ export default class extends Controller {
6
6
  static values = {
7
7
  revealLabel: String,
8
8
  hideLabel: String,
9
- copyLabel: String
9
+ copyLabel: String,
10
+ copiedStatus: String
10
11
  }
11
12
 
12
13
  connect() {
@@ -31,6 +32,6 @@ export default class extends Controller {
31
32
  this.inputTarget.setSelectionRange(0, 0)
32
33
  }
33
34
 
34
- if (this.hasStatusTarget) this.statusTarget.textContent = `${this.copyLabelValue} complete`
35
+ if (this.hasStatusTarget) this.statusTarget.textContent = this.copiedStatusValue
35
36
  }
36
37
  }
@@ -4,6 +4,7 @@ import { Controller } from "@hotwired/stimulus"
4
4
  // Local UI: previous/next controls, dots, and arrow-key navigation.
5
5
  export default class extends Controller {
6
6
  static targets = ["slide", "dot", "status"]
7
+ static values = { statusTemplate: String }
7
8
 
8
9
  connect() {
9
10
  this.index = 0
@@ -45,6 +46,10 @@ export default class extends Controller {
45
46
  this.dotTargets.forEach((dot, dotIndex) => {
46
47
  dot.setAttribute("aria-current", dotIndex === this.index ? "true" : "false")
47
48
  })
48
- if (this.hasStatusTarget) this.statusTarget.textContent = `Slide ${this.index + 1} of ${count}`
49
+ if (this.hasStatusTarget && this.statusTemplateValue) {
50
+ this.statusTarget.textContent = this.statusTemplateValue
51
+ .replaceAll("%{current}", this.index + 1)
52
+ .replaceAll("%{total}", count)
53
+ }
49
54
  }
50
55
  }
@@ -4,7 +4,7 @@ import { Controller } from "@hotwired/stimulus"
4
4
  // Local UI: copy text to the clipboard and announce success.
5
5
  export default class extends Controller {
6
6
  static targets = ["source", "button", "status"]
7
- static values = { copiedLabel: String }
7
+ static values = { copiedLabel: String, copiedStatus: String }
8
8
 
9
9
  async copy() {
10
10
  const value = this.sourceTarget.value || this.sourceTarget.textContent
@@ -13,8 +13,8 @@ export default class extends Controller {
13
13
  if (!this.hasButtonTarget) return
14
14
 
15
15
  const original = this.buttonTarget.textContent
16
- this.buttonTarget.textContent = this.copiedLabelValue || "Copied"
17
- if (this.hasStatusTarget) this.statusTarget.textContent = "Copied to clipboard"
16
+ if (this.copiedLabelValue) this.buttonTarget.textContent = this.copiedLabelValue
17
+ if (this.hasStatusTarget) this.statusTarget.textContent = this.copiedStatusValue
18
18
 
19
19
  clearTimeout(this._resetTimer)
20
20
  this._resetTimer = setTimeout(() => {
@@ -16,7 +16,7 @@ const DROPPED_PASTE_TAGS = new Set(["SCRIPT", "STYLE", "NOSCRIPT", "TEMPLATE", "
16
16
  // Local UI: tiny contenteditable toolbar synced to a hidden textarea.
17
17
  export default class extends Controller {
18
18
  static targets = ["editor", "input", "button"]
19
- static values = { debug: Boolean }
19
+ static values = { debug: Boolean, linkPrompt: String }
20
20
 
21
21
  connect() {
22
22
  this.savedRange = null
@@ -59,7 +59,7 @@ export default class extends Controller {
59
59
  this.debug("format:start", () => ({ command, before: this.snapshot() }))
60
60
  this.restoreSelection()
61
61
  if (command === "createLink") {
62
- const url = window.prompt("Paste a URL")
62
+ const url = window.prompt(this.linkPromptValue)
63
63
  this.debug("createLink:prompt", () => ({ url, afterPrompt: this.snapshot() }))
64
64
  if (!url) return
65
65
  this.restoreSelection()
@@ -3,7 +3,8 @@ import { Controller } from "@hotwired/stimulus"
3
3
  // senren--theme-toggle
4
4
  // Local UI: toggles the document dark class and stores the preference.
5
5
  export default class extends Controller {
6
- static targets = ["label", "icon"]
6
+ static targets = ["label", "iconLight", "iconDark"]
7
+ static values = { lightLabel: String, darkLabel: String }
7
8
 
8
9
  connect() {
9
10
  this._sync()
@@ -18,7 +19,16 @@ export default class extends Controller {
18
19
  _sync() {
19
20
  const dark = document.documentElement.classList.contains("dark")
20
21
  this.element.setAttribute("aria-pressed", dark ? "true" : "false")
21
- if (this.hasLabelTarget) this.labelTarget.textContent = dark ? "Light theme" : "Dark theme"
22
- if (this.hasIconTarget) this.iconTarget.textContent = dark ? "L" : "D"
22
+ if (this.hasLabelTarget) this.labelTarget.textContent = dark ? this.lightLabelValue : this.darkLabelValue
23
+
24
+ // The icon names the theme the click switches *to*, matching the label: in
25
+ // light mode the label reads "Dark theme", so the moon is showing. Showing
26
+ // the current state instead would have icon and label contradict each other.
27
+ //
28
+ // Swapping visibility rather than rewriting one element's text also means
29
+ // the icons are markup the server rendered, so the right one is on screen
30
+ // before this controller connects.
31
+ if (this.hasIconLightTarget) this.iconLightTarget.hidden = !dark
32
+ if (this.hasIconDarkTarget) this.iconDarkTarget.hidden = dark
23
33
  }
24
34
  }
@@ -0,0 +1,151 @@
1
+ # Senren UI - English strings. This is the canonical wording.
2
+ #
3
+ # GENERATED by bin/i18n-sync from the senren_t(key, default:) calls in
4
+ # templates/components. Do not edit it by hand: change the `default:` in the
5
+ # component and run bin/i18n-sync, or a test will fail.
6
+ #
7
+ # Copy this file to senren.<locale>.yml, change the top-level `en:` to your
8
+ # locale, and translate the values. Keep every %{variable} - they are filled in
9
+ # when the component renders. A key you leave out falls back to the English.
10
+ en:
11
+ senren:
12
+ shared:
13
+ open: "Open"
14
+ close: "Close"
15
+ accordion:
16
+ section: "Section %{number}"
17
+ api_key_field:
18
+ label: "API key"
19
+ reveal: "Reveal"
20
+ hide: "Hide"
21
+ copy: "Copy"
22
+ copied_status: "%{label} complete"
23
+ app_shell:
24
+ skip_to_content: "Skip to content"
25
+ breadcrumb:
26
+ label: "Breadcrumb"
27
+ bulk_action_bar:
28
+ items: "items"
29
+ selected: "%{count} %{item_label} selected"
30
+ ready: "Selection ready"
31
+ calendar:
32
+ title: "%{month} %{year}"
33
+ months:
34
+ january: "January"
35
+ february: "February"
36
+ march: "March"
37
+ april: "April"
38
+ may: "May"
39
+ june: "June"
40
+ july: "July"
41
+ august: "August"
42
+ september: "September"
43
+ october: "October"
44
+ november: "November"
45
+ december: "December"
46
+ weekdays:
47
+ sun: "Sun"
48
+ mon: "Mon"
49
+ tue: "Tue"
50
+ wed: "Wed"
51
+ thu: "Thu"
52
+ fri: "Fri"
53
+ sat: "Sat"
54
+ carousel:
55
+ label: "Carousel"
56
+ previous: "Previous slide"
57
+ next: "Next slide"
58
+ go_to: "Go to slide %{number}"
59
+ status: "Slide %{current} of %{total}"
60
+ cart:
61
+ title: "Cart"
62
+ empty: "Your cart is empty."
63
+ checkout: "Checkout"
64
+ subtotal: "Subtotal"
65
+ remove: "Remove"
66
+ quantity_group: "%{name} quantity"
67
+ decrease: "Decrease %{name} quantity"
68
+ increase: "Increase %{name} quantity"
69
+ remove_item: "Remove %{name}"
70
+ clipboard:
71
+ copy: "Copy"
72
+ copied: "Copied"
73
+ copied_status: "Copied to clipboard"
74
+ collapsible:
75
+ details: "Details"
76
+ combobox:
77
+ placeholder: "Search..."
78
+ command:
79
+ placeholder: "Type a command..."
80
+ label: "Command menu"
81
+ empty: "No results found."
82
+ data_table:
83
+ empty: "No records found."
84
+ sort: "Sort"
85
+ date_picker:
86
+ placeholder: "Select date"
87
+ today: "Today"
88
+ clear: "Clear"
89
+ filter_bar:
90
+ label: "Filters"
91
+ invite_member_dialog:
92
+ title: "Invite teammate"
93
+ description: "Send an invitation to join this workspace."
94
+ role_member: "Member"
95
+ role_admin: "Admin"
96
+ button: "Invite member"
97
+ email: "Email"
98
+ email_placeholder: "teammate@company.com"
99
+ role: "Role"
100
+ cancel: "Cancel"
101
+ send: "Send invite"
102
+ pagination:
103
+ label: "Pagination"
104
+ previous: "Previous"
105
+ next: "Next"
106
+ product_card:
107
+ add_to_cart: "Add to cart"
108
+ out_of_stock: "Out of stock"
109
+ no_image: "No image"
110
+ rich_text_editor_lite:
111
+ label: "Content"
112
+ placeholder: "Write something..."
113
+ link_prompt: "Paste a URL"
114
+ toolbar:
115
+ paragraph: "Paragraph"
116
+ heading_1: "Heading 1"
117
+ heading_2: "Heading 2"
118
+ heading_3: "Heading 3"
119
+ bold: "Bold"
120
+ italic: "Italic"
121
+ create_link: "Create link"
122
+ bulleted_list: "Bulleted list"
123
+ numbered_list: "Numbered list"
124
+ align_left: "Align left"
125
+ align_center: "Align center"
126
+ align_right: "Align right"
127
+ justify: "Justify"
128
+ toolbar_short:
129
+ link: "Link"
130
+ list: "List"
131
+ left: "Left"
132
+ center: "Center"
133
+ right: "Right"
134
+ justify: "Justify"
135
+ search_input:
136
+ placeholder: "Search..."
137
+ label: "Search"
138
+ sidebar:
139
+ brand: "Senren"
140
+ label: "Primary"
141
+ toggle: "Toggle sidebar"
142
+ tabs:
143
+ label: "Tabs"
144
+ tab: "Tab %{number}"
145
+ theme_toggle:
146
+ label: "Toggle theme"
147
+ light: "Light theme"
148
+ dark: "Dark theme"
149
+ top_nav:
150
+ label: "Global"
151
+ brand: "Senren"
@@ -0,0 +1,151 @@
1
+ # Senren UI - Vietnamese strings (Tiếng Việt).
2
+ #
3
+ # Written by hand, in the register of a Vietnamese product or support team:
4
+ # short and plain, with the words people already say in English ("email", "slide",
5
+ # "tab") left as they are. The keys and every %{variable} match senren.en.yml;
6
+ # test/i18n_catalog_test.rb fails if they stop matching.
7
+ #
8
+ # Copy this file to config/locales/ (bin/rails senren:locales vi), then change any
9
+ # wording you like - it is your file. A key you delete falls back to the English.
10
+ vi:
11
+ senren:
12
+ shared:
13
+ open: "Mở"
14
+ close: "Đóng"
15
+ accordion:
16
+ section: "Mục %{number}"
17
+ api_key_field:
18
+ label: "Khóa API"
19
+ reveal: "Hiện"
20
+ hide: "Ẩn"
21
+ copy: "Sao chép"
22
+ copied_status: "%{label} xong"
23
+ app_shell:
24
+ skip_to_content: "Chuyển đến nội dung chính"
25
+ breadcrumb:
26
+ label: "Đường dẫn"
27
+ bulk_action_bar:
28
+ items: "mục"
29
+ selected: "Đã chọn %{count} %{item_label}"
30
+ ready: "Đã chọn xong"
31
+ calendar:
32
+ title: "%{month} năm %{year}"
33
+ months:
34
+ january: "Tháng 1"
35
+ february: "Tháng 2"
36
+ march: "Tháng 3"
37
+ april: "Tháng 4"
38
+ may: "Tháng 5"
39
+ june: "Tháng 6"
40
+ july: "Tháng 7"
41
+ august: "Tháng 8"
42
+ september: "Tháng 9"
43
+ october: "Tháng 10"
44
+ november: "Tháng 11"
45
+ december: "Tháng 12"
46
+ weekdays:
47
+ sun: "CN"
48
+ mon: "T2"
49
+ tue: "T3"
50
+ wed: "T4"
51
+ thu: "T5"
52
+ fri: "T6"
53
+ sat: "T7"
54
+ carousel:
55
+ label: "Trình chiếu"
56
+ previous: "Slide trước"
57
+ next: "Slide sau"
58
+ go_to: "Đến slide %{number}"
59
+ status: "Slide %{current} trên %{total}"
60
+ cart:
61
+ title: "Giỏ hàng"
62
+ empty: "Giỏ hàng của bạn đang trống."
63
+ checkout: "Thanh toán"
64
+ subtotal: "Tạm tính"
65
+ remove: "Xóa"
66
+ quantity_group: "Số lượng %{name}"
67
+ decrease: "Giảm số lượng %{name}"
68
+ increase: "Tăng số lượng %{name}"
69
+ remove_item: "Xóa %{name}"
70
+ clipboard:
71
+ copy: "Sao chép"
72
+ copied: "Đã sao chép"
73
+ copied_status: "Đã sao chép vào bộ nhớ tạm"
74
+ collapsible:
75
+ details: "Chi tiết"
76
+ combobox:
77
+ placeholder: "Tìm kiếm..."
78
+ command:
79
+ placeholder: "Nhập lệnh..."
80
+ label: "Menu lệnh"
81
+ empty: "Không tìm thấy kết quả."
82
+ data_table:
83
+ empty: "Không có dữ liệu."
84
+ sort: "Sắp xếp"
85
+ date_picker:
86
+ placeholder: "Chọn ngày"
87
+ today: "Hôm nay"
88
+ clear: "Xóa"
89
+ filter_bar:
90
+ label: "Bộ lọc"
91
+ invite_member_dialog:
92
+ title: "Mời đồng nghiệp"
93
+ description: "Gửi lời mời tham gia không gian làm việc này."
94
+ role_member: "Thành viên"
95
+ role_admin: "Quản trị viên"
96
+ button: "Mời thành viên"
97
+ email: "Email"
98
+ email_placeholder: "ten@congty.com"
99
+ role: "Vai trò"
100
+ cancel: "Hủy"
101
+ send: "Gửi lời mời"
102
+ pagination:
103
+ label: "Phân trang"
104
+ previous: "Trước"
105
+ next: "Sau"
106
+ product_card:
107
+ add_to_cart: "Thêm vào giỏ"
108
+ out_of_stock: "Hết hàng"
109
+ no_image: "Chưa có ảnh"
110
+ rich_text_editor_lite:
111
+ label: "Nội dung"
112
+ placeholder: "Nhập nội dung..."
113
+ link_prompt: "Dán một URL"
114
+ toolbar:
115
+ paragraph: "Đoạn văn"
116
+ heading_1: "Tiêu đề 1"
117
+ heading_2: "Tiêu đề 2"
118
+ heading_3: "Tiêu đề 3"
119
+ bold: "In đậm"
120
+ italic: "In nghiêng"
121
+ create_link: "Tạo liên kết"
122
+ bulleted_list: "Danh sách dấu đầu dòng"
123
+ numbered_list: "Danh sách đánh số"
124
+ align_left: "Căn trái"
125
+ align_center: "Căn giữa"
126
+ align_right: "Căn phải"
127
+ justify: "Căn đều"
128
+ toolbar_short:
129
+ link: "Liên kết"
130
+ list: "Danh sách"
131
+ left: "Trái"
132
+ center: "Giữa"
133
+ right: "Phải"
134
+ justify: "Đều"
135
+ search_input:
136
+ placeholder: "Tìm kiếm..."
137
+ label: "Tìm kiếm"
138
+ sidebar:
139
+ brand: "Senren"
140
+ label: "Điều hướng chính"
141
+ toggle: "Bật/tắt thanh bên"
142
+ tabs:
143
+ label: "Các tab"
144
+ tab: "Tab %{number}"
145
+ theme_toggle:
146
+ label: "Đổi giao diện"
147
+ light: "Giao diện sáng"
148
+ dark: "Giao diện tối"
149
+ top_nav:
150
+ label: "Điều hướng chung"
151
+ brand: "Senren"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: senren-ui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - vutt
@@ -55,9 +55,11 @@ files:
55
55
  - Rakefile
56
56
  - docs/components.md
57
57
  - docs/hot_reload.md
58
+ - docs/i18n.md
58
59
  - docs/performance_testing.md
59
60
  - docs/visual_style.md
60
61
  - lib/commands/senren/add/add_command.rb
62
+ - lib/commands/senren/locales/locales_command.rb
61
63
  - lib/generators/senren/component/component_generator.rb
62
64
  - lib/generators/senren/component/templates/component.html.erb.tt
63
65
  - lib/generators/senren/component/templates/component.rb.tt
@@ -81,6 +83,7 @@ files:
81
83
  - lib/senren/rails/engine.rb
82
84
  - lib/senren/rails/host_paths.rb
83
85
  - lib/senren/rails/llms_writer.rb
86
+ - lib/senren/rails/locale_installer.rb
84
87
  - lib/senren/rails/marker_block.rb
85
88
  - lib/senren/rails/registry.rb
86
89
  - lib/senren/rails/safe_write.rb
@@ -244,6 +247,8 @@ files:
244
247
  - templates/controllers/tabs_controller.js
245
248
  - templates/controllers/theme_toggle_controller.js
246
249
  - templates/controllers/tooltip_controller.js
250
+ - templates/locales/senren.en.yml
251
+ - templates/locales/senren.vi.yml
247
252
  homepage: https://www.senren-ui.dev/
248
253
  licenses:
249
254
  - MIT