@antha/gamepad-type 0.1.0

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 (66) hide show
  1. package/LICENSE-CC0 +121 -0
  2. package/LICENSE-MIT +21 -0
  3. package/README.md +102 -0
  4. package/dist/create.d.ts +10 -0
  5. package/dist/create.js +20 -0
  6. package/dist/default-layouts.d.ts +8 -0
  7. package/dist/default-layouts.js +339 -0
  8. package/dist/default-models.d.ts +14 -0
  9. package/dist/default-models.js +46 -0
  10. package/dist/demo/elements/gamepad/vir-gamepad-axe.element.d.ts +7 -0
  11. package/dist/demo/elements/gamepad/vir-gamepad-axe.element.js +61 -0
  12. package/dist/demo/elements/gamepad/vir-gamepad-button.element.d.ts +7 -0
  13. package/dist/demo/elements/gamepad/vir-gamepad-button.element.js +87 -0
  14. package/dist/demo/elements/gamepad/vir-gamepad-input.element.d.ts +7 -0
  15. package/dist/demo/elements/gamepad/vir-gamepad-input.element.js +38 -0
  16. package/dist/demo/elements/gamepad/vir-gamepad-inputs.element.d.ts +20 -0
  17. package/dist/demo/elements/gamepad/vir-gamepad-inputs.element.js +110 -0
  18. package/dist/demo/elements/gamepad/vir-gamepad-name.element.d.ts +11 -0
  19. package/dist/demo/elements/gamepad/vir-gamepad-name.element.js +158 -0
  20. package/dist/demo/elements/gamepad/vir-gamepad.element.d.ts +8 -0
  21. package/dist/demo/elements/gamepad/vir-gamepad.element.js +41 -0
  22. package/dist/demo/elements/modals/vir-create-new-type.modal.element.d.ts +9 -0
  23. package/dist/demo/elements/modals/vir-create-new-type.modal.element.js +135 -0
  24. package/dist/demo/elements/modals/vir-edit-mappings.modal.element.d.ts +29 -0
  25. package/dist/demo/elements/modals/vir-edit-mappings.modal.element.js +185 -0
  26. package/dist/demo/elements/modals/vir-modal.element.d.ts +3 -0
  27. package/dist/demo/elements/modals/vir-modal.element.js +88 -0
  28. package/dist/demo/elements/vir-demo.element.d.ts +27 -0
  29. package/dist/demo/elements/vir-demo.element.js +469 -0
  30. package/dist/demo/events/modal-close.event.d.ts +1 -0
  31. package/dist/demo/events/modal-close.event.js +2 -0
  32. package/dist/demo/events/selected-gamepad-index-change.event.d.ts +1 -0
  33. package/dist/demo/events/selected-gamepad-index-change.event.js +2 -0
  34. package/dist/demo/gamepad-brand-color.d.ts +1 -0
  35. package/dist/demo/gamepad-brand-color.js +14 -0
  36. package/dist/demo/global-vars.d.ts +6 -0
  37. package/dist/demo/global-vars.js +3 -0
  38. package/dist/demo/overrides.d.ts +7 -0
  39. package/dist/demo/overrides.js +1 -0
  40. package/dist/demo/sentry-setup.d.ts +1 -0
  41. package/dist/demo/sentry-setup.js +11 -0
  42. package/dist/demo/util/casing.d.ts +1 -0
  43. package/dist/demo/util/casing.js +7 -0
  44. package/dist/demo/util/check-changes.d.ts +6 -0
  45. package/dist/demo/util/check-changes.js +28 -0
  46. package/dist/demo/util/local-save-data.d.ts +9 -0
  47. package/dist/demo/util/local-save-data.js +36 -0
  48. package/dist/demo/util/user-agent.d.ts +6 -0
  49. package/dist/demo/util/user-agent.js +2 -0
  50. package/dist/demo/util/window-listener.d.ts +1 -0
  51. package/dist/demo/util/window-listener.js +6 -0
  52. package/dist/find-matches.d.ts +42 -0
  53. package/dist/find-matches.js +77 -0
  54. package/dist/gamepad-layout.d.ts +29 -0
  55. package/dist/gamepad-layout.js +1 -0
  56. package/dist/gamepad-model.d.ts +67 -0
  57. package/dist/gamepad-model.js +69 -0
  58. package/dist/index.d.ts +8 -0
  59. package/dist/index.js +8 -0
  60. package/dist/known-input.d.ts +126 -0
  61. package/dist/known-input.js +128 -0
  62. package/dist/system-versions.d.ts +25 -0
  63. package/dist/system-versions.js +26 -0
  64. package/dist/util/lowercase-keys.d.ts +2 -0
  65. package/dist/util/lowercase-keys.js +10 -0
  66. package/package.json +67 -0
