x
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<div class="alert alert-warning"> Since this page is an iframe, you need to click here first, and then press the shortcut.</div><div data-controller="command-list" class="command-list"> <a href="#" class="nav-item me-3 p-1 text-decoration-none"> <div class="input-group command-list-button ms-2" data-bs-toggle="modal" data-bs-target="#command-list-modal"> <span class="input-group-text" id="basic-addon1"><i class="fa fa-search"></i></span> <div class="form-control form-control-sm text-center py-2"> <span class="translation_missing" title="translation missing: en.command_list.press">Press</span> <kbd class="command-list-icon" data-command-list-target="metaKey"></kbd> + <kbd class="command-list-icon">K</kbd> </div> </div> </a> <div id="command-list-modal" class="modal" tabindex="-1" data-command-list-target="list"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-body"> <div class="input-group mb-3"> <span class="input-group-text" id="basic-addon1"><i class="fa fa-search"></i></span> <input autocomplete="off" type="text" class="form-control" data-command-list-target="searchField" data-action="keydown.up->command-list#previousItem keydown.enter->command-list#performCommand keydown.down->command-list#nextItem"> </div> <div class="list-group" data-command-list-target="listGroup"> <div class="form-label mt-2 fw-medium"><span class="translation_missing" title="translation missing: en.command_list.search">Search</span></div> <a class="list-group-item" href="#" data-action="click->command-list#selectCommand" data-command-list-target="listItem" data-placeholder="Search text" data-command="https://www.google.com/search?q=_1_" data-target="_blank"> Search on Google </a> <div class="form-label mt-2 fw-medium"><span class="translation_missing" title="translation missing: en.command_list.link">Link</span></div> <a class="list-group-item" href="#" data-action="click->command-list#selectCommand" data-command-list-target="listItem" data-placeholder="badge, button, avatar, etc." data-command="/lookbook/inspect/_1_/themes" data-target=""> Go to component </a> </div> </div> <div class="modal-footer d-flex command-list-footer"> <small class="pe-1"><kbd class="command-list-icon">⏎</kbd> <span class="translation_missing" title="translation missing: en.command_list.to_select">To Select</span></small> <small class="pe-1"> <kbd class="command-list-icon">↑</kbd> <kbd class="command-list-icon">↓</kbd> <span class="translation_missing" title="translation missing: en.command_list.to_navigate">To Navigate</span> </small> <small class="pe-1"><kbd class="command-list-icon">ESC</kbd> <span class="translation_missing" title="translation missing: en.command_list.to_exit">To Exit</span></small> </div> </div> </div> </div></div>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<div class="alert alert-warning"> Since this page is an iframe, you need to click here first, and then press the shortcut.</div><%command_list_actions = [ { label: 'Search on Google', command: 'https://www.google.com/search?q=_1_', placeholder: 'Search text', category: 'search', target: '_blank' }, { label: 'Go to component', command: '/lookbook/inspect/_1_/themes', placeholder: 'badge, button, avatar, etc.', category: 'link' }].group_by { |action| action[:category] }%><div data-controller="command-list" class="command-list"> <a href="#" class="nav-item me-3 p-1 text-decoration-none"> <div class="input-group command-list-button ms-2" data-bs-toggle="modal" data-bs-target="#command-list-modal"> <span class="input-group-text" id="basic-addon1"><i class="fa fa-search"></i></span> <div class="form-control form-control-sm text-center py-2"> <%= t('command_list.press') %> <kbd class="command-list-icon" data-command-list-target="metaKey"></kbd> + <kbd class="command-list-icon">K</kbd> </div> </div> </a> <div id="command-list-modal" class="modal" tabindex="-1" data-command-list-target="list"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-body"> <div class="input-group mb-3"> <span class="input-group-text" id="basic-addon1"><i class="fa fa-search"></i></span> <input autocomplete="off" type="text" class="form-control" data-command-list-target="searchField" data-action="keydown.up->command-list#previousItem keydown.enter->command-list#performCommand keydown.down->command-list#nextItem"> </div> <div class="list-group" data-command-list-target="listGroup"> <% command_list_actions.each do |category, actions| %> <div class="form-label mt-2 fw-medium"><%= t("command_list.#{category}") %></div> <% actions.each do |action| %> <a class="list-group-item" href="#" data-action="click->command-list#selectCommand" data-command-list-target="listItem" data-placeholder="<%= action[:placeholder] || '' %>" data-command="<%= action[:command] %>" data-target="<%= action[:target] || '' %>"> <%= action[:label] %> </a> <% end %> <% end %> </div> </div> <div class="modal-footer d-flex command-list-footer"> <small class="pe-1"><kbd class="command-list-icon">⏎</kbd> <%= t('command_list.to_select') %></small> <small class="pe-1"> <kbd class="command-list-icon">↑</kbd> <kbd class="command-list-icon">↓</kbd> <%= t('command_list.to_navigate') %> </small> <small class="pe-1"><kbd class="command-list-icon">ESC</kbd> <%= t('command_list.to_exit') %></small> </div> </div> </div> </div></div>No notes provided.
No params configured.
_command_list.scss
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
.command-list { .command-list-button { min-width: 170px; } .modal-dialog { --bs-modal-width: 600px; --bs-modal-margin: 5rem; --bs-modal-padding: 1.5rem; } .list-group-item { color: $navbar-light-color; &.active { color: white; } } .command-list-footer { color: $navbar-light-color; } .command-list-icon { border: 1px outset $navbar-light-color; }}command_list_controller.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import { Controller } from '@hotwired/stimulus';export default class CommandListController extends Controller { static targets = ['list', 'metaKey', 'searchField', 'listGroup', 'listItem']; connect() { this.modal = new bootstrap.Modal(this.listTarget, {}); this.children = Array.from(this.listGroupTargets[0].children); if (this.listItemTargets.length > 0) { this.switchActive(this.listItemTargets[0]); } this.metaKeyTarget.innerText = this.getMetaKey(); document.addEventListener('keydown', this.handleKeydown.bind(this)); } disconnect() { document.removeEventListener('keydown', this.handleKeydown.bind(this)); } handleKeydown(event) { console.log('handling keydown', event.metaKey, event.key); if ((event.metaKey) && event.key === 'k') { this.openModal(); } } openModal() { this.modal.show(); this.searchField().focus(); } performCommand(event) { event.preventDefault(); const searchParts = this.searchField().value.split(',').map((part) => part.trim()); const command = this.activeCommand.dataset.command .replace('_1_', searchParts[0]) .replace('_2_', searchParts[1]) .replace('_3_', searchParts[2]); if (this.activeCommand.dataset.target === '_blank') { window.open(command, '_blank'); } else { Turbo.visit(command); } } getMetaKey() { const userAgent = window.navigator.userAgent.toLowerCase(); if (userAgent.includes(' mac ')) { return '⌘'; } return 'CTRL'; } switchActive(element) { if (this.activeCommand) { this.activeCommand.classList.remove('active'); } element.classList.add('active'); this.activeCommand = element; this.searchField().placeholder = element.dataset.placeholder ?? ''; } selectCommand(event) { event.preventDefault(); this.switchActive(event.currentTarget); this.searchField().focus(); } previousItem() { const previous = this.listItemTargets[this.listItemTargets.indexOf(this.activeCommand) - 1]; if (previous) { this.switchActive(previous); } } nextItem() { const next = this.listItemTargets[this.listItemTargets.indexOf(this.activeCommand) + 1]; if (next) { this.switchActive(next); } } searchField() { return this.searchFieldTarget; }}