augment.dart 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. import 'package:flutter/material.dart';
  2. import 'dart:html' as html;
  3. import 'dart:js' as js;
  4. class EmailToolbar extends StatefulWidget {
  5. final Function(String) onJumpToSpan;
  6. final VoidCallback onButtonPressed;
  7. EmailToolbar(
  8. {Key? key, required this.onButtonPressed, required this.onJumpToSpan})
  9. : super(key: key);
  10. @override
  11. _DynamicClassesAugment createState() => _DynamicClassesAugment();
  12. }
  13. class _DynamicClassesAugment extends State<EmailToolbar> {
  14. String selectedClass = 'Class 1';
  15. // TextEditingController _jumpController = TextEditingController();
  16. // late final FocusNode _JumpItemfocusNode;
  17. // late final FocusNode _viewSpecsfocusNode;
  18. // bool _jumpItemHasFocus = false;
  19. // bool _viewSpecsHasFocus = false;
  20. @override
  21. void initState() {
  22. super.initState();
  23. // _JumpItemfocusNode = FocusNode();
  24. // _viewSpecsfocusNode = FocusNode();
  25. // _JumpItemfocusNode.addListener(() {
  26. // setState(() => _jumpItemHasFocus = _JumpItemfocusNode.hasFocus);
  27. // });
  28. // _viewSpecsfocusNode.addListener(() {
  29. // setState(() => _viewSpecsHasFocus = _viewSpecsfocusNode.hasFocus);
  30. // });
  31. }
  32. @override
  33. void dispose() {
  34. // _JumpItemfocusNode.dispose();
  35. // _viewSpecsfocusNode.dispose();
  36. // _jumpController.dispose();
  37. super.dispose();
  38. }
  39. @override
  40. Widget build(BuildContext context) {
  41. // const animationDuration = Duration(milliseconds: 250);
  42. return Column(children: [
  43. Row(
  44. children: [
  45. ElevatedButton(
  46. onPressed: () => AugmentClasses.handleHome(context),
  47. child: Text('Home'),
  48. ),
  49. SizedBox(width: 8),
  50. ElevatedButton(
  51. onPressed: AugmentClasses.handleReload,
  52. child: Text('Reload'),
  53. ),
  54. ElevatedButton(
  55. onPressed: AugmentClasses.handleImages,
  56. child: Text('Images'),
  57. ),
  58. SizedBox(width: 8),
  59. ElevatedButton(
  60. onPressed: AugmentClasses.handleOpen,
  61. child: Text('Open'),
  62. ),
  63. // SizedBox(width: 8),
  64. ElevatedButton(
  65. onPressed: AugmentClasses.handleFind,
  66. child: Text('Find'),
  67. ),
  68. // SizedBox(width: 8),
  69. ElevatedButton(
  70. onPressed: AugmentClasses.handleStop,
  71. child: Text('Stop'),
  72. ),
  73. Spacer(),
  74. PopupMenuButton<String>(
  75. onSelected: (String value) {
  76. setState(() {
  77. selectedClass = value;
  78. print(selectedClass);
  79. });
  80. },
  81. itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
  82. const PopupMenuItem<String>(
  83. value: 'Class 1',
  84. child: Text('Class 1'),
  85. ),
  86. const PopupMenuItem<String>(
  87. value: 'Class 2',
  88. child: Text('Class 2'),
  89. ),
  90. const PopupMenuItem<String>(
  91. value: 'Turbo 3',
  92. child: Text('Turbo 3'),
  93. ),
  94. ],
  95. // child: ElevatedButton(
  96. // onPressed: () {},
  97. child: Text('Options'),
  98. ),
  99. ],
  100. ),
  101. if (selectedClass == 'Class 2')
  102. Stack(children: [
  103. Row(
  104. children: [
  105. ElevatedButton(
  106. onPressed: () => AugmentClasses.JumpButton(context),
  107. child: Text('JumpItem:'),
  108. ),
  109. // SizedBox(
  110. // width: 8,
  111. // ),
  112. Container(
  113. width: 50,
  114. height: 30,
  115. child: TextField(
  116. // controller: _jumpController,
  117. decoration: InputDecoration(
  118. border: OutlineInputBorder(),
  119. // suffixIcon: Icon(Icons.search)
  120. ),
  121. onSubmitted: (value) {
  122. print("onSubmitted");
  123. if (value.isNotEmpty) {
  124. widget.onJumpToSpan(value);
  125. }
  126. },
  127. ),
  128. ),
  129. //TODO: Make an animation to make the button a textfield
  130. // AnimatedSwitcher(
  131. // duration: animationDuration,
  132. // transitionBuilder: (Widget child, Animation<double> animation) {
  133. // return FadeTransition(opacity: animation, child: child);
  134. // },
  135. // child: _jumpItemHasFocus
  136. // ? Container(
  137. // key: ValueKey('TextField1'),
  138. // width: 150,
  139. // child: TextField(
  140. // focusNode: _JumpItemfocusNode,
  141. // decoration: InputDecoration(
  142. // hintText: 'Enter Text',
  143. // border: OutlineInputBorder(),
  144. // ),
  145. // ),
  146. // )
  147. // : Container(
  148. // key: ValueKey('Button1'),
  149. // child: ElevatedButton(
  150. // onPressed: () => _JumpItemfocusNode.requestFocus(),
  151. // child: Text('Jump Item:'),
  152. // ),
  153. // ),
  154. // ),
  155. SizedBox(width: 8),
  156. ElevatedButton(
  157. onPressed: () => AugmentClasses.ViewSpecsButton(context),
  158. child: Text('ViewSpecs:')),
  159. Container(
  160. width: 50,
  161. height: 30,
  162. child: TextField(
  163. decoration: InputDecoration(
  164. labelText: '',
  165. border: OutlineInputBorder(),
  166. // suffixIcon: Icon(Icons.style_rounded)
  167. ),
  168. ),
  169. ),
  170. ElevatedButton(
  171. onPressed: () => AugmentClasses.FilterButton(context),
  172. child: Text('Filter'),
  173. ),
  174. SizedBox(width: 8),
  175. ElevatedButton(
  176. onPressed: AugmentClasses.handleOpen,
  177. child: Text('Lookup'),
  178. ),
  179. // SizedBox(width: 8),
  180. ElevatedButton(
  181. onPressed: AugmentClasses.handleFind,
  182. child: Text('Create Link'),
  183. ),
  184. ElevatedButton(
  185. onPressed: AugmentClasses.handleFind,
  186. child: Text('Paste Link'),
  187. ),
  188. ],
  189. )
  190. ])
  191. ]);
  192. }
  193. }
  194. class AugmentClasses {
  195. static void handleHome(BuildContext context) {
  196. Navigator.of(context).popUntil((route) => route.isFirst);
  197. }
  198. static void handleReload() {
  199. print("reload");
  200. }
  201. static void handleImages() {
  202. print("Images button pressed");
  203. }
  204. static void handleOpen() {
  205. print("Open button pressed");
  206. }
  207. static void handleFind() {
  208. print("Find button pressed");
  209. }
  210. static void handleStop() {
  211. print("Stop button pressed");
  212. }
  213. static void handleJump(String spanId) {
  214. String js_code = '''
  215. var iframe = document.getElementsByTagName('iframe')[0]; // 0 for the first iframe, 1 for the second, etc.
  216. // Check if the iframe is loaded and has content
  217. if (iframe && iframe.contentDocument) {
  218. // Access the document inside the iframe
  219. var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
  220. // Find the element with the specific id inside the iframe
  221. var targetElement = iframeDoc.getElementById("$spanId"); // Replace '36 ' with the actual id of the target element
  222. // If the element exists, scroll to it
  223. if (targetElement) {
  224. targetElement.scrollIntoView();
  225. console.log('Scrolled to element with id "$spanId" inside the iframe.');
  226. } else {
  227. console.log('Element with id "$spanId" not found inside the iframe.');
  228. }
  229. } else {
  230. console.log('Iframe not found or not loaded.');
  231. }
  232. ''';
  233. js.context.callMethod('eval', [js_code]);
  234. }
  235. static void invisibility(String htmlClass) {}
  236. static Future<void> JumpButton(BuildContext context) async {
  237. // FocusNode textFieldFocusNode = FocusNode();
  238. AugmentClasses.disableIframePointerEvents();
  239. await showDialog(
  240. barrierDismissible: true,
  241. // barrierColor: Colors.yellow,
  242. context: context,
  243. builder: (context) => AlertDialog(
  244. title: Text('Jump Item:'),
  245. content: Container(
  246. width: 300,
  247. height: 170,
  248. child: Column(
  249. mainAxisSize: MainAxisSize.min,
  250. children: [
  251. Text('Jump (to) Item (at)'),
  252. SizedBox(height: 10),
  253. TextField(
  254. autofocus: true,
  255. decoration: InputDecoration(
  256. labelText: '...',
  257. border: OutlineInputBorder(),
  258. suffixIcon: Icon(Icons.search)),
  259. onSubmitted: (value) {
  260. print("onSubmitted: $value");
  261. if (value.isNotEmpty) {
  262. handleJump(value);
  263. Navigator.of(context).pop();
  264. }
  265. },
  266. ),
  267. Spacer(
  268. flex: 5,
  269. ),
  270. Row(
  271. mainAxisSize: MainAxisSize.min,
  272. children: [
  273. ElevatedButton(
  274. onPressed: () => AugmentClasses.ViewSpecsButton(context),
  275. child: Text("Viewspecs:"),
  276. ),
  277. SizedBox(
  278. width: 150,
  279. child: TextField(
  280. maxLines: 1,
  281. decoration: InputDecoration(
  282. labelText: '',
  283. border: OutlineInputBorder(),
  284. suffixIcon: Icon(Icons.search)),
  285. onSubmitted: (value) {
  286. print("onSubmitted: $value");
  287. if (value.isNotEmpty) {
  288. handleJump(value);
  289. Navigator.of(context).pop();
  290. }
  291. },
  292. ),
  293. ),
  294. ],
  295. ),
  296. ],
  297. ),
  298. ),
  299. actions: [
  300. ElevatedButton(
  301. onPressed: () {
  302. //TODO: Grab both textfields and call both of the functions handles
  303. },
  304. child: Text('OK')),
  305. TextButton(
  306. onPressed: () {
  307. Navigator.of(context).pop();
  308. // print('close pressed');
  309. },
  310. child: Text('Cancel'),
  311. ),
  312. ElevatedButton(
  313. onPressed: () {
  314. //TODO: in the ui demo didn't see it
  315. },
  316. child: Text('Help'))
  317. ],
  318. ),
  319. ).then((_) {
  320. AugmentClasses.enableIframePointerEvents();
  321. });
  322. }
  323. static Future<void> ViewSpecsButton(context) async {
  324. //TODO: finish it
  325. bool blankLines = false;
  326. bool numbering = false;
  327. bool statementSignatures = false;
  328. AugmentClasses.disableIframePointerEvents();
  329. await showDialog(
  330. context: context,
  331. builder: (context) => Container(
  332. height: 150,
  333. width: 300,
  334. child: AlertDialog(
  335. title: Text('Viewspecs(short)'),
  336. content: Container(
  337. width: 400, // Set the width to simulate the Windows style
  338. child: Column(
  339. mainAxisSize: MainAxisSize.min,
  340. crossAxisAlignment: CrossAxisAlignment.start,
  341. children: [
  342. Row(
  343. children: [
  344. // First section: Checkboxes for "Show"
  345. Expanded(
  346. child: Column(
  347. crossAxisAlignment: CrossAxisAlignment.start,
  348. children: [
  349. Text('Show'),
  350. Row(
  351. children: [
  352. Text("y z"),
  353. Checkbox(
  354. value: blankLines,
  355. onChanged: (bool? value) {
  356. blankLines = value!;
  357. },
  358. ),
  359. Text('Blank lines'),
  360. ],
  361. ),
  362. Row(
  363. children: [
  364. Text('m n'),
  365. Checkbox(
  366. value: numbering,
  367. onChanged: (bool? value) {
  368. numbering = value!;
  369. },
  370. ),
  371. Text('Numbering'),
  372. ],
  373. ),
  374. Row(
  375. children: [
  376. Text('K L'),
  377. Checkbox(
  378. value: statementSignatures,
  379. onChanged: (bool? value) {
  380. statementSignatures = value!;
  381. },
  382. ),
  383. Text('Statement signatures'),
  384. ],
  385. ),
  386. ],
  387. ),
  388. ),
  389. // Second section: Numeric input for Outline, Levels, and Lines
  390. Expanded(
  391. child: Column(
  392. crossAxisAlignment: CrossAxisAlignment.start,
  393. children: [
  394. Text('Outline'),
  395. Row(
  396. children: [
  397. Text('Levels'),
  398. SizedBox(width: 10),
  399. Container(
  400. width: 40,
  401. child: TextField(
  402. decoration: InputDecoration(
  403. isDense: true,
  404. border: OutlineInputBorder(),
  405. ),
  406. keyboardType: TextInputType.number,
  407. ),
  408. ),
  409. ],
  410. ),
  411. Row(
  412. children: [
  413. Text('Lines'),
  414. SizedBox(width: 10),
  415. Container(
  416. width: 40,
  417. child: TextField(
  418. decoration: InputDecoration(
  419. isDense: true,
  420. border: OutlineInputBorder(),
  421. ),
  422. keyboardType: TextInputType.number,
  423. ),
  424. ),
  425. ],
  426. ),
  427. ],
  428. ),
  429. ),
  430. ],
  431. ),
  432. SizedBox(height: 20),
  433. Row(
  434. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  435. children: [
  436. ElevatedButton(onPressed: () {}, child: Text('OK')),
  437. ElevatedButton(
  438. onPressed: () {
  439. AugmentClasses.disableIframePointerEvents();
  440. Navigator.of(context).pop();
  441. },
  442. child: Text('Cancel')),
  443. ElevatedButton(
  444. onPressed: () {}, child: Text('Reset')),
  445. ElevatedButton(onPressed: () {}, child: Text('Help')),
  446. ],
  447. ),
  448. ],
  449. ),
  450. ),
  451. ),
  452. )).then((_) {
  453. AugmentClasses.enableIframePointerEvents(); // may be useless?
  454. });
  455. }
  456. void handleFilter() {}
  457. static Future<void> FilterButton(context) async {
  458. //this is literally ctrl+F :skull:
  459. //idea is to search in file, extract the <p> tags that contain these
  460. //words and highlight, then when zoom, you just jump to that paragraph
  461. AugmentClasses.disableIframePointerEvents();
  462. await showDialog(
  463. context: context,
  464. builder: (context) => Container(
  465. height: 150,
  466. width: 300,
  467. child: AlertDialog(
  468. title: Text('Filter'),
  469. content: Container(
  470. width: 400, // Set the width to simulate the Windows style
  471. child: Column(
  472. mainAxisSize: MainAxisSize.min,
  473. crossAxisAlignment: CrossAxisAlignment.start,
  474. children: [
  475. Text('Set filter:'),
  476. SizedBox(
  477. width: 175,
  478. child: TextField(
  479. maxLines: 1,
  480. decoration: InputDecoration(
  481. border: OutlineInputBorder(),
  482. ),
  483. ),
  484. )
  485. ],
  486. )))));
  487. }
  488. static void disableIframePointerEvents() {
  489. final iframes = html.document.getElementsByTagName('iframe');
  490. for (var iframe in iframes) {
  491. if (iframe is html.Element) {
  492. iframe.style.pointerEvents = 'none'; // Disable pointer events
  493. }
  494. }
  495. }
  496. static void enableIframePointerEvents() {
  497. final iframes = html.document.getElementsByTagName('iframe');
  498. for (var iframe in iframes) {
  499. if (iframe is html.Element) {
  500. iframe.style.pointerEvents = 'auto'; // Re-enable pointer events
  501. }
  502. }
  503. }
  504. }