@@ -0,0 +1,135 @@
1
+ import { assert } from '@augment-vir/assert';
2
+ import { waitForAnimationFrame } from '@augment-vir/web';
3
+ import { css, defineElement, defineElementEvent, html, listen } from 'element-vir';
4
+ import { ViraButton, ViraInput, noNativeFormStyles } from 'vira';
5
+ import { findMatchingGamepadModel } from '../../../index.js';
6
+ import { ModalClose } from '../../events/modal-close.event.js';
7
+ import { SelectedGamepadIndexChange } from '../../events/selected-gamepad-index-change.event.js';
8
+ import { kebabCaseToNameCase } from '../../util/casing.js';
9
+ import { VirModal } from './vir-modal.element.js';
10
+ export const VirCreateNewTypeModal = defineElement()({
11
+ tagName: 'vir-create-new-type-modal',
12
+ events: {
13
+ newModelCreate: defineElementEvent(),
14
+ },
15
+ styles: css `
16
+ .wrapper {
17
+ display: flex;
18
+ flex-direction: column;
19
+ align-items: stretch;
20
+ gap: 24px;
21
+ }
22
+
23
+ select {
24
+ ${noNativeFormStyles};
25
+ flex-grow: 1;
26
+ cursor: pointer;
27
+ padding: 2px 4px;
28
+ font: inherit;
29
+ margin: 2px 0;
30
+ }
31
+
32
+ .selected-gamepad {
33
+ display: flex;
34
+ flex-direction: column;
35
+ }
36
+
37
+ .new-model-name {
38
+ display: flex;
39
+ flex-direction: column;
40
+ gap: 16px;
41
+ }
42
+
43
+ ${ViraInput} {
44
+ width: 100%;
45
+ }
46
+ `,
47
+ state() {
48
+ return {
49
+ newModelName: '',
50
+ };
51
+ },
52
+ init({ host }) {
53
+ setTimeout(async () => {
54
+ await waitForAnimationFrame();
55
+ const viraInput = host.shadowRoot.querySelector(ViraInput.tagName);
56
+ if (viraInput) {
57
+ assert.instanceOf(viraInput, ViraInput);
58
+ viraInput.shadowRoot.querySelector('input')?.select();
59
+ }
60
+ }, 0);
61
+ },
62
+ render({ inputs, dispatch, state, updateState, events }) {
63
+ const selectedDevice = inputs.gamepadDevices[inputs.selectedGamepadIndex];
64
+ if (!selectedDevice) {
65
+ return html `
66
+ No selected gamepad.
67
+ `;
68
+ }
69
+ const gamepadNameSelectTemplate = html `
70
+ <select
71
+ ${listen('change', (event) => {
72
+ const element = event.currentTarget;
73
+ assert.instanceOf(element, HTMLSelectElement);
74
+ const value = Number(element.value);
75
+ if (!isNaN(value)) {
76
+ dispatch(new SelectedGamepadIndexChange(value));
77
+ }
78
+ })}
79
+ >
80
+ ${inputs.gamepadDevices.map((gamepadDevice, gamepadIndex) => {
81
+ return html `
82
+ <option
83
+ ?selected=${inputs.selectedGamepadIndex === gamepadIndex}
84
+ value=${String(gamepadIndex)}
85
+ title=${gamepadDevice.deviceName}
86
+ >
87
+ &nbsp;${gamepadIndex}: "${gamepadDevice.deviceName}"
88
+ </option>
89
+ `;
90
+ })}
91
+ </select>
92
+ `;
93
+ const { gamepadModel } = findMatchingGamepadModel({
94
+ gamepad: selectedDevice,
95
+ });
96
+ const currentModelTemplate = html `
97
+ <p>Current model: ${gamepadModel ? kebabCaseToNameCase(gamepadModel) : 'unknown'}</p>
98
+ `;
99
+ const currentNameMapTemplate = html `
100
+ <${ViraInput.assign({
101
+ placeholder: 'Enter a new model name',
102
+ value: state.newModelName,
103
+ disableBrowserHelps: true,
104
+ })}
105
+ ${listen(ViraInput.events.valueChange, (event) => {
106
+ updateState({
107
+ newModelName: event.detail,
108
+ });
109
+ })}
110
+ ></${ViraInput}>
111
+ <${ViraButton.assign({
112
+ text: 'Save',
113
+ })}
114
+ ${listen('click', () => {
115
+ dispatch(new events.newModelCreate(state.newModelName));
116
+ dispatch(new ModalClose());
117
+ })}
118
+ ></${ViraButton}>
119
+ `;
120
+ return html `
121
+ <${VirModal.assign({
122
+ modalTitle: 'Enter New Model',
123
+ })}>
124
+ <div class="wrapper">
125
+ <div class="selected-gamepad">
126
+ <span>Selected Gamepad by Name:</span>
127
+ ${gamepadNameSelectTemplate}
128
+ </div>
129
+ <div class="current-model">${currentModelTemplate}</div>
130
+ <div class="new-model-name">${currentNameMapTemplate}</div>
131
+ </div>
132
+ </${VirModal}>
133
+ `;
134
+ },
135
+ });
@@ -0,0 +1,29 @@
1
+ import { type GamepadDevice, type GamepadInputValue, type InputDeviceHandler } from 'input-device-handler';
2
+ import { type GamepadLayout, type GamepadModelMap } from '../../../index.js';
3
+ export declare const VirEditMappingsModal: import("element-vir").DeclarativeElementDefinition<"vir-edit-mappings-modal", {
4
+ gamepadDevices: ReadonlyArray<Readonly<GamepadDevice>>;
5
+ savedGamepadLayouts: ReadonlyArray<Readonly<GamepadLayout>>;
6
+ selectedGamepadIndex: number;
7
+ selectedGamepadInput: Readonly<GamepadInputValue> | undefined;
8
+ savedGamepadModelMap: Readonly<GamepadModelMap>;
9
+ inputDeviceHandler: Readonly<InputDeviceHandler>;
10
+ }, {
11
+ cleanup: undefined | (() => void);
12
+ }, {
13
+ inputMapSave: import("element-vir").DefineEvent<Readonly<{
14
+ inputName: string;
15
+ mappedName: string;
16
+ }>>;
17
+ selectedGamepadInputChange: import("element-vir").DefineEvent<Readonly<Readonly<{
18
+ deviceName: string;
19
+ deviceKey: import("input-device-handler").GamepadInputDeviceKey;
20
+ deviceType: import("input-device-handler").InputDeviceType;
21
+ inputName: string;
22
+ inputValue: number;
23
+ details: Readonly<Readonly<{
24
+ inputName: string;
25
+ value: number;
26
+ inputType: import("input-device-handler").DeviceInputType;
27
+ }>>;
28
+ }>> | undefined>;
29
+ }, "vir-edit-mappings-modal-", "vir-edit-mappings-modal-", readonly [], readonly []>;
@@ -0,0 +1,185 @@
1
+ import { assert } from '@augment-vir/assert';
2
+ import { waitForAnimationFrame } from '@augment-vir/web';
3
+ import { css, defineElement, defineElementEvent, html, listen } from 'element-vir';
4
+ import { CurrentInputsChangedEvent, } from 'input-device-handler';
5
+ import { ViraInput, noNativeFormStyles } from 'vira';
6
+ import { findMatchingGamepadLayout, findMatchingGamepadModel, } from '../../../index.js';
7
+ import { SelectedGamepadIndexChange } from '../../events/selected-gamepad-index-change.event.js';
8
+ import { kebabCaseToNameCase } from '../../util/casing.js';
9
+ import { VirGamepadInput } from '../gamepad/vir-gamepad-input.element.js';
10
+ import { VirModal } from './vir-modal.element.js';
11
+ export const VirEditMappingsModal = defineElement()({
12
+ tagName: 'vir-edit-mappings-modal',
13
+ events: {
14
+ inputMapSave: defineElementEvent(),
15
+ selectedGamepadInputChange: defineElementEvent(),
16
+ },
17
+ styles: css `
18
+ .wrapper {
19
+ display: flex;
20
+ flex-direction: column;
21
+ align-items: stretch;
22
+ gap: 24px;
23
+ }
24
+
25
+ select {
26
+ ${noNativeFormStyles};
27
+ flex-grow: 1;
28
+ cursor: pointer;
29
+ padding: 2px 4px;
30
+ font: inherit;
31
+ margin: 2px 0;
32
+ }
33
+
34
+ .selected-gamepad {
35
+ display: flex;
36
+ flex-direction: column;
37
+ }
38
+
39
+ .current-input {
40
+ display: flex;
41
+ justify-content: center;
42
+ }
43
+ `,
44
+ state() {
45
+ return {
46
+ cleanup: undefined,
47
+ };
48
+ },
49
+ init({ state, updateState, inputs, host, dispatch, events }) {
50
+ function focusInput() {
51
+ setTimeout(async () => {
52
+ await waitForAnimationFrame();
53
+ const viraInput = host.shadowRoot.querySelector(ViraInput.tagName);
54
+ if (viraInput) {
55
+ assert.instanceOf(viraInput, ViraInput);
56
+ viraInput.shadowRoot.querySelector('input')?.select();
57
+ }
58
+ }, 0);
59
+ }
60
+ focusInput();
61
+ if (!state.cleanup) {
62
+ const cleanup = inputs.inputDeviceHandler.listen(CurrentInputsChangedEvent, (event) => {
63
+ const selectedDevice = inputs.gamepadDevices[inputs.selectedGamepadIndex];
64
+ if (!selectedDevice) {
65
+ return;
66
+ }
67
+ const newInputs = event.detail.inputs.newInputs
68
+ .filter((newInput) => newInput.deviceKey === selectedDevice.deviceKey)
69
+ .sort((a, b) => Math.abs(b.inputValue) - Math.abs(a.inputValue));
70
+ if (!newInputs.length) {
71
+ return;
72
+ }
73
+ const firstNewInput = newInputs[0];
74
+ if (firstNewInput &&
75
+ inputs.selectedGamepadInput?.inputName !== firstNewInput.inputName) {
76
+ focusInput();
77
+ dispatch(new events.selectedGamepadInputChange(firstNewInput));
78
+ }
79
+ });
80
+ updateState({
81
+ cleanup,
82
+ });
83
+ }
84
+ },
85
+ cleanup({ state, updateState }) {
86
+ state.cleanup?.();
87
+ updateState({
88
+ cleanup: undefined,
89
+ });
90
+ },
91
+ render({ inputs, dispatch, events }) {
92
+ const selectedDevice = inputs.gamepadDevices[inputs.selectedGamepadIndex];
93
+ if (!selectedDevice) {
94
+ return html `
95
+ No selected gamepad.
96
+ `;
97
+ }
98
+ const { gamepadBrand } = findMatchingGamepadModel({
99
+ gamepad: selectedDevice.deviceName,
100
+ gamepadModelMap: inputs.savedGamepadModelMap,
101
+ });
102
+ if (inputs.selectedGamepadInput &&
103
+ (selectedDevice.deviceKey !== inputs.selectedGamepadInput.deviceKey ||
104
+ selectedDevice.deviceName !== inputs.selectedGamepadInput.deviceName)) {
105
+ dispatch(new events.selectedGamepadInputChange(undefined));
106
+ }
107
+ const gamepadTypeSelectTemplate = html `
108
+ <select
109
+ ${listen('change', (event) => {
110
+ const element = event.currentTarget;
111
+ assert.instanceOf(element, HTMLSelectElement);
112
+ const value = Number(element.value);
113
+ if (!isNaN(value)) {
114
+ dispatch(new SelectedGamepadIndexChange(value));
115
+ }
116
+ })}
117
+ >
118
+ ${inputs.gamepadDevices.map((gamepadDevice, gamepadIndex) => {
119
+ const { gamepadModel } = findMatchingGamepadModel({
120
+ gamepad: gamepadDevice.deviceName,
121
+ gamepadModelMap: inputs.savedGamepadModelMap,
122
+ });
123
+ return html `
124
+ <option
125
+ ?selected=${inputs.selectedGamepadIndex === gamepadIndex}
126
+ value=${String(gamepadIndex)}
127
+ >
128
+ &nbsp;${gamepadIndex}:
129
+ ${gamepadModel ? kebabCaseToNameCase(gamepadModel) : 'unknown'}
130
+ </option>
131
+ `;
132
+ })}
133
+ </select>
134
+ `;
135
+ const currentInput = inputs.selectedGamepadInput
136
+ ? selectedDevice.deviceDetails.inputsByName[inputs.selectedGamepadInput.inputName]
137
+ : undefined;
138
+ const currentInputTemplate = currentInput
139
+ ? html `
140
+ <${VirGamepadInput.assign({
141
+ gamepadLayout: undefined,
142
+ gamepadInput: currentInput,
143
+ gamepadBrand,
144
+ })}></${VirGamepadInput}>
145
+ `
146
+ : html `
147
+ <p>Press an input on this gamepad to map it.</p>
148
+ `;
149
+ const matchingLayout = findMatchingGamepadLayout({
150
+ gamepad: selectedDevice,
151
+ layouts: inputs.savedGamepadLayouts,
152
+ gamepadModelMap: inputs.savedGamepadModelMap,
153
+ });
154
+ const currentNameMapTemplate = currentInput
155
+ ? html `
156
+ <${ViraInput.assign({
157
+ placeholder: 'Enter an input name',
158
+ value: matchingLayout?.inputMappings[currentInput.inputName] || '',
159
+ disableBrowserHelps: true,
160
+ })}
161
+ ${listen(ViraInput.events.valueChange, (event) => {
162
+ dispatch(new events.inputMapSave({
163
+ inputName: currentInput.inputName,
164
+ mappedName: event.detail,
165
+ }));
166
+ })}
167
+ ></${ViraInput}>
168
+ `
169
+ : '';
170
+ return html `
171
+ <${VirModal.assign({
172
+ modalTitle: 'Edit Mappings',
173
+ })}>
174
+ <div class="wrapper">
175
+ <div class="selected-gamepad">
176
+ <span>Selected Gamepad:</span>
177
+ ${gamepadTypeSelectTemplate}
178
+ </div>
179
+ <div class="current-input">${currentInputTemplate}</div>
180
+ <div class="current-mapping">${currentNameMapTemplate}</div>
181
+ </div>
182
+ </${VirModal}>
183
+ `;
184
+ },
185
+ });
@@ -0,0 +1,3 @@
1
+ export declare const VirModal: import("element-vir").DeclarativeElementDefinition<"vir-modal", {
2
+ modalTitle: string;
3
+ }, {}, {}, "vir-modal-", "vir-modal-", readonly [], readonly []>;
@@ -0,0 +1,88 @@
1
+ import { css, defineElement, html, listen } from 'element-vir';
2
+ import { CloseX24Icon, ViraIcon, noNativeFormStyles, noNativeSpacing } from 'vira';
3
+ import { ModalClose } from '../../events/modal-close.event.js';
4
+ export const VirModal = defineElement()({
5
+ tagName: 'vir-modal',
6
+ styles: css `
7
+ .background {
8
+ display: flex;
9
+ justify-content: center;
10
+ align-items: center;
11
+ position: fixed;
12
+ top: 0;
13
+ left: 0;
14
+ z-index: 9999;
15
+ width: 100vw;
16
+ height: 100vh;
17
+ background-color: rgba(0, 0, 0, 0.8);
18
+ }
19
+
20
+ .modal-wrapper {
21
+ background-color: white;
22
+ border: 4px solid #ddd;
23
+ border-radius: 16px;
24
+ padding: 0 0 32px 16px;
25
+ }
26
+
27
+ h1 {
28
+ ${noNativeSpacing};
29
+ font-size: 1.8em;
30
+ margin-top: 8px;
31
+ margin-bottom: 16px;
32
+ flex-grow: 1;
33
+ }
34
+
35
+ .slot-wrapper {
36
+ margin: 0 32px 16px 16px;
37
+ }
38
+
39
+ .close-x {
40
+ ${noNativeFormStyles};
41
+ padding: 2px;
42
+ font-family: monospace;
43
+ color: #aaa;
44
+ cursor: pointer;
45
+ transition: 100ms;
46
+ }
47
+
48
+ .close-x:hover {
49
+ color: black;
50
+ }
51
+
52
+ header {
53
+ display: flex;
54
+ align-items: flex-start;
55
+ }
56
+ `,
57
+ render({ inputs, dispatch }) {
58
+ return html `
59
+ <div
60
+ class="background"
61
+ ${listen('mousedown', (event) => {
62
+ if (event.target === event.currentTarget) {
63
+ dispatch(new ModalClose());
64
+ }
65
+ })}
66
+ >
67
+ <div class="modal-wrapper">
68
+ <header>
69
+ <h1>${inputs.modalTitle}</h1>
70
+ <button
71
+ class="close-x"
72
+ ${listen('click', () => {
73
+ dispatch(new ModalClose());
74
+ })}
75
+ >
76
+ <${ViraIcon.assign({
77
+ icon: CloseX24Icon,
78
+ })}></${ViraIcon}>
79
+ </button>
80
+ </header>
81
+ <div class="slot-wrapper">
82
+ <slot></slot>
83
+ </div>
84
+ </div>
85
+ </div>
86
+ `;
87
+ },
88
+ });
@@ -0,0 +1,27 @@
1
+ import { type GamepadDevice, type GamepadInputValue, InputDeviceHandler } from 'input-device-handler';
2
+ import { type GamepadLayout } from '../../index.js';
3
+ import { type GamepadOverrides } from '../overrides.js';
4
+ declare enum EditMode {
5
+ None = "none",
6
+ Mappings = "mappings",
7
+ CreateType = "create-type"
8
+ }
9
+ export declare const VirDemo: import("element-vir").DeclarativeElementDefinition<"vir-demo", {}, {
10
+ deviceHandler: InputDeviceHandler;
11
+ gamepadDevices: ReadonlyArray<Readonly<GamepadDevice>>;
12
+ newInputs: {
13
+ timestamp: number;
14
+ devices: ReadonlyArray<string>;
15
+ };
16
+ gamepadIndexForEditing: number;
17
+ inputForEditing: Readonly<GamepadInputValue> | undefined;
18
+ savedGamepadLayouts: import("element-vir").AsyncProp<readonly Readonly<GamepadLayout>[], void>;
19
+ submittedChanges: import("element-vir").AsyncProp<Readonly<import("../util/check-changes.js").ChangedEntries> | undefined, void>;
20
+ savedGamepadModelMap: import("element-vir").AsyncProp<Readonly<Readonly<{
21
+ [x: string]: string;
22
+ }>>, void>;
23
+ overrides: undefined | GamepadOverrides;
24
+ editMode: EditMode;
25
+ cleanup: (() => void) | undefined;
26
+ }, {}, "vir-demo-", "vir-demo-", readonly [], readonly []>;
27
+ export {};