cjm-fasthtml-token-selector 0.0.1__py3-none-any.whl
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.
- cjm_fasthtml_token_selector/__init__.py +1 -0
- cjm_fasthtml_token_selector/_modidx.py +104 -0
- cjm_fasthtml_token_selector/components/__init__.py +0 -0
- cjm_fasthtml_token_selector/components/inputs.py +46 -0
- cjm_fasthtml_token_selector/components/tokens.py +217 -0
- cjm_fasthtml_token_selector/core/__init__.py +0 -0
- cjm_fasthtml_token_selector/core/config.py +57 -0
- cjm_fasthtml_token_selector/core/constants.py +50 -0
- cjm_fasthtml_token_selector/core/html_ids.py +51 -0
- cjm_fasthtml_token_selector/core/models.py +37 -0
- cjm_fasthtml_token_selector/helpers/__init__.py +0 -0
- cjm_fasthtml_token_selector/helpers/tokenizer.py +76 -0
- cjm_fasthtml_token_selector/js/__init__.py +0 -0
- cjm_fasthtml_token_selector/js/core.py +181 -0
- cjm_fasthtml_token_selector/js/display.py +146 -0
- cjm_fasthtml_token_selector/js/navigation.py +228 -0
- cjm_fasthtml_token_selector/js/repeat.py +119 -0
- cjm_fasthtml_token_selector/keyboard/__init__.py +0 -0
- cjm_fasthtml_token_selector/keyboard/actions.py +157 -0
- cjm_fasthtml_token_selector-0.0.1.dist-info/METADATA +760 -0
- cjm_fasthtml_token_selector-0.0.1.dist-info/RECORD +31 -0
- cjm_fasthtml_token_selector-0.0.1.dist-info/WHEEL +5 -0
- cjm_fasthtml_token_selector-0.0.1.dist-info/entry_points.txt +2 -0
- cjm_fasthtml_token_selector-0.0.1.dist-info/licenses/LICENSE +201 -0
- cjm_fasthtml_token_selector-0.0.1.dist-info/top_level.txt +2 -0
- demos/__init__.py +0 -0
- demos/data.py +17 -0
- demos/gap.py +93 -0
- demos/shared.py +148 -0
- demos/span.py +92 -0
- demos/word.py +88 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Tokenization utilities for splitting text into tokens and converting between token indices and character positions."""
|
|
2
|
+
|
|
3
|
+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/helpers/tokenizer.ipynb.
|
|
4
|
+
|
|
5
|
+
# %% auto #0
|
|
6
|
+
__all__ = ['count_tokens', 'get_token_list', 'token_index_to_char_position', 'tokenize']
|
|
7
|
+
|
|
8
|
+
# %% ../../nbs/helpers/tokenizer.ipynb #c161bb62
|
|
9
|
+
from typing import Any, List, Optional, Union
|
|
10
|
+
|
|
11
|
+
from ..core.models import Token
|
|
12
|
+
|
|
13
|
+
# %% ../../nbs/helpers/tokenizer.ipynb #1cded653
|
|
14
|
+
def count_tokens(
|
|
15
|
+
text:str, # text to count tokens in
|
|
16
|
+
) -> int: # token count
|
|
17
|
+
"""Count the number of whitespace-delimited tokens in text."""
|
|
18
|
+
if not text:
|
|
19
|
+
return 0
|
|
20
|
+
return len(text.split())
|
|
21
|
+
|
|
22
|
+
# %% ../../nbs/helpers/tokenizer.ipynb #71f880db
|
|
23
|
+
def get_token_list(
|
|
24
|
+
text:str, # text to split into tokens
|
|
25
|
+
) -> List[str]: # list of token strings
|
|
26
|
+
"""Split text into a list of whitespace-delimited tokens."""
|
|
27
|
+
if not text:
|
|
28
|
+
return []
|
|
29
|
+
return text.split()
|
|
30
|
+
|
|
31
|
+
# %% ../../nbs/helpers/tokenizer.ipynb #b0f6d4c0
|
|
32
|
+
def token_index_to_char_position(
|
|
33
|
+
text:str, # full text string
|
|
34
|
+
token_index:int, # 0-based token index
|
|
35
|
+
) -> int: # character position for split
|
|
36
|
+
"""Convert a token index to the character position where a split should occur."""
|
|
37
|
+
if token_index <= 0:
|
|
38
|
+
return 0
|
|
39
|
+
|
|
40
|
+
words = text.split()
|
|
41
|
+
if token_index >= len(words):
|
|
42
|
+
return len(text)
|
|
43
|
+
|
|
44
|
+
# Walk through the original text to find the character position
|
|
45
|
+
# before the word at token_index
|
|
46
|
+
pos = 0
|
|
47
|
+
for i, word in enumerate(words):
|
|
48
|
+
if i == token_index:
|
|
49
|
+
break
|
|
50
|
+
pos += len(word)
|
|
51
|
+
# Advance past whitespace between words
|
|
52
|
+
while pos < len(text) and text[pos] == ' ':
|
|
53
|
+
pos += 1
|
|
54
|
+
|
|
55
|
+
return pos
|
|
56
|
+
|
|
57
|
+
# %% ../../nbs/helpers/tokenizer.ipynb #c4c9d469
|
|
58
|
+
def tokenize(
|
|
59
|
+
text_or_tokens:Union[str, List[str]], # raw text string or pre-tokenized list
|
|
60
|
+
metadata:Optional[List[Any]] = None, # per-token metadata (must match token count)
|
|
61
|
+
) -> List[Token]: # list of Token objects
|
|
62
|
+
"""Convert text or a pre-tokenized list into Token objects."""
|
|
63
|
+
if isinstance(text_or_tokens, str):
|
|
64
|
+
words = get_token_list(text_or_tokens)
|
|
65
|
+
else:
|
|
66
|
+
words = list(text_or_tokens)
|
|
67
|
+
|
|
68
|
+
if metadata is not None and len(metadata) != len(words):
|
|
69
|
+
raise ValueError(
|
|
70
|
+
f"metadata length ({len(metadata)}) must match token count ({len(words)})"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
return [
|
|
74
|
+
Token(text=w, index=i, metadata=metadata[i] if metadata else None)
|
|
75
|
+
for i, w in enumerate(words)
|
|
76
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""Master IIFE composer for the token selector JS runtime."""
|
|
2
|
+
|
|
3
|
+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/js/core.ipynb.
|
|
4
|
+
|
|
5
|
+
# %% auto #0
|
|
6
|
+
__all__ = ['global_callback_name', 'generate_token_selector_js']
|
|
7
|
+
|
|
8
|
+
# %% ../../nbs/js/core.ipynb #3dd234c7
|
|
9
|
+
from typing import Any, Tuple
|
|
10
|
+
|
|
11
|
+
from fasthtml.common import Script
|
|
12
|
+
|
|
13
|
+
from ..core.config import TokenSelectorConfig
|
|
14
|
+
from ..core.models import TokenSelectorState
|
|
15
|
+
from ..core.html_ids import TokenSelectorHtmlIds
|
|
16
|
+
from .navigation import generate_navigation_js
|
|
17
|
+
from .display import generate_display_js
|
|
18
|
+
from .repeat import generate_key_repeat_js
|
|
19
|
+
|
|
20
|
+
# %% ../../nbs/js/core.ipynb #bee1f126
|
|
21
|
+
_GLOBAL_CALLBACKS = (
|
|
22
|
+
"activate",
|
|
23
|
+
"deactivate",
|
|
24
|
+
"selectGap",
|
|
25
|
+
"selectWord",
|
|
26
|
+
"selectToken",
|
|
27
|
+
"moveToFirst",
|
|
28
|
+
"moveToLast",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
def global_callback_name(
|
|
32
|
+
prefix:str, # token selector instance prefix
|
|
33
|
+
callback:str, # base callback name
|
|
34
|
+
) -> str: # global function name
|
|
35
|
+
"""Generate a prefix-unique global callback name."""
|
|
36
|
+
return f"{prefix}_{callback}"
|
|
37
|
+
|
|
38
|
+
# %% ../../nbs/js/core.ipynb #173e8f33
|
|
39
|
+
def _generate_state_init_js(
|
|
40
|
+
config:TokenSelectorConfig, # config for this instance
|
|
41
|
+
state:TokenSelectorState, # initial state
|
|
42
|
+
) -> str: # JS code fragment
|
|
43
|
+
"""Generate state initialization code."""
|
|
44
|
+
return f"""
|
|
45
|
+
// State
|
|
46
|
+
ns.anchor = {state.anchor};
|
|
47
|
+
ns.focus = {state.focus};
|
|
48
|
+
ns.wordCount = {state.word_count};
|
|
49
|
+
ns.active = false;
|
|
50
|
+
ns.mode = '{config.selection_mode}';
|
|
51
|
+
ns._prevAnchor = -1;
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
# %% ../../nbs/js/core.ipynb #94cc4f28
|
|
55
|
+
def _generate_on_change_js(
|
|
56
|
+
config:TokenSelectorConfig, # config for this instance
|
|
57
|
+
) -> str: # JS code fragment
|
|
58
|
+
"""Generate the on-change callback dispatcher."""
|
|
59
|
+
if config.on_change_callback:
|
|
60
|
+
return f"""
|
|
61
|
+
ns._fireOnChange = function() {{
|
|
62
|
+
if (typeof window['{config.on_change_callback}'] === 'function') {{
|
|
63
|
+
window['{config.on_change_callback}'](ns.anchor, ns.focus, ns.mode);
|
|
64
|
+
}}
|
|
65
|
+
}};
|
|
66
|
+
"""
|
|
67
|
+
return """
|
|
68
|
+
ns._fireOnChange = function() {};
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
# %% ../../nbs/js/core.ipynb #0089ec56
|
|
72
|
+
def _generate_activation_js(
|
|
73
|
+
config:TokenSelectorConfig, # config for this instance
|
|
74
|
+
ids:TokenSelectorHtmlIds, # HTML IDs
|
|
75
|
+
) -> str: # JS code fragment
|
|
76
|
+
"""Generate activate/deactivate functions."""
|
|
77
|
+
return f"""
|
|
78
|
+
ns.activate = function() {{
|
|
79
|
+
ns.active = true;
|
|
80
|
+
// Re-read word count from DOM
|
|
81
|
+
var grid = document.getElementById('{ids.token_grid}');
|
|
82
|
+
if (grid) ns.wordCount = parseInt(grid.dataset.wordCount) || 0;
|
|
83
|
+
// Reset position
|
|
84
|
+
ns.anchor = 0;
|
|
85
|
+
ns.focus = 0;
|
|
86
|
+
// Add key listeners
|
|
87
|
+
document.addEventListener('keydown', ns._handleKeyDown, true);
|
|
88
|
+
document.addEventListener('keyup', ns._handleKeyUp, true);
|
|
89
|
+
ns.updateDisplay();
|
|
90
|
+
ns.updateInputs();
|
|
91
|
+
}};
|
|
92
|
+
|
|
93
|
+
ns.deactivate = function() {{
|
|
94
|
+
ns.active = false;
|
|
95
|
+
// Cancel repeat
|
|
96
|
+
if (ns._repeatTimer) {{
|
|
97
|
+
clearTimeout(ns._repeatTimer);
|
|
98
|
+
ns._repeatTimer = null;
|
|
99
|
+
}}
|
|
100
|
+
ns._repeatKey = null;
|
|
101
|
+
ns._repeatShift = false;
|
|
102
|
+
// Remove key listeners
|
|
103
|
+
document.removeEventListener('keydown', ns._handleKeyDown, true);
|
|
104
|
+
document.removeEventListener('keyup', ns._handleKeyUp, true);
|
|
105
|
+
}};
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
# %% ../../nbs/js/core.ipynb #d62a060d
|
|
109
|
+
def _generate_global_callbacks_js(
|
|
110
|
+
config:TokenSelectorConfig, # config for this instance
|
|
111
|
+
) -> str: # JS code fragment
|
|
112
|
+
"""Generate global callback wrappers."""
|
|
113
|
+
lines = []
|
|
114
|
+
for cb_name in _GLOBAL_CALLBACKS:
|
|
115
|
+
global_name = global_callback_name(config.prefix, cb_name)
|
|
116
|
+
lines.append(
|
|
117
|
+
f" window['{global_name}'] = function() {{ "
|
|
118
|
+
f"if (ns.{cb_name}) ns.{cb_name}.apply(ns, arguments); }};"
|
|
119
|
+
)
|
|
120
|
+
return "\n".join(lines)
|
|
121
|
+
|
|
122
|
+
# %% ../../nbs/js/core.ipynb #111900aa
|
|
123
|
+
def _generate_settle_handler_js(
|
|
124
|
+
config:TokenSelectorConfig, # config for this instance
|
|
125
|
+
ids:TokenSelectorHtmlIds, # HTML IDs
|
|
126
|
+
) -> str: # JS code fragment
|
|
127
|
+
"""Generate htmx:afterSettle handler for swap resilience."""
|
|
128
|
+
guard = f"_tsMasterListener_{config.prefix}"
|
|
129
|
+
return f"""
|
|
130
|
+
// HTMX afterSettle: re-read word count and update display
|
|
131
|
+
if (!window['{guard}']) {{
|
|
132
|
+
window['{guard}'] = true;
|
|
133
|
+
document.body.addEventListener('htmx:afterSettle', function() {{
|
|
134
|
+
var grid = document.getElementById('{ids.token_grid}');
|
|
135
|
+
if (grid && ns.active) {{
|
|
136
|
+
ns.wordCount = parseInt(grid.dataset.wordCount) || 0;
|
|
137
|
+
ns.updateDisplay();
|
|
138
|
+
ns.updateInputs();
|
|
139
|
+
}}
|
|
140
|
+
}});
|
|
141
|
+
}}
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
# %% ../../nbs/js/core.ipynb #48c0e44b
|
|
145
|
+
def generate_token_selector_js(
|
|
146
|
+
config:TokenSelectorConfig, # config for this instance
|
|
147
|
+
ids:TokenSelectorHtmlIds, # HTML IDs
|
|
148
|
+
state:TokenSelectorState = None, # initial state
|
|
149
|
+
extra_scripts:Tuple[str, ...] = (), # additional JS to include in the IIFE
|
|
150
|
+
) -> Any: # Script element with the complete IIFE
|
|
151
|
+
"""Compose all token selector JS into a single namespaced IIFE."""
|
|
152
|
+
if state is None:
|
|
153
|
+
state = TokenSelectorState()
|
|
154
|
+
|
|
155
|
+
# Collect all JS fragments
|
|
156
|
+
fragments = [
|
|
157
|
+
_generate_state_init_js(config, state),
|
|
158
|
+
_generate_on_change_js(config),
|
|
159
|
+
generate_display_js(config, ids),
|
|
160
|
+
generate_navigation_js(config, ids),
|
|
161
|
+
generate_key_repeat_js(config),
|
|
162
|
+
_generate_activation_js(config, ids),
|
|
163
|
+
_generate_global_callbacks_js(config),
|
|
164
|
+
_generate_settle_handler_js(config, ids),
|
|
165
|
+
]
|
|
166
|
+
|
|
167
|
+
# Consumer extra scripts
|
|
168
|
+
if extra_scripts:
|
|
169
|
+
fragments.append("\n".join(extra_scripts))
|
|
170
|
+
|
|
171
|
+
body = "\n".join(fragments)
|
|
172
|
+
prefix = config.prefix
|
|
173
|
+
|
|
174
|
+
iife = f"""(function() {{
|
|
175
|
+
'use strict';
|
|
176
|
+
window.tokenSelectors = window.tokenSelectors || {{}};
|
|
177
|
+
var ns = window.tokenSelectors['{prefix}'] = {{}};
|
|
178
|
+
{body}
|
|
179
|
+
}})();"""
|
|
180
|
+
|
|
181
|
+
return Script(iife)
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""Generates JS functions for updating token display state (caret indicators, highlights, dimming, hidden inputs)."""
|
|
2
|
+
|
|
3
|
+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/js/display.ipynb.
|
|
4
|
+
|
|
5
|
+
# %% auto #0
|
|
6
|
+
__all__ = ['generate_display_js']
|
|
7
|
+
|
|
8
|
+
# %% ../../nbs/js/display.ipynb #4614e222
|
|
9
|
+
from cjm_fasthtml_token_selector.core.constants import (
|
|
10
|
+
CARET_INDICATOR_CLS, OPACITY_50_CLS, HIGHLIGHT_CLS,
|
|
11
|
+
)
|
|
12
|
+
from ..core.config import TokenSelectorConfig
|
|
13
|
+
from ..core.html_ids import TokenSelectorHtmlIds
|
|
14
|
+
|
|
15
|
+
# %% ../../nbs/js/display.ipynb #85e3702e
|
|
16
|
+
def _generate_update_inputs_js(
|
|
17
|
+
ids:TokenSelectorHtmlIds, # HTML IDs
|
|
18
|
+
) -> str: # JS code fragment
|
|
19
|
+
"""Generate the hidden input sync function."""
|
|
20
|
+
return f"""
|
|
21
|
+
ns.updateInputs = function() {{
|
|
22
|
+
var anchorEl = document.getElementById('{ids.anchor_input}');
|
|
23
|
+
var focusEl = document.getElementById('{ids.focus_input}');
|
|
24
|
+
if (anchorEl) anchorEl.value = ns.anchor;
|
|
25
|
+
if (focusEl) focusEl.value = ns.focus;
|
|
26
|
+
}};
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
# %% ../../nbs/js/display.ipynb #9c85ab30
|
|
30
|
+
def _generate_gap_display_js(
|
|
31
|
+
ids:TokenSelectorHtmlIds, # HTML IDs
|
|
32
|
+
) -> str: # JS code fragment
|
|
33
|
+
"""Generate gap mode display update function."""
|
|
34
|
+
return f"""
|
|
35
|
+
ns.updateDisplay = function() {{
|
|
36
|
+
var grid = document.getElementById('{ids.token_grid}');
|
|
37
|
+
if (!grid) return;
|
|
38
|
+
|
|
39
|
+
// Remove existing caret indicators
|
|
40
|
+
grid.querySelectorAll('.caret-indicator').forEach(function(el) {{ el.remove(); }});
|
|
41
|
+
|
|
42
|
+
var tokens = grid.querySelectorAll('.token');
|
|
43
|
+
var prevAnchor = ns._prevAnchor !== undefined ? ns._prevAnchor : -1;
|
|
44
|
+
|
|
45
|
+
tokens.forEach(function(token) {{
|
|
46
|
+
var idx = parseInt(token.dataset.tokenIndex);
|
|
47
|
+
|
|
48
|
+
// Dimming: tokens before caret
|
|
49
|
+
if (idx < ns.anchor) {{
|
|
50
|
+
if (!token.classList.contains('{OPACITY_50_CLS}')) {{
|
|
51
|
+
token.classList.add('{OPACITY_50_CLS}');
|
|
52
|
+
}}
|
|
53
|
+
}} else {{
|
|
54
|
+
token.classList.remove('{OPACITY_50_CLS}');
|
|
55
|
+
}}
|
|
56
|
+
|
|
57
|
+
// Caret indicator at anchor position
|
|
58
|
+
if (idx === ns.anchor) {{
|
|
59
|
+
var caret = document.createElement('div');
|
|
60
|
+
caret.className = '{CARET_INDICATOR_CLS}';
|
|
61
|
+
token.insertBefore(caret, token.firstChild);
|
|
62
|
+
}}
|
|
63
|
+
}});
|
|
64
|
+
|
|
65
|
+
ns._prevAnchor = ns.anchor;
|
|
66
|
+
}};
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
# %% ../../nbs/js/display.ipynb #90b62704
|
|
70
|
+
def _generate_word_display_js(
|
|
71
|
+
ids:TokenSelectorHtmlIds, # HTML IDs
|
|
72
|
+
) -> str: # JS code fragment
|
|
73
|
+
"""Generate word mode display update function."""
|
|
74
|
+
return f"""
|
|
75
|
+
ns.updateDisplay = function() {{
|
|
76
|
+
var grid = document.getElementById('{ids.token_grid}');
|
|
77
|
+
if (!grid) return;
|
|
78
|
+
|
|
79
|
+
var tokens = grid.querySelectorAll('.token');
|
|
80
|
+
|
|
81
|
+
tokens.forEach(function(token) {{
|
|
82
|
+
var idx = parseInt(token.dataset.tokenIndex);
|
|
83
|
+
|
|
84
|
+
if (idx === ns.anchor) {{
|
|
85
|
+
if (!token.classList.contains('{HIGHLIGHT_CLS}')) {{
|
|
86
|
+
token.classList.add('{HIGHLIGHT_CLS}');
|
|
87
|
+
}}
|
|
88
|
+
}} else {{
|
|
89
|
+
token.classList.remove('{HIGHLIGHT_CLS}');
|
|
90
|
+
}}
|
|
91
|
+
}});
|
|
92
|
+
}};
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
# %% ../../nbs/js/display.ipynb #f3f54861
|
|
96
|
+
def _generate_span_display_js(
|
|
97
|
+
ids:TokenSelectorHtmlIds, # HTML IDs
|
|
98
|
+
) -> str: # JS code fragment
|
|
99
|
+
"""Generate span mode display update function."""
|
|
100
|
+
return f"""
|
|
101
|
+
ns.updateDisplay = function() {{
|
|
102
|
+
var grid = document.getElementById('{ids.token_grid}');
|
|
103
|
+
if (!grid) return;
|
|
104
|
+
|
|
105
|
+
// Remove existing caret indicators
|
|
106
|
+
grid.querySelectorAll('.caret-indicator').forEach(function(el) {{ el.remove(); }});
|
|
107
|
+
|
|
108
|
+
var tokens = grid.querySelectorAll('.token');
|
|
109
|
+
var lo = Math.min(ns.anchor, ns.focus);
|
|
110
|
+
var hi = Math.max(ns.anchor, ns.focus);
|
|
111
|
+
|
|
112
|
+
tokens.forEach(function(token) {{
|
|
113
|
+
var idx = parseInt(token.dataset.tokenIndex);
|
|
114
|
+
|
|
115
|
+
// Highlight tokens in range
|
|
116
|
+
if (idx >= lo && idx <= hi) {{
|
|
117
|
+
if (!token.classList.contains('{HIGHLIGHT_CLS}')) {{
|
|
118
|
+
token.classList.add('{HIGHLIGHT_CLS}');
|
|
119
|
+
}}
|
|
120
|
+
}} else {{
|
|
121
|
+
token.classList.remove('{HIGHLIGHT_CLS}');
|
|
122
|
+
}}
|
|
123
|
+
|
|
124
|
+
// Caret at focus position
|
|
125
|
+
if (idx === ns.focus) {{
|
|
126
|
+
var caret = document.createElement('div');
|
|
127
|
+
caret.className = '{CARET_INDICATOR_CLS}';
|
|
128
|
+
token.insertBefore(caret, token.firstChild);
|
|
129
|
+
}}
|
|
130
|
+
}});
|
|
131
|
+
}};
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
# %% ../../nbs/js/display.ipynb #c782dde5
|
|
135
|
+
def generate_display_js(
|
|
136
|
+
config:TokenSelectorConfig, # config for this instance
|
|
137
|
+
ids:TokenSelectorHtmlIds, # HTML IDs
|
|
138
|
+
) -> str: # JS code fragment for the IIFE
|
|
139
|
+
"""Generate display update and hidden input sync JS functions."""
|
|
140
|
+
display_generators = {
|
|
141
|
+
"gap": _generate_gap_display_js,
|
|
142
|
+
"word": _generate_word_display_js,
|
|
143
|
+
"span": _generate_span_display_js,
|
|
144
|
+
}
|
|
145
|
+
gen = display_generators.get(config.selection_mode, _generate_gap_display_js)
|
|
146
|
+
return _generate_update_inputs_js(ids) + gen(ids)
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""Generates mode-specific navigation and selection JS functions."""
|
|
2
|
+
|
|
3
|
+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/js/navigation.ipynb.
|
|
4
|
+
|
|
5
|
+
# %% auto #0
|
|
6
|
+
__all__ = ['generate_navigation_js']
|
|
7
|
+
|
|
8
|
+
# %% ../../nbs/js/navigation.ipynb #386f20ca
|
|
9
|
+
from ..core.config import TokenSelectorConfig
|
|
10
|
+
from ..core.html_ids import TokenSelectorHtmlIds
|
|
11
|
+
|
|
12
|
+
# %% ../../nbs/js/navigation.ipynb #7146756c
|
|
13
|
+
def _generate_gap_nav_js(
|
|
14
|
+
config:TokenSelectorConfig, # config for this instance
|
|
15
|
+
ids:TokenSelectorHtmlIds, # HTML IDs
|
|
16
|
+
) -> str: # JS code fragment
|
|
17
|
+
"""Generate gap mode navigation functions."""
|
|
18
|
+
wrap = "true" if config.wrap_navigation else "false"
|
|
19
|
+
return f"""
|
|
20
|
+
ns.moveLeft = function() {{
|
|
21
|
+
if (ns.anchor > 0) {{
|
|
22
|
+
ns.anchor--;
|
|
23
|
+
ns.focus = ns.anchor;
|
|
24
|
+
}} else if ({wrap}) {{
|
|
25
|
+
ns.anchor = ns.wordCount;
|
|
26
|
+
ns.focus = ns.anchor;
|
|
27
|
+
}}
|
|
28
|
+
ns.updateDisplay();
|
|
29
|
+
ns.updateInputs();
|
|
30
|
+
ns._fireOnChange();
|
|
31
|
+
}};
|
|
32
|
+
|
|
33
|
+
ns.moveRight = function() {{
|
|
34
|
+
if (ns.anchor < ns.wordCount) {{
|
|
35
|
+
ns.anchor++;
|
|
36
|
+
ns.focus = ns.anchor;
|
|
37
|
+
}} else if ({wrap}) {{
|
|
38
|
+
ns.anchor = 0;
|
|
39
|
+
ns.focus = ns.anchor;
|
|
40
|
+
}}
|
|
41
|
+
ns.updateDisplay();
|
|
42
|
+
ns.updateInputs();
|
|
43
|
+
ns._fireOnChange();
|
|
44
|
+
}};
|
|
45
|
+
|
|
46
|
+
ns.selectGap = function(position) {{
|
|
47
|
+
ns.anchor = Math.max(0, Math.min(position, ns.wordCount));
|
|
48
|
+
ns.focus = ns.anchor;
|
|
49
|
+
ns.updateDisplay();
|
|
50
|
+
ns.updateInputs();
|
|
51
|
+
ns._fireOnChange();
|
|
52
|
+
}};
|
|
53
|
+
|
|
54
|
+
ns.moveToFirst = function() {{
|
|
55
|
+
ns.anchor = 0;
|
|
56
|
+
ns.focus = ns.anchor;
|
|
57
|
+
ns.updateDisplay();
|
|
58
|
+
ns.updateInputs();
|
|
59
|
+
ns._fireOnChange();
|
|
60
|
+
}};
|
|
61
|
+
|
|
62
|
+
ns.moveToLast = function() {{
|
|
63
|
+
ns.anchor = ns.wordCount;
|
|
64
|
+
ns.focus = ns.anchor;
|
|
65
|
+
ns.updateDisplay();
|
|
66
|
+
ns.updateInputs();
|
|
67
|
+
ns._fireOnChange();
|
|
68
|
+
}};
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
# %% ../../nbs/js/navigation.ipynb #4ef3a24d
|
|
72
|
+
def _generate_word_nav_js(
|
|
73
|
+
config:TokenSelectorConfig, # config for this instance
|
|
74
|
+
ids:TokenSelectorHtmlIds, # HTML IDs
|
|
75
|
+
) -> str: # JS code fragment
|
|
76
|
+
"""Generate word mode navigation functions."""
|
|
77
|
+
wrap = "true" if config.wrap_navigation else "false"
|
|
78
|
+
return f"""
|
|
79
|
+
ns.moveLeft = function() {{
|
|
80
|
+
if (ns.anchor > 0) {{
|
|
81
|
+
ns.anchor--;
|
|
82
|
+
ns.focus = ns.anchor;
|
|
83
|
+
}} else if ({wrap} && ns.wordCount > 0) {{
|
|
84
|
+
ns.anchor = ns.wordCount - 1;
|
|
85
|
+
ns.focus = ns.anchor;
|
|
86
|
+
}}
|
|
87
|
+
ns.updateDisplay();
|
|
88
|
+
ns.updateInputs();
|
|
89
|
+
ns._fireOnChange();
|
|
90
|
+
}};
|
|
91
|
+
|
|
92
|
+
ns.moveRight = function() {{
|
|
93
|
+
if (ns.anchor < ns.wordCount - 1) {{
|
|
94
|
+
ns.anchor++;
|
|
95
|
+
ns.focus = ns.anchor;
|
|
96
|
+
}} else if ({wrap}) {{
|
|
97
|
+
ns.anchor = 0;
|
|
98
|
+
ns.focus = ns.anchor;
|
|
99
|
+
}}
|
|
100
|
+
ns.updateDisplay();
|
|
101
|
+
ns.updateInputs();
|
|
102
|
+
ns._fireOnChange();
|
|
103
|
+
}};
|
|
104
|
+
|
|
105
|
+
ns.selectWord = function(index) {{
|
|
106
|
+
ns.anchor = Math.max(0, Math.min(index, ns.wordCount - 1));
|
|
107
|
+
ns.focus = ns.anchor;
|
|
108
|
+
ns.updateDisplay();
|
|
109
|
+
ns.updateInputs();
|
|
110
|
+
ns._fireOnChange();
|
|
111
|
+
}};
|
|
112
|
+
|
|
113
|
+
ns.moveToFirst = function() {{
|
|
114
|
+
ns.anchor = 0;
|
|
115
|
+
ns.focus = ns.anchor;
|
|
116
|
+
ns.updateDisplay();
|
|
117
|
+
ns.updateInputs();
|
|
118
|
+
ns._fireOnChange();
|
|
119
|
+
}};
|
|
120
|
+
|
|
121
|
+
ns.moveToLast = function() {{
|
|
122
|
+
ns.anchor = Math.max(0, ns.wordCount - 1);
|
|
123
|
+
ns.focus = ns.anchor;
|
|
124
|
+
ns.updateDisplay();
|
|
125
|
+
ns.updateInputs();
|
|
126
|
+
ns._fireOnChange();
|
|
127
|
+
}};
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
# %% ../../nbs/js/navigation.ipynb #7a3c234e
|
|
131
|
+
def _generate_span_nav_js(
|
|
132
|
+
config:TokenSelectorConfig, # config for this instance
|
|
133
|
+
ids:TokenSelectorHtmlIds, # HTML IDs
|
|
134
|
+
) -> str: # JS code fragment
|
|
135
|
+
"""Generate span mode navigation functions."""
|
|
136
|
+
wrap = "true" if config.wrap_navigation else "false"
|
|
137
|
+
return f"""
|
|
138
|
+
ns.moveLeft = function() {{
|
|
139
|
+
if (ns.focus > 0) {{
|
|
140
|
+
ns.focus--;
|
|
141
|
+
ns.anchor = ns.focus;
|
|
142
|
+
}} else if ({wrap} && ns.wordCount > 0) {{
|
|
143
|
+
ns.focus = ns.wordCount - 1;
|
|
144
|
+
ns.anchor = ns.focus;
|
|
145
|
+
}}
|
|
146
|
+
ns.updateDisplay();
|
|
147
|
+
ns.updateInputs();
|
|
148
|
+
ns._fireOnChange();
|
|
149
|
+
}};
|
|
150
|
+
|
|
151
|
+
ns.moveRight = function() {{
|
|
152
|
+
if (ns.focus < ns.wordCount - 1) {{
|
|
153
|
+
ns.focus++;
|
|
154
|
+
ns.anchor = ns.focus;
|
|
155
|
+
}} else if ({wrap}) {{
|
|
156
|
+
ns.focus = 0;
|
|
157
|
+
ns.anchor = ns.focus;
|
|
158
|
+
}}
|
|
159
|
+
ns.updateDisplay();
|
|
160
|
+
ns.updateInputs();
|
|
161
|
+
ns._fireOnChange();
|
|
162
|
+
}};
|
|
163
|
+
|
|
164
|
+
ns.extendLeft = function() {{
|
|
165
|
+
if (ns.focus > 0) {{
|
|
166
|
+
ns.focus--;
|
|
167
|
+
}}
|
|
168
|
+
ns.updateDisplay();
|
|
169
|
+
ns.updateInputs();
|
|
170
|
+
ns._fireOnChange();
|
|
171
|
+
}};
|
|
172
|
+
|
|
173
|
+
ns.extendRight = function() {{
|
|
174
|
+
if (ns.focus < ns.wordCount - 1) {{
|
|
175
|
+
ns.focus++;
|
|
176
|
+
}}
|
|
177
|
+
ns.updateDisplay();
|
|
178
|
+
ns.updateInputs();
|
|
179
|
+
ns._fireOnChange();
|
|
180
|
+
}};
|
|
181
|
+
|
|
182
|
+
ns.selectToken = function(index) {{
|
|
183
|
+
ns.focus = Math.max(0, Math.min(index, ns.wordCount - 1));
|
|
184
|
+
ns.updateDisplay();
|
|
185
|
+
ns.updateInputs();
|
|
186
|
+
ns._fireOnChange();
|
|
187
|
+
}};
|
|
188
|
+
|
|
189
|
+
ns.selectGap = function(position) {{
|
|
190
|
+
var idx = Math.max(0, Math.min(position, ns.wordCount - 1));
|
|
191
|
+
ns.anchor = idx;
|
|
192
|
+
ns.focus = idx;
|
|
193
|
+
ns.updateDisplay();
|
|
194
|
+
ns.updateInputs();
|
|
195
|
+
ns._fireOnChange();
|
|
196
|
+
}};
|
|
197
|
+
|
|
198
|
+
ns.moveToFirst = function() {{
|
|
199
|
+
ns.anchor = 0;
|
|
200
|
+
ns.focus = 0;
|
|
201
|
+
ns.updateDisplay();
|
|
202
|
+
ns.updateInputs();
|
|
203
|
+
ns._fireOnChange();
|
|
204
|
+
}};
|
|
205
|
+
|
|
206
|
+
ns.moveToLast = function() {{
|
|
207
|
+
var last = Math.max(0, ns.wordCount - 1);
|
|
208
|
+
ns.anchor = last;
|
|
209
|
+
ns.focus = last;
|
|
210
|
+
ns.updateDisplay();
|
|
211
|
+
ns.updateInputs();
|
|
212
|
+
ns._fireOnChange();
|
|
213
|
+
}};
|
|
214
|
+
"""
|
|
215
|
+
|
|
216
|
+
# %% ../../nbs/js/navigation.ipynb #eb0cbbbb
|
|
217
|
+
def generate_navigation_js(
|
|
218
|
+
config:TokenSelectorConfig, # config for this instance
|
|
219
|
+
ids:TokenSelectorHtmlIds, # HTML IDs
|
|
220
|
+
) -> str: # JS code fragment for the IIFE
|
|
221
|
+
"""Generate mode-specific navigation and selection JS functions."""
|
|
222
|
+
generators = {
|
|
223
|
+
"gap": _generate_gap_nav_js,
|
|
224
|
+
"word": _generate_word_nav_js,
|
|
225
|
+
"span": _generate_span_nav_js,
|
|
226
|
+
}
|
|
227
|
+
gen = generators.get(config.selection_mode, _generate_gap_nav_js)
|
|
228
|
+
return gen(config, ids)
|