├── .babelrc
├── .circleci
└── config.yml
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .idea
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── dash-color-picker.iml
├── inspectionProfiles
│ └── Project_Default.xml
├── misc.xml
├── modules.xml
└── vcs.xml
├── .npmignore
├── .prettierrc
├── .pylintrc
├── CHANGELOG.md
├── MANIFEST.in
├── README.md
├── dash_dual_listbox-0.0.1.tar.gz
├── dash_dual_listbox.egg-info
├── PKG-INFO
├── SOURCES.txt
├── dependency_links.txt
└── top_level.txt
├── dash_dual_listbox
├── DualList.py
├── __init__.py
├── _imports_.py
├── bundle.js
├── metadata.json
├── package.json
└── style.css
├── extract-meta
├── index.html
├── package-lock.json
├── package.json
├── setup.py
├── src
├── demo
│ ├── App.js
│ ├── index.js
│ └── style.css
└── lib
│ ├── components
│ └── DualList.react.js
│ └── index.js
├── tests
├── IntegrationTests.py
├── __init__.py
├── requirements.txt
└── test_render.py
├── usage.py
├── webpack.config.js
└── webpack.serve.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["env", "react"]
3 | }
4 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | jobs:
4 | "node":
5 | docker:
6 | - image: circleci/node:8.11.3
7 |
8 | steps:
9 | - checkout
10 |
11 | - restore_cache:
12 | key: deps1-{{ .Branch }}-{{ checksum "package.json" }}
13 |
14 | - run:
15 | name: Install package.json
16 | command: npm i
17 |
18 | - save_cache:
19 | key: deps1-{{ .Branch }}-{{ checksum "package.json" }}
20 | paths:
21 | - node_modules
22 |
23 | - run:
24 | name: Run eslint
25 | command: ./node_modules/.bin/eslint src
26 | when: always
27 |
28 |
29 | "python-3.6":
30 | docker:
31 | - image: circleci/python:3.6-stretch-browsers
32 |
33 | environment:
34 | PERCY_ENABLED: False
35 |
36 | steps:
37 | - checkout
38 |
39 | - restore_cache:
40 | key: deps1-{{ .Branch }}-{{ checksum "tests/requirements.txt" }}
41 |
42 | - run:
43 | name: Create virtualenv
44 | command: |
45 | python3 -m venv venv
46 |
47 | - run:
48 | name: Install requirements
49 | command: |
50 | . venv/bin/activate
51 | pip install -r tests/requirements.txt --quiet
52 |
53 | - save_cache:
54 | key: deps1-{{ .Branch }}-{{ checksum "tests/requirements.txt" }}
55 | paths:
56 | - "venv"
57 |
58 | - run:
59 | name: Run pylint
60 | command: |
61 | . venv/bin/activate
62 | pylint usage.py tests
63 | when: always
64 |
65 | - run:
66 | name: Run flake8
67 | command: |
68 | . venv/bin/activate
69 | flake8 usage.py tests
70 | when: always
71 |
72 | - run:
73 | name: Integration Tests
74 | command: |
75 | . venv/bin/activate
76 | python -m unittest tests.test_render
77 | when: always
78 |
79 |
80 | workflows:
81 | version: 2
82 | build:
83 | jobs:
84 | - "python-3.6"
85 | - "node"
86 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | *.css
2 | registerServiceWorker.js
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["eslint:recommended", "prettier"],
3 | "parser": "babel-eslint",
4 | "parserOptions": {
5 | "ecmaVersion": 6,
6 | "sourceType": "module",
7 | "ecmaFeatures": {
8 | "arrowFunctions": true,
9 | "blockBindings": true,
10 | "classes": true,
11 | "defaultParams": true,
12 | "destructuring": true,
13 | "forOf": true,
14 | "generators": true,
15 | "modules": true,
16 | "templateStrings": true,
17 | "jsx": true
18 | }
19 | },
20 | "env": {
21 | "browser": true,
22 | "es6": true,
23 | "jasmine": true,
24 | "jest": true,
25 | "node": true
26 | },
27 | "globals": {
28 | "jest": true
29 | },
30 | "plugins": [
31 | "react",
32 | "import"
33 | ],
34 | "rules": {
35 | "accessor-pairs": ["error"],
36 | "block-scoped-var": ["error"],
37 | "consistent-return": ["error"],
38 | "curly": ["error", "all"],
39 | "default-case": ["error"],
40 | "dot-location": ["off"],
41 | "dot-notation": ["error"],
42 | "eqeqeq": ["error"],
43 | "guard-for-in": ["off"],
44 | "import/named": ["off"],
45 | "import/no-duplicates": ["error"],
46 | "import/no-named-as-default": ["error"],
47 | "new-cap": ["error"],
48 | "no-alert": [1],
49 | "no-caller": ["error"],
50 | "no-case-declarations": ["error"],
51 | "no-console": ["off"],
52 | "no-div-regex": ["error"],
53 | "no-dupe-keys": ["error"],
54 | "no-else-return": ["error"],
55 | "no-empty-pattern": ["error"],
56 | "no-eq-null": ["error"],
57 | "no-eval": ["error"],
58 | "no-extend-native": ["error"],
59 | "no-extra-bind": ["error"],
60 | "no-extra-boolean-cast": ["error"],
61 | "no-inline-comments": ["error"],
62 | "no-implicit-coercion": ["error"],
63 | "no-implied-eval": ["error"],
64 | "no-inner-declarations": ["off"],
65 | "no-invalid-this": ["error"],
66 | "no-iterator": ["error"],
67 | "no-labels": ["error"],
68 | "no-lone-blocks": ["error"],
69 | "no-loop-func": ["error"],
70 | "no-multi-str": ["error"],
71 | "no-native-reassign": ["error"],
72 | "no-new": ["error"],
73 | "no-new-func": ["error"],
74 | "no-new-wrappers": ["error"],
75 | "no-param-reassign": ["error"],
76 | "no-process-env": ["warn"],
77 | "no-proto": ["error"],
78 | "no-redeclare": ["error"],
79 | "no-return-assign": ["error"],
80 | "no-script-url": ["error"],
81 | "no-self-compare": ["error"],
82 | "no-sequences": ["error"],
83 | "no-shadow": ["off"],
84 | "no-throw-literal": ["error"],
85 | "no-undefined": ["error"],
86 | "no-unused-expressions": ["error"],
87 | "no-use-before-define": ["error", "nofunc"],
88 | "no-useless-call": ["error"],
89 | "no-useless-concat": ["error"],
90 | "no-with": ["error"],
91 | "prefer-const": ["error"],
92 | "radix": ["error"],
93 | "react/jsx-no-duplicate-props": ["error"],
94 | "react/jsx-no-undef": ["error"],
95 | "react/jsx-uses-react": ["error"],
96 | "react/jsx-uses-vars": ["error"],
97 | "react/no-did-update-set-state": ["error"],
98 | "react/no-direct-mutation-state": ["error"],
99 | "react/no-is-mounted": ["error"],
100 | "react/no-unknown-property": ["error"],
101 | "react/prefer-es6-class": ["error", "always"],
102 | "react/prop-types": "error",
103 | "valid-jsdoc": ["off"],
104 | "yoda": ["error"],
105 | "spaced-comment": ["error", "always", {
106 | "block": {
107 | exceptions: ["*"]
108 | }
109 | }],
110 | "no-unused-vars": ["error", {
111 | "args": "after-used",
112 | "argsIgnorePattern": "^_",
113 | "caughtErrorsIgnorePattern": "^e$"
114 | }],
115 | "no-magic-numbers": ["error", {
116 | "ignoreArrayIndexes": true,
117 | "ignore": [-1, 0, 1, 2, 3, 100, 10, 0.5]
118 | }],
119 | "no-underscore-dangle": ["off"]
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # production
7 | /build
8 | /demo
9 |
10 | # testing
11 | /coverage
12 |
13 | # misc
14 | .DS_Store
15 | .env.local
16 | .env.development.local
17 | .env.test.local
18 | .env.production.local
19 |
20 | npm-debug.log*
21 | yarn-debug.log*
22 | yarn-error.log*
23 |
24 | # virtualenv
25 | vv
26 | venv
27 |
28 | # python
29 | *.pyc
30 |
31 | # builds
32 | my_dash_component.egg-info
33 | dist
34 | *__pycache__*
35 | __pycache__/
36 |
37 | *.pyc
38 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/dash-color-picker.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | /node_modules
3 |
4 | # testing
5 | /coverage
6 |
7 | # misc
8 | .DS_Store
9 | .env.local
10 | .env.development.local
11 | .env.test.local
12 | .env.production.local
13 |
14 | npm-debug.log*
15 | yarn-debug.log*
16 | yarn-error.log*
17 |
18 | # Development folders and files
19 | public
20 | src
21 | scripts
22 | config
23 | .travis.yml
24 | CHANGELOG.md
25 | README.md
26 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 4,
3 | "singleQuote": true,
4 | "bracketSpacing": false,
5 | "trailingComma": "es5"
6 | }
7 |
--------------------------------------------------------------------------------
/.pylintrc:
--------------------------------------------------------------------------------
1 | [MASTER]
2 |
3 | # A comma-separated list of package or module names from where C extensions may
4 | # be loaded. Extensions are loading into the active Python interpreter and may
5 | # run arbitrary code
6 | extension-pkg-whitelist=
7 |
8 | # Add files or directories to the blacklist. They should be base names, not
9 | # paths.
10 | ignore=CVS
11 |
12 | # Add files or directories matching the regex patterns to the blacklist. The
13 | # regex matches against base names, not paths.
14 | ignore-patterns=
15 |
16 | # Python code to execute, usually for sys.path manipulation such as
17 | # pygtk.require().
18 | #init-hook=
19 |
20 | # Use multiple processes to speed up Pylint.
21 | jobs=1
22 |
23 | # List of plugins (as comma separated values of python modules names) to load,
24 | # usually to register additional checkers.
25 | load-plugins=
26 |
27 | # Pickle collected data for later comparisons.
28 | persistent=yes
29 |
30 | # Specify a configuration file.
31 | #rcfile=
32 |
33 | # When enabled, pylint would attempt to guess common misconfiguration and emit
34 | # user-friendly hints instead of false-positive error messages
35 | suggestion-mode=yes
36 |
37 | # Allow loading of arbitrary C extensions. Extensions are imported into the
38 | # active Python interpreter and may run arbitrary code.
39 | unsafe-load-any-extension=no
40 |
41 |
42 | [MESSAGES CONTROL]
43 |
44 | # Only show warnings with the listed confidence levels. Leave empty to show
45 | # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
46 | confidence=
47 |
48 | # Disable the message, report, category or checker with the given id(s). You
49 | # can either give multiple identifiers separated by comma (,) or put this
50 | # option multiple times (only on the command line, not in the configuration
51 | # file where it should appear only once).You can also use "--disable=all" to
52 | # disable everything first and then reenable specific checks. For example, if
53 | # you want to run only the similarities checker, you can use "--disable=all
54 | # --enable=similarities". If you want to run only the classes checker, but have
55 | # no Warning level messages displayed, use"--disable=all --enable=classes
56 | # --disable=W"
57 | disable=print-statement,
58 | parameter-unpacking,
59 | unpacking-in-except,
60 | old-raise-syntax,
61 | backtick,
62 | long-suffix,
63 | old-ne-operator,
64 | old-octal-literal,
65 | import-star-module-level,
66 | non-ascii-bytes-literal,
67 | raw-checker-failed,
68 | bad-inline-option,
69 | locally-disabled,
70 | locally-enabled,
71 | file-ignored,
72 | suppressed-message,
73 | useless-suppression,
74 | deprecated-pragma,
75 | apply-builtin,
76 | basestring-builtin,
77 | buffer-builtin,
78 | cmp-builtin,
79 | coerce-builtin,
80 | execfile-builtin,
81 | file-builtin,
82 | long-builtin,
83 | raw_input-builtin,
84 | reduce-builtin,
85 | standarderror-builtin,
86 | unicode-builtin,
87 | xrange-builtin,
88 | coerce-method,
89 | delslice-method,
90 | getslice-method,
91 | setslice-method,
92 | no-absolute-import,
93 | old-division,
94 | dict-iter-method,
95 | dict-view-method,
96 | next-method-called,
97 | metaclass-assignment,
98 | indexing-exception,
99 | raising-string,
100 | reload-builtin,
101 | oct-method,
102 | hex-method,
103 | nonzero-method,
104 | cmp-method,
105 | input-builtin,
106 | round-builtin,
107 | intern-builtin,
108 | unichr-builtin,
109 | map-builtin-not-iterating,
110 | zip-builtin-not-iterating,
111 | range-builtin-not-iterating,
112 | filter-builtin-not-iterating,
113 | using-cmp-argument,
114 | eq-without-hash,
115 | div-method,
116 | idiv-method,
117 | rdiv-method,
118 | exception-message-attribute,
119 | invalid-str-codec,
120 | sys-max-int,
121 | bad-python3-import,
122 | deprecated-string-function,
123 | deprecated-str-translate-call,
124 | deprecated-itertools-function,
125 | deprecated-types-field,
126 | next-method-defined,
127 | dict-items-not-iterating,
128 | dict-keys-not-iterating,
129 | dict-values-not-iterating,
130 | no-member,
131 | missing-docstring,
132 | invalid-name,
133 | redefined-builtin,
134 | wrong-import-order,
135 | too-many-arguments,
136 | too-many-locals,
137 | consider-using-enumerate,
138 | len-as-condition,
139 | too-many-branches,
140 | too-many-statements,
141 | blacklisted-name,
142 | line-too-long,
143 | bare-except,
144 | duplicate-code,
145 | too-many-function-args,
146 | attribute-defined-outside-init,
147 | broad-except
148 |
149 | # Enable the message, report, category or checker with the given id(s). You can
150 | # either give multiple identifier separated by comma (,) or put this option
151 | # multiple time (only on the command line, not in the configuration file where
152 | # it should appear only once). See also the "--disable" option for examples.
153 | enable=c-extension-no-member
154 |
155 |
156 | [REPORTS]
157 |
158 | # Python expression which should return a note less than 10 (10 is the highest
159 | # note). You have access to the variables errors warning, statement which
160 | # respectively contain the number of errors / warnings messages and the total
161 | # number of statements analyzed. This is used by the global evaluation report
162 | # (RP0004).
163 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
164 |
165 | # Template used to display messages. This is a python new-style format string
166 | # used to format the message information. See doc for all details
167 | #msg-template=
168 |
169 | # Set the output format. Available formats are text, parseable, colorized, json
170 | # and msvs (visual studio).You can also give a reporter class, eg
171 | # mypackage.mymodule.MyReporterClass.
172 | output-format=text
173 |
174 | # Tells whether to display a full report or only the messages
175 | reports=no
176 |
177 | # Activate the evaluation score.
178 | score=yes
179 |
180 |
181 | [REFACTORING]
182 |
183 | # Maximum number of nested blocks for function / method body
184 | max-nested-blocks=5
185 |
186 | # Complete name of functions that never returns. When checking for
187 | # inconsistent-return-statements if a never returning function is called then
188 | # it will be considered as an explicit return statement and no message will be
189 | # printed.
190 | never-returning-functions=optparse.Values,sys.exit
191 |
192 |
193 | [BASIC]
194 |
195 | # Naming style matching correct argument names
196 | argument-naming-style=snake_case
197 |
198 | # Regular expression matching correct argument names. Overrides argument-
199 | # naming-style
200 | #argument-rgx=
201 |
202 | # Naming style matching correct attribute names
203 | attr-naming-style=snake_case
204 |
205 | # Regular expression matching correct attribute names. Overrides attr-naming-
206 | # style
207 | #attr-rgx=
208 |
209 | # Bad variable names which should always be refused, separated by a comma
210 | bad-names=foo,
211 | bar,
212 | baz,
213 | toto,
214 | tutu,
215 | tata
216 |
217 | # Naming style matching correct class attribute names
218 | class-attribute-naming-style=any
219 |
220 | # Regular expression matching correct class attribute names. Overrides class-
221 | # attribute-naming-style
222 | #class-attribute-rgx=
223 |
224 | # Naming style matching correct class names
225 | class-naming-style=PascalCase
226 |
227 | # Regular expression matching correct class names. Overrides class-naming-style
228 | #class-rgx=
229 |
230 | # Naming style matching correct constant names
231 | const-naming-style=UPPER_CASE
232 |
233 | # Regular expression matching correct constant names. Overrides const-naming-
234 | # style
235 | #const-rgx=
236 |
237 | # Minimum line length for functions/classes that require docstrings, shorter
238 | # ones are exempt.
239 | docstring-min-length=-1
240 |
241 | # Naming style matching correct function names
242 | function-naming-style=snake_case
243 |
244 | # Regular expression matching correct function names. Overrides function-
245 | # naming-style
246 | #function-rgx=
247 |
248 | # Good variable names which should always be accepted, separated by a comma
249 | good-names=i,
250 | j,
251 | k,
252 | ex,
253 | Run,
254 | _
255 |
256 | # Include a hint for the correct naming format with invalid-name
257 | include-naming-hint=no
258 |
259 | # Naming style matching correct inline iteration names
260 | inlinevar-naming-style=any
261 |
262 | # Regular expression matching correct inline iteration names. Overrides
263 | # inlinevar-naming-style
264 | #inlinevar-rgx=
265 |
266 | # Naming style matching correct method names
267 | method-naming-style=snake_case
268 |
269 | # Regular expression matching correct method names. Overrides method-naming-
270 | # style
271 | #method-rgx=
272 |
273 | # Naming style matching correct module names
274 | module-naming-style=snake_case
275 |
276 | # Regular expression matching correct module names. Overrides module-naming-
277 | # style
278 | #module-rgx=
279 |
280 | # Colon-delimited sets of names that determine each other's naming style when
281 | # the name regexes allow several styles.
282 | name-group=
283 |
284 | # Regular expression which should only match function or class names that do
285 | # not require a docstring.
286 | no-docstring-rgx=^_
287 |
288 | # List of decorators that produce properties, such as abc.abstractproperty. Add
289 | # to this list to register other decorators that produce valid properties.
290 | property-classes=abc.abstractproperty
291 |
292 | # Naming style matching correct variable names
293 | variable-naming-style=snake_case
294 |
295 | # Regular expression matching correct variable names. Overrides variable-
296 | # naming-style
297 | #variable-rgx=
298 |
299 |
300 | [FORMAT]
301 |
302 | # Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
303 | expected-line-ending-format=
304 |
305 | # Regexp for a line that is allowed to be longer than the limit.
306 | ignore-long-lines=^\s*(# )??$
307 |
308 | # Number of spaces of indent required inside a hanging or continued line.
309 | indent-after-paren=4
310 |
311 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
312 | # tab).
313 | indent-string=' '
314 |
315 | # Maximum number of characters on a single line.
316 | max-line-length=100
317 |
318 | # Maximum number of lines in a module
319 | max-module-lines=1000
320 |
321 | # List of optional constructs for which whitespace checking is disabled. `dict-
322 | # separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
323 | # `trailing-comma` allows a space between comma and closing bracket: (a, ).
324 | # `empty-line` allows space-only lines.
325 | no-space-check=trailing-comma,
326 | dict-separator
327 |
328 | # Allow the body of a class to be on the same line as the declaration if body
329 | # contains single statement.
330 | single-line-class-stmt=no
331 |
332 | # Allow the body of an if to be on the same line as the test if there is no
333 | # else.
334 | single-line-if-stmt=no
335 |
336 |
337 | [LOGGING]
338 |
339 | # Logging modules to check that the string format arguments are in logging
340 | # function parameter format
341 | logging-modules=logging
342 |
343 |
344 | [MISCELLANEOUS]
345 |
346 | # List of note tags to take in consideration, separated by a comma.
347 | notes=FIXME,
348 | XXX,
349 |
350 |
351 | [SIMILARITIES]
352 |
353 | # Ignore comments when computing similarities.
354 | ignore-comments=yes
355 |
356 | # Ignore docstrings when computing similarities.
357 | ignore-docstrings=yes
358 |
359 | # Ignore imports when computing similarities.
360 | ignore-imports=no
361 |
362 | # Minimum lines number of a similarity.
363 | min-similarity-lines=4
364 |
365 |
366 | [SPELLING]
367 |
368 | # Limits count of emitted suggestions for spelling mistakes
369 | max-spelling-suggestions=4
370 |
371 | # Spelling dictionary name. Available dictionaries: none. To make it working
372 | # install python-enchant package.
373 | spelling-dict=
374 |
375 | # List of comma separated words that should not be checked.
376 | spelling-ignore-words=
377 |
378 | # A path to a file that contains private dictionary; one word per line.
379 | spelling-private-dict-file=
380 |
381 | # Tells whether to store unknown words to indicated private dictionary in
382 | # --spelling-private-dict-file option instead of raising a message.
383 | spelling-store-unknown-words=no
384 |
385 |
386 | [TYPECHECK]
387 |
388 | # List of decorators that produce context managers, such as
389 | # contextlib.contextmanager. Add to this list to register other decorators that
390 | # produce valid context managers.
391 | contextmanager-decorators=contextlib.contextmanager
392 |
393 | # List of members which are set dynamically and missed by pylint inference
394 | # system, and so shouldn't trigger E1101 when accessed. Python regular
395 | # expressions are accepted.
396 | generated-members=
397 |
398 | # Tells whether missing members accessed in mixin class should be ignored. A
399 | # mixin class is detected if its name ends with "mixin" (case insensitive).
400 | ignore-mixin-members=yes
401 |
402 | # This flag controls whether pylint should warn about no-member and similar
403 | # checks whenever an opaque object is returned when inferring. The inference
404 | # can return multiple potential results while evaluating a Python object, but
405 | # some branches might not be evaluated, which results in partial inference. In
406 | # that case, it might be useful to still emit no-member and other checks for
407 | # the rest of the inferred objects.
408 | ignore-on-opaque-inference=yes
409 |
410 | # List of class names for which member attributes should not be checked (useful
411 | # for classes with dynamically set attributes). This supports the use of
412 | # qualified names.
413 | ignored-classes=optparse.Values,thread._local,_thread._local
414 |
415 | # List of module names for which member attributes should not be checked
416 | # (useful for modules/projects where namespaces are manipulated during runtime
417 | # and thus existing member attributes cannot be deduced by static analysis. It
418 | # supports qualified module names, as well as Unix pattern matching.
419 | ignored-modules=
420 |
421 | # Show a hint with possible names when a member name was not found. The aspect
422 | # of finding the hint is based on edit distance.
423 | missing-member-hint=yes
424 |
425 | # The minimum edit distance a name should have in order to be considered a
426 | # similar match for a missing member name.
427 | missing-member-hint-distance=1
428 |
429 | # The total number of similar names that should be taken in consideration when
430 | # showing a hint for a missing member.
431 | missing-member-max-choices=1
432 |
433 |
434 | [VARIABLES]
435 |
436 | # List of additional names supposed to be defined in builtins. Remember that
437 | # you should avoid to define new builtins when possible.
438 | additional-builtins=
439 |
440 | # Tells whether unused global variables should be treated as a violation.
441 | allow-global-unused-variables=yes
442 |
443 | # List of strings which can identify a callback function by name. A callback
444 | # name must start or end with one of those strings.
445 | callbacks=cb_,
446 | _cb
447 |
448 | # A regular expression matching the name of dummy variables (i.e. expectedly
449 | # not used).
450 | dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
451 |
452 | # Argument names that match this expression will be ignored. Default to name
453 | # with leading underscore
454 | ignored-argument-names=_.*|^ignored_|^unused_
455 |
456 | # Tells whether we should check for unused import in __init__ files.
457 | init-import=no
458 |
459 | # List of qualified module names which can have objects that can redefine
460 | # builtins.
461 | redefining-builtins-modules=six.moves,past.builtins,future.builtins
462 |
463 |
464 | [CLASSES]
465 |
466 | # List of method names used to declare (i.e. assign) instance attributes.
467 | defining-attr-methods=__init__,
468 | __new__,
469 | setUp
470 |
471 | # List of member names, which should be excluded from the protected access
472 | # warning.
473 | exclude-protected=_asdict,
474 | _fields,
475 | _replace,
476 | _source,
477 | _make
478 |
479 | # List of valid names for the first argument in a class method.
480 | valid-classmethod-first-arg=cls
481 |
482 | # List of valid names for the first argument in a metaclass class method.
483 | valid-metaclass-classmethod-first-arg=mcs
484 |
485 |
486 | [DESIGN]
487 |
488 | # Maximum number of arguments for function / method
489 | max-args=5
490 |
491 | # Maximum number of attributes for a class (see R0902).
492 | max-attributes=7
493 |
494 | # Maximum number of boolean expressions in a if statement
495 | max-bool-expr=5
496 |
497 | # Maximum number of branch for function / method body
498 | max-branches=12
499 |
500 | # Maximum number of locals for function / method body
501 | max-locals=15
502 |
503 | # Maximum number of parents for a class (see R0901).
504 | max-parents=7
505 |
506 | # Maximum number of public methods for a class (see R0904).
507 | max-public-methods=20
508 |
509 | # Maximum number of return / yield for function / method body
510 | max-returns=6
511 |
512 | # Maximum number of statements in function / method body
513 | max-statements=50
514 |
515 | # Minimum number of public methods for a class (see R0903).
516 | min-public-methods=2
517 |
518 |
519 | [IMPORTS]
520 |
521 | # Allow wildcard imports from modules that define __all__.
522 | allow-wildcard-with-all=no
523 |
524 | # Analyse import fallback blocks. This can be used to support both Python 2 and
525 | # 3 compatible code, which means that the block might have code that exists
526 | # only in one or another interpreter, leading to false positives when analysed.
527 | analyse-fallback-blocks=no
528 |
529 | # Deprecated modules which should not be used, separated by a comma
530 | deprecated-modules=optparse,tkinter.tix
531 |
532 | # Create a graph of external dependencies in the given file (report RP0402 must
533 | # not be disabled)
534 | ext-import-graph=
535 |
536 | # Create a graph of every (i.e. internal and external) dependencies in the
537 | # given file (report RP0402 must not be disabled)
538 | import-graph=
539 |
540 | # Create a graph of internal dependencies in the given file (report RP0402 must
541 | # not be disabled)
542 | int-import-graph=
543 |
544 | # Force import order to recognize a module as part of the standard
545 | # compatibility libraries.
546 | known-standard-library=
547 |
548 | # Force import order to recognize a module as part of a third party library.
549 | known-third-party=enchant
550 |
551 |
552 | [EXCEPTIONS]
553 |
554 | # Exceptions that will emit a warning when being caught. Defaults to
555 | # "Exception"
556 | overgeneral-exceptions=Exception
557 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include dash_dual_listbox/bundle.js
2 | include dash_dual_listbox/metadata.json
3 | include dash_dual_listbox/package.json
4 | include dash_dual_listbox/style.css
5 | include README.md
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Dual listbox for Dash. Original component: https://rawgit.com/jyotirmaybanerjee/react-duallist/master/example/examples.html#
2 |
3 | Install using:
4 | pip install dash-dual-listbox
5 |
--------------------------------------------------------------------------------
/dash_dual_listbox-0.0.1.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vivekvs1/dash-dual-listbox/9067f3d93a022c00155bc68b7bdc55c7ab530097/dash_dual_listbox-0.0.1.tar.gz
--------------------------------------------------------------------------------
/dash_dual_listbox.egg-info/PKG-INFO:
--------------------------------------------------------------------------------
1 | Metadata-Version: 1.0
2 | Name: dash-dual-listbox
3 | Version: 0.0.1
4 | Summary: Dual listbox for Dash. Original component: https://rawgit.com/jyotirmaybanerjee/react-duallist/master/example/examples.html#
5 | Home-page: UNKNOWN
6 | Author: Vivek Shankar vivekvs1@gmail.com
7 | Author-email: UNKNOWN
8 | License: MIT
9 | Description: UNKNOWN
10 | Platform: UNKNOWN
11 |
--------------------------------------------------------------------------------
/dash_dual_listbox.egg-info/SOURCES.txt:
--------------------------------------------------------------------------------
1 | MANIFEST.in
2 | README.md
3 | setup.py
4 | dash_dual_listbox/DualList.py
5 | dash_dual_listbox/__init__.py
6 | dash_dual_listbox/_imports_.py
7 | dash_dual_listbox/bundle.js
8 | dash_dual_listbox/metadata.json
9 | dash_dual_listbox/package.json
10 | dash_dual_listbox/style.css
11 | dash_dual_listbox.egg-info/PKG-INFO
12 | dash_dual_listbox.egg-info/SOURCES.txt
13 | dash_dual_listbox.egg-info/dependency_links.txt
14 | dash_dual_listbox.egg-info/top_level.txt
--------------------------------------------------------------------------------
/dash_dual_listbox.egg-info/dependency_links.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/dash_dual_listbox.egg-info/top_level.txt:
--------------------------------------------------------------------------------
1 | dash_dual_listbox
2 |
--------------------------------------------------------------------------------
/dash_dual_listbox/DualList.py:
--------------------------------------------------------------------------------
1 | # AUTO GENERATED FILE - DO NOT EDIT
2 |
3 | from dash.development.base_component import Component, _explicitize_args
4 |
5 |
6 | class DualList(Component):
7 | """A DualList component.
8 |
9 |
10 | Keyword arguments:
11 | - id (string; optional): The ID of this component, used to identify dash components
12 | in callbacks. The ID needs to be unique across all of the
13 | components in an app.
14 | - selected (list; required): List of selected options, will appear in the right box
15 | - available (list; required): List of available options, will appear in the left box
16 | - leftLabel (string; optional): A header for the left (available) list
17 | - rightLabel (string; optional): A header for the right (selected) list
18 | - searchable (boolean; optional): A false value will hide the search field on the top
19 | - sortable (boolean; optional): A false value will hide the reorder buttons on the right
20 | - moveLeftIcon (string; optional): fontawesome icons or icon of your choice
21 | - moveRightIcon (string; optional): fontawesome icons or icon of your choice
22 | - moveAllLeftIcon (string; optional): fontawesome icons or icon of your choice
23 | - moveAllRightIcon (string; optional): fontawesome icons or icon of your choice
24 | - moveUpIcon (string; optional): fontawesome icons or icon of your choice
25 | - moveTopIcon (string; optional): fontawesome icons or icon of your choice
26 | - moveDownIcon (string; optional): fontawesome icons or icon of your choice
27 | - moveBottomIcon (string; optional): fontawesome icons or icon of your choice
28 |
29 | Available events: """
30 | @_explicitize_args
31 | def __init__(self, id=Component.UNDEFINED, selected=Component.REQUIRED, available=Component.REQUIRED, leftLabel=Component.UNDEFINED, rightLabel=Component.UNDEFINED, searchable=Component.UNDEFINED, sortable=Component.UNDEFINED, moveLeftIcon=Component.UNDEFINED, moveRightIcon=Component.UNDEFINED, moveAllLeftIcon=Component.UNDEFINED, moveAllRightIcon=Component.UNDEFINED, moveUpIcon=Component.UNDEFINED, moveTopIcon=Component.UNDEFINED, moveDownIcon=Component.UNDEFINED, moveBottomIcon=Component.UNDEFINED, **kwargs):
32 | self._prop_names = ['id', 'selected', 'available', 'leftLabel', 'rightLabel', 'searchable', 'sortable', 'moveLeftIcon', 'moveRightIcon', 'moveAllLeftIcon', 'moveAllRightIcon', 'moveUpIcon', 'moveTopIcon', 'moveDownIcon', 'moveBottomIcon']
33 | self._type = 'DualList'
34 | self._namespace = 'dash_dual_listbox'
35 | self._valid_wildcard_attributes = []
36 | self.available_events = []
37 | self.available_properties = ['id', 'selected', 'available', 'leftLabel', 'rightLabel', 'searchable', 'sortable', 'moveLeftIcon', 'moveRightIcon', 'moveAllLeftIcon', 'moveAllRightIcon', 'moveUpIcon', 'moveTopIcon', 'moveDownIcon', 'moveBottomIcon']
38 | self.available_wildcard_properties = []
39 |
40 | _explicit_args = kwargs.pop('_explicit_args')
41 | _locals = locals()
42 | _locals.update(kwargs) # For wildcard attrs
43 | args = {k: _locals[k] for k in _explicit_args if k != 'children'}
44 |
45 | for k in ['selected', 'available']:
46 | if k not in args:
47 | raise TypeError(
48 | 'Required argument `' + k + '` was not specified.')
49 | super(DualList, self).__init__(**args)
50 |
51 | def __repr__(self):
52 | if(any(getattr(self, c, None) is not None
53 | for c in self._prop_names
54 | if c is not self._prop_names[0])
55 | or any(getattr(self, c, None) is not None
56 | for c in self.__dict__.keys()
57 | if any(c.startswith(wc_attr)
58 | for wc_attr in self._valid_wildcard_attributes))):
59 | props_string = ', '.join([c+'='+repr(getattr(self, c, None))
60 | for c in self._prop_names
61 | if getattr(self, c, None) is not None])
62 | wilds_string = ', '.join([c+'='+repr(getattr(self, c, None))
63 | for c in self.__dict__.keys()
64 | if any([c.startswith(wc_attr)
65 | for wc_attr in
66 | self._valid_wildcard_attributes])])
67 | return ('DualList(' + props_string +
68 | (', ' + wilds_string if wilds_string != '' else '') + ')')
69 | else:
70 | return (
71 | 'DualList(' +
72 | repr(getattr(self, self._prop_names[0], None)) + ')')
73 |
--------------------------------------------------------------------------------
/dash_dual_listbox/__init__.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function as _
2 |
3 | import os as _os
4 | import sys as _sys
5 | import json
6 |
7 | import dash as _dash
8 |
9 | if not hasattr(_dash, 'development'):
10 | print('Dash was not successfully imported. '
11 | 'Make sure you don\'t have a file '
12 | 'named \n"dash.py" in your current directory.', file=_sys.stderr)
13 | _sys.exit(1)
14 |
15 | _basepath = _os.path.dirname(__file__)
16 | _filepath = _os.path.abspath(_os.path.join(_basepath, 'package.json'))
17 | with open(_filepath) as f:
18 | package = json.load(f)
19 |
20 | package_name = package['name'].replace(' ', '_').replace('-', '_')
21 | __version__ = package['version']
22 |
23 | _current_path = _os.path.dirname(_os.path.abspath(__file__))
24 | _components = _dash.development.component_loader.load_components(
25 | _os.path.join(_current_path, 'metadata.json'),
26 | package_name
27 | )
28 |
29 | _this_module = _sys.modules[__name__]
30 |
31 |
32 | _js_dist = [
33 | {
34 | 'relative_package_path': 'bundle.js',
35 | 'external_url': (
36 | 'https://unpkg.com/my_dash_component'
37 | '/' + package_name + '/bundle.js'
38 | ).format(__version__),
39 | 'namespace': package_name
40 | }
41 | ]
42 |
43 | _css_dist = []
44 |
45 |
46 | for _component in _components:
47 | setattr(_this_module, _component.__name__, _component)
48 | setattr(_component, '_js_dist', _js_dist)
49 | setattr(_component, '_css_dist', _css_dist)
--------------------------------------------------------------------------------
/dash_dual_listbox/_imports_.py:
--------------------------------------------------------------------------------
1 | from .DualList import DualList
2 |
3 |
4 | __all__ = [
5 | "DualList",
6 | ]
7 |
--------------------------------------------------------------------------------
/dash_dual_listbox/bundle.js:
--------------------------------------------------------------------------------
1 | window.dash_dual_listbox=function(e){var t={};function o(n){if(t[n])return t[n].exports;var l=t[n]={i:n,l:!1,exports:{}};return e[n].call(l.exports,l,l.exports,o),l.l=!0,l.exports}return o.m=e,o.c=t,o.d=function(e,t,n){o.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(e,t){if(1&t&&(e=o(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(o.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var l in e)o.d(n,l,function(t){return e[t]}.bind(null,l));return n},o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="",o(o.s=1)}([function(e,t){e.exports=window.React},function(e,t,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.DualList=void 0;var n=function(e){return e&&e.__esModule?e:{default:e}}(o(2));t.DualList=n.default},function(e,t,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(e,t){for(var o=0;o"),moveAllRightIcon:a.default.createElement("span",{style:{fontSize:"14px",fontWeight:"bold"}},">>"),moveUpIcon:a.default.createElement("span",{style:{fontSize:"14px",fontWeight:"bold",color:"#000"}},"↑"),moveTopIcon:a.default.createElement("span",{style:{fontSize:"14px",fontWeight:"bold",color:"#000"}},"⇈"),moveDownIcon:a.default.createElement("span",{style:{fontSize:"14px",fontWeight:"bold",color:"#000"}},"↓"),moveBottomIcon:a.default.createElement("span",{style:{fontSize:"14px",fontWeight:"bold",color:"#000"}},"⇊")},t.default=i}]);
--------------------------------------------------------------------------------
/dash_dual_listbox/metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "src/lib/components/DualList.react.js": {
3 | "description": "",
4 | "displayName": "DualList",
5 | "methods": [
6 | {
7 | "name": "onMove",
8 | "docblock": null,
9 | "modifiers": [],
10 | "params": [
11 | {
12 | "name": "selected",
13 | "type": null
14 | }
15 | ],
16 | "returns": null
17 | }
18 | ],
19 | "props": {
20 | "id": {
21 | "type": {
22 | "name": "string"
23 | },
24 | "required": false,
25 | "description": "The ID of this component, used to identify dash components\r\nin callbacks. The ID needs to be unique across all of the\r\ncomponents in an app."
26 | },
27 | "selected": {
28 | "type": {
29 | "name": "arrayOf",
30 | "value": {
31 | "name": "union",
32 | "value": [
33 | {
34 | "name": "string"
35 | }
36 | ]
37 | }
38 | },
39 | "required": true,
40 | "description": "List of selected options, will appear in the right box"
41 | },
42 | "available": {
43 | "type": {
44 | "name": "arrayOf",
45 | "value": {
46 | "name": "union",
47 | "value": [
48 | {
49 | "name": "shape",
50 | "value": {
51 | "value": {
52 | "name": "any",
53 | "required": true
54 | },
55 | "label": {
56 | "name": "string",
57 | "required": true
58 | }
59 | }
60 | },
61 | {
62 | "name": "shape",
63 | "value": {
64 | "value": {
65 | "name": "any",
66 | "required": false
67 | },
68 | "options": {
69 | "name": "arrayOf",
70 | "value": {
71 | "name": "shape",
72 | "value": {
73 | "value": {
74 | "name": "any",
75 | "required": true
76 | },
77 | "label": {
78 | "name": "string",
79 | "required": true
80 | }
81 | }
82 | },
83 | "required": false
84 | }
85 | }
86 | }
87 | ]
88 | }
89 | },
90 | "required": true,
91 | "description": "List of available options, will appear in the left box"
92 | },
93 | "leftLabel": {
94 | "type": {
95 | "name": "string"
96 | },
97 | "required": false,
98 | "description": "A header for the left (available) list"
99 | },
100 | "rightLabel": {
101 | "type": {
102 | "name": "string"
103 | },
104 | "required": false,
105 | "description": "A header for the right (selected) list"
106 | },
107 | "searchable": {
108 | "type": {
109 | "name": "bool"
110 | },
111 | "required": false,
112 | "description": "A false value will hide the search field on the top"
113 | },
114 | "sortable": {
115 | "type": {
116 | "name": "bool"
117 | },
118 | "required": false,
119 | "description": "A false value will hide the reorder buttons on the right"
120 | },
121 | "moveLeftIcon": {
122 | "type": {
123 | "name": "string"
124 | },
125 | "required": false,
126 | "description": "fontawesome icons or icon of your choice"
127 | },
128 | "moveRightIcon": {
129 | "type": {
130 | "name": "string"
131 | },
132 | "required": false,
133 | "description": "fontawesome icons or icon of your choice"
134 | },
135 | "moveAllLeftIcon": {
136 | "type": {
137 | "name": "string"
138 | },
139 | "required": false,
140 | "description": "fontawesome icons or icon of your choice"
141 | },
142 | "moveAllRightIcon": {
143 | "type": {
144 | "name": "string"
145 | },
146 | "required": false,
147 | "description": "fontawesome icons or icon of your choice"
148 | },
149 | "moveUpIcon": {
150 | "type": {
151 | "name": "string"
152 | },
153 | "required": false,
154 | "description": "fontawesome icons or icon of your choice"
155 | },
156 | "moveTopIcon": {
157 | "type": {
158 | "name": "string"
159 | },
160 | "required": false,
161 | "description": "fontawesome icons or icon of your choice"
162 | },
163 | "moveDownIcon": {
164 | "type": {
165 | "name": "string"
166 | },
167 | "required": false,
168 | "description": "fontawesome icons or icon of your choice"
169 | },
170 | "moveBottomIcon": {
171 | "type": {
172 | "name": "string"
173 | },
174 | "required": false,
175 | "description": "fontawesome icons or icon of your choice"
176 | },
177 | "setProps": {
178 | "type": {
179 | "name": "func"
180 | },
181 | "required": false,
182 | "description": ""
183 | }
184 | }
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/dash_dual_listbox/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dash-dual-listbox",
3 | "version": "0.0.1",
4 | "description": "Dual listbox for Dash. Original component: https://rawgit.com/jyotirmaybanerjee/react-duallist/master/example/examples.html# ",
5 | "main": "build/index.js",
6 | "scripts": {
7 | "start": "webpack-serve ./webpack.serve.config.js --open",
8 | "build:js-dev": "webpack --mode development",
9 | "build:js": "webpack --mode production",
10 | "build:py": "node ./extract-meta src/lib/components > dash_dual_listbox/metadata.json && copyfiles package.json dash_dual_listbox && python -c \"import dash; dash.development.component_loader.generate_classes('dash_dual_listbox', 'dash_dual_listbox/metadata.json')\"",
11 | "build:all": "npm run build:js & npm run build:py",
12 | "build:all-dev": "npm run build:js-dev & npm run build:py"
13 | },
14 | "author": "Vivek Shankar vivekvs1@gmail.com",
15 | "license": "MIT",
16 | "dependencies": {
17 | "ramda": "^0.25.0",
18 | "react": "15.4.2",
19 | "react-dom": "15.4.2",
20 | "react-duallist": "^1.1.5"
21 | },
22 | "devDependencies": {
23 | "babel-core": "^6.26.3",
24 | "babel-eslint": "^8.2.3",
25 | "babel-loader": "^7.1.4",
26 | "copyfiles": "^2.0.0",
27 | "babel-preset-env": "^1.7.0",
28 | "babel-preset-react": "^6.24.1",
29 | "css-loader": "^0.28.11",
30 | "eslint": "^4.19.1",
31 | "eslint-config-prettier": "^2.9.0",
32 | "eslint-plugin-import": "^2.12.0",
33 | "eslint-plugin-react": "^7.9.1",
34 | "npm": "^6.1.0",
35 | "react-docgen": "^2.20.1",
36 | "style-loader": "^0.21.0",
37 | "webpack": "^4.8.3",
38 | "webpack-cli": "^2.1.3",
39 | "webpack-serve": "^1.0.2"
40 | },
41 | "peerDependencies": {
42 | "react": ">=0.14",
43 | "react-dom": ">=0.14"
44 | },
45 | "engines": {
46 | "node": ">=8.11.0",
47 | "npm": ">=6.1.0"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/dash_dual_listbox/style.css:
--------------------------------------------------------------------------------
1 | .react-listbox-dual-list {
2 | display: flex;
3 | align-items: center;
4 | box-sizing: border-box;
5 | font-family: Roboto, sans-serif;
6 | }
7 |
8 | .react-listbox-dual-list * {
9 | box-sizing: border-box;
10 | }
11 |
12 | .react-listbox-dual-list input:disabled, .react-listbox-dual-list select:disabled {
13 | background: #eee;
14 | cursor: not-allowed;
15 | }
16 |
17 | .react-listbox-dual-list button, .react-listbox-dual-list select {
18 | line-height: 1.42857;
19 | font-family: inherit;
20 | }
21 |
22 | .react-listbox-dual-list .react-listbox-list-box {
23 | display: flex;
24 | flex: 1 1 0;
25 | flex-direction: column;
26 | align-self: stretch;
27 | }
28 |
29 | .react-listbox-dual-list .react-listbox-list-box .search-bar {
30 | margin-bottom: 5px;
31 | }
32 |
33 | .react-listbox-dual-list .react-listbox-list-box select {
34 | min-height: 150px;
35 | }
36 |
37 | .react-listbox-dual-list .react-listbox-list-box .list-filter, .react-listbox-dual-list .react-listbox-list-box .list-control {
38 | display: block;
39 | border: 1px solid #ccc;
40 | border-radius: 2px;
41 | padding: 8px 12px;
42 | width: 100%;
43 | color: #333;
44 | font-size: 14px;
45 | }
46 |
47 | .react-listbox-dual-list .react-listbox-list-box .list-filter {
48 | margin-bottom: 10px;
49 | }
50 |
51 | .react-listbox-dual-list .react-listbox-center-toolbar {
52 | display: flex;
53 | flex: 0 0 auto;
54 | flex-direction: column;
55 | margin: 55px 10px 0 10px;
56 | }
57 |
58 | .react-listbox-dual-list .react-listbox-center-toolbar .move-left, .react-listbox-dual-list .react-listbox-center-toolbar .move-right {
59 | display: flex;
60 | flex-direction: column;
61 | }
62 |
63 | .react-listbox-dual-list .react-listbox-center-toolbar .move-right {
64 | margin-bottom: 10px;
65 | }
66 |
67 | .react-listbox-dual-list .react-listbox-right-toolbar {
68 | display: flex;
69 | flex: 0 0 auto;
70 | flex-direction: column;
71 | margin: 55px 10px 0 10px;
72 | }
73 |
74 | .react-listbox-dual-list .react-listbox-right-toolbar .move-top, .react-listbox-dual-list .react-listbox-right-toolbar .move-bottom {
75 | display: flex;
76 | flex-direction: column;
77 | }
78 |
79 | .react-listbox-dual-list .react-listbox-right-toolbar .move-top {
80 | margin-bottom: 10px;
81 | }
82 |
83 | .react-listbox-dual-list .btn-move {
84 | margin-bottom: 5px;
85 | border: 1px solid #ccc;
86 | border-radius: 2px;
87 | background: #fff;
88 | cursor: pointer;
89 | padding: 5px 10px;
90 | color: #333;
91 | font-size: 12px;
92 | }
93 |
94 | .react-listbox-dual-list .btn-move:active:not(:disabled), .react-listbox-dual-list .btn-move:focus:not(:disabled) {
95 | border-color: #8c8c8c;
96 | background: #e6e6e6;
97 | }
98 |
99 | .react-listbox-dual-list .btn-move:focus:not(:disabled) {
100 | outline: thin dotted;
101 | outline-offset: -2px;
102 | }
103 |
104 | .react-listbox-dual-list .btn-move:hover:not(:disabled) {
105 | border-color: #adadad;
106 | background: #e6e6e6;
107 | }
108 |
109 | .react-listbox-dual-list .btn-move:disabled {
110 | opacity: 0.5;
111 | cursor: not-allowed;
112 | }
113 |
114 | .react-listbox-dual-list .btn-move:last-child {
115 | margin-bottom: 0;
116 | }
117 |
118 | .react-listbox-dual-list .btn-move i {
119 | margin: 0 -1px;
120 | }
121 |
122 | .react-listbox-dual-list .list-container {
123 | display: flex;
124 | flex: 1 0 auto;
125 | }
126 |
127 | .react-listbox-dual-list .list-label {
128 | position: absolute;
129 | clip: rect(0 0 0 0);
130 | }
131 |
132 | .react-listbox-dual-list .list-control {
133 | flex: 1 0 auto;
134 | }
135 |
136 | .react-listbox-dual-list .list-control optgroup {
137 | font: inherit;
138 | font-weight: 700;
139 | }
--------------------------------------------------------------------------------
/extract-meta:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const fs = require('fs');
4 | const path = require('path');
5 | const reactDocs = require('react-docgen');
6 |
7 | const componentPaths = process.argv.slice(2);
8 | if (!componentPaths.length) {
9 | help();
10 | process.exit(1);
11 | }
12 |
13 | const metadata = Object.create(null);
14 | componentPaths.forEach(componentPath =>
15 | collectMetadataRecursively(componentPath)
16 | );
17 | writeOut(metadata);
18 |
19 | function help() {
20 | console.error('usage: ');
21 | console.error(
22 | 'extract-meta path/to/component(s) ' +
23 | ' [path/to/more/component(s), ...] > metadata.json'
24 | );
25 | }
26 |
27 | function writeError(msg, filePath) {
28 | if (filePath) {
29 | process.stderr.write(`Error with path ${filePath}`);
30 | }
31 |
32 | process.stderr.write(msg + '\n');
33 | if (msg instanceof Error) {
34 | process.stderr.write(msg.stack + '\n');
35 | }
36 | }
37 |
38 | function parseFile(filepath) {
39 | const urlpath = filepath.split(path.sep).join('/');
40 | let src;
41 |
42 | if (!['.jsx', '.js'].includes(path.extname(filepath))) {
43 | return;
44 | }
45 |
46 | try {
47 | src = fs.readFileSync(filepath);
48 | metadata[urlpath] = reactDocs.parse(src);
49 | } catch (error) {
50 | writeError(error, filepath);
51 | }
52 | }
53 |
54 | function collectMetadataRecursively(componentPath) {
55 | if (fs.lstatSync(componentPath).isDirectory()) {
56 | let dirs;
57 | try {
58 | dirs = fs.readdirSync(componentPath);
59 | } catch (error) {
60 | writeError(error, componentPath);
61 | }
62 | dirs.forEach(filename => {
63 | const filepath = path.join(componentPath, filename);
64 | if (fs.lstatSync(filepath).isDirectory()) {
65 | collectMetadataRecursively(filepath);
66 | } else {
67 | parseFile(filepath);
68 | }
69 | });
70 | } else {
71 | parseFile(componentPath);
72 | }
73 | }
74 |
75 | function writeOut(result) {
76 | console.log(JSON.stringify(result, '\t', 2));
77 | }
78 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | dash-dual-listbox
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dash-dual-listbox",
3 | "version": "0.0.1",
4 | "description": "Dual listbox for Dash. Original component: https://rawgit.com/jyotirmaybanerjee/react-duallist/master/example/examples.html# ",
5 | "main": "build/index.js",
6 | "scripts": {
7 | "start": "webpack-serve ./webpack.serve.config.js --open",
8 | "build:js-dev": "webpack --mode development",
9 | "build:js": "webpack --mode production",
10 | "build:py": "node ./extract-meta src/lib/components > dash_dual_listbox/metadata.json && copyfiles package.json dash_dual_listbox && python -c \"import dash; dash.development.component_loader.generate_classes('dash_dual_listbox', 'dash_dual_listbox/metadata.json')\"",
11 | "build:all": "npm run build:js & npm run build:py",
12 | "build:all-dev": "npm run build:js-dev & npm run build:py"
13 | },
14 | "author": "Vivek Shankar vivekvs1@gmail.com",
15 | "license": "MIT",
16 | "dependencies": {
17 | "ramda": "^0.25.0",
18 | "react": "15.4.2",
19 | "react-dom": "15.4.2",
20 | "react-duallist": "^1.1.5"
21 | },
22 | "devDependencies": {
23 | "babel-core": "^6.26.3",
24 | "babel-eslint": "^8.2.3",
25 | "babel-loader": "^7.1.4",
26 | "copyfiles": "^2.0.0",
27 | "babel-preset-env": "^1.7.0",
28 | "babel-preset-react": "^6.24.1",
29 | "css-loader": "^0.28.11",
30 | "eslint": "^4.19.1",
31 | "eslint-config-prettier": "^2.9.0",
32 | "eslint-plugin-import": "^2.12.0",
33 | "eslint-plugin-react": "^7.9.1",
34 | "npm": "^6.1.0",
35 | "react-docgen": "^2.20.1",
36 | "style-loader": "^0.21.0",
37 | "webpack": "^4.8.3",
38 | "webpack-cli": "^2.1.3",
39 | "webpack-serve": "^1.0.2"
40 | },
41 | "peerDependencies": {
42 | "react": ">=0.14",
43 | "react-dom": ">=0.14"
44 | },
45 | "engines": {
46 | "node": ">=8.11.0",
47 | "npm": ">=6.1.0"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import json
2 | import os
3 | from setuptools import setup
4 |
5 |
6 | with open(os.path.join('dash_dual_listbox', 'package.json')) as f:
7 | package = json.load(f)
8 |
9 | package_name = package["name"].replace(" ", "_").replace("-", "_")
10 |
11 | setup(
12 | name=package_name,
13 | version=package["version"],
14 | author=package['author'],
15 | packages=[package_name],
16 | include_package_data=True,
17 | license=package['license'],
18 | description=package['description'] if 'description' in package else package_name,
19 | install_requires=[]
20 | )
21 |
--------------------------------------------------------------------------------
/src/demo/App.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {DualList} from '../lib';
3 | import './style.css';
4 |
5 | class App extends Component {
6 |
7 | constructor(props) {
8 | super(props);
9 | this.state = {
10 | available: [
11 | {label: 'sdf', value: 'AL'},
12 | {label: 'Alassdfsdfka', value: 'AK'},
13 | {label: 'Arizona', value: 'AZ'},
14 | {label: 'Arkansas', value: 'AR'},
15 | {label: 'California', value: 'CA'},
16 | {label: 'Colorado', value: 'CO'},
17 | {label: 'sdfg', value: 'CT'},
18 | {label: 'Delaware', value: 'DE'},
19 | {label: 'Florida', value: 'FL'},
20 | {label: 'Georgia', value: 'GA'},
21 | ],
22 | selected: ['AL', 'CA', 'AK'],
23 | }
24 |
25 | }
26 |
27 | render() {
28 | const {available, selected} = this.state;
29 | const leftLabel = ['Left Editable label']
30 | const rightLabel = ['Right Editable label']
31 |
32 |
33 | return (
34 |
38 |
39 | );
40 | }
41 | }
42 |
43 | export default App;
44 |
--------------------------------------------------------------------------------
/src/demo/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 |
6 | ReactDOM.render(, document.getElementById('root'));
7 |
--------------------------------------------------------------------------------
/src/demo/style.css:
--------------------------------------------------------------------------------
1 | .react-listbox-dual-list {
2 | display: flex;
3 | align-items: center;
4 | box-sizing: border-box;
5 | font-family: Roboto, sans-serif;
6 | }
7 |
8 | .react-listbox-dual-list * {
9 | box-sizing: border-box;
10 | }
11 |
12 | .react-listbox-dual-list input:disabled, .react-listbox-dual-list select:disabled {
13 | background: #eee;
14 | cursor: not-allowed;
15 | }
16 |
17 | .react-listbox-dual-list button, .react-listbox-dual-list select {
18 | line-height: 1.42857;
19 | font-family: inherit;
20 | }
21 |
22 | .react-listbox-dual-list .react-listbox-list-box {
23 | display: flex;
24 | flex: 1 1 0;
25 | flex-direction: column;
26 | align-self: stretch;
27 | }
28 |
29 | .react-listbox-dual-list .react-listbox-list-box .search-bar {
30 | margin-bottom: 5px;
31 | }
32 |
33 | .react-listbox-dual-list .react-listbox-list-box select {
34 | min-height: 150px;
35 | }
36 |
37 | .react-listbox-dual-list .react-listbox-list-box .list-filter, .react-listbox-dual-list .react-listbox-list-box .list-control {
38 | display: block;
39 | border: 1px solid #ccc;
40 | border-radius: 2px;
41 | padding: 8px 12px;
42 | width: 100%;
43 | color: #333;
44 | font-size: 14px;
45 | }
46 |
47 | .react-listbox-dual-list .react-listbox-list-box .list-filter {
48 | margin-bottom: 10px;
49 | }
50 |
51 | .react-listbox-dual-list .react-listbox-center-toolbar {
52 | display: flex;
53 | flex: 0 0 auto;
54 | flex-direction: column;
55 | margin: 55px 10px 0 10px;
56 | }
57 |
58 | .react-listbox-dual-list .react-listbox-center-toolbar .move-left, .react-listbox-dual-list .react-listbox-center-toolbar .move-right {
59 | display: flex;
60 | flex-direction: column;
61 | }
62 |
63 | .react-listbox-dual-list .react-listbox-center-toolbar .move-right {
64 | margin-bottom: 10px;
65 | }
66 |
67 | .react-listbox-dual-list .react-listbox-right-toolbar {
68 | display: flex;
69 | flex: 0 0 auto;
70 | flex-direction: column;
71 | margin: 55px 10px 0 10px;
72 | }
73 |
74 | .react-listbox-dual-list .react-listbox-right-toolbar .move-top, .react-listbox-dual-list .react-listbox-right-toolbar .move-bottom {
75 | display: flex;
76 | flex-direction: column;
77 | }
78 |
79 | .react-listbox-dual-list .react-listbox-right-toolbar .move-top {
80 | margin-bottom: 10px;
81 | }
82 |
83 | .react-listbox-dual-list .btn-move {
84 | margin-bottom: 5px;
85 | border: 1px solid #ccc;
86 | border-radius: 2px;
87 | background: #fff;
88 | cursor: pointer;
89 | padding: 5px 10px;
90 | color: #333;
91 | font-size: 12px;
92 | }
93 |
94 | .react-listbox-dual-list .btn-move:active:not(:disabled), .react-listbox-dual-list .btn-move:focus:not(:disabled) {
95 | border-color: #8c8c8c;
96 | background: #e6e6e6;
97 | }
98 |
99 | .react-listbox-dual-list .btn-move:focus:not(:disabled) {
100 | outline: thin dotted;
101 | outline-offset: -2px;
102 | }
103 |
104 | .react-listbox-dual-list .btn-move:hover:not(:disabled) {
105 | border-color: #adadad;
106 | background: #e6e6e6;
107 | }
108 |
109 | .react-listbox-dual-list .btn-move:disabled {
110 | opacity: 0.5;
111 | cursor: not-allowed;
112 | }
113 |
114 | .react-listbox-dual-list .btn-move:last-child {
115 | margin-bottom: 0;
116 | }
117 |
118 | .react-listbox-dual-list .btn-move i {
119 | margin: 0 -1px;
120 | }
121 |
122 | .react-listbox-dual-list .list-container {
123 | display: flex;
124 | flex: 1 0 auto;
125 | }
126 |
127 | .react-listbox-dual-list .list-label {
128 | position: absolute;
129 | clip: rect(0 0 0 0);
130 | }
131 |
132 | .react-listbox-dual-list .list-control {
133 | flex: 1 0 auto;
134 | }
135 |
136 | .react-listbox-dual-list .list-control optgroup {
137 | font: inherit;
138 | font-weight: 700;
139 | }
--------------------------------------------------------------------------------
/src/lib/components/DualList.react.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React, {Component} from 'react';
3 | import Duallist from 'react-duallist';
4 |
5 | class DualList extends Component {
6 |
7 | constructor(props) {
8 | super(props);
9 | this.state = {
10 | selected: props.selected,
11 | }
12 |
13 | this.onMove = this.onMove.bind(this);
14 | }
15 |
16 |
17 | onMove(selected) {
18 | this.setState({selected});
19 |
20 | const {setProps} = this.props;
21 | if (setProps) {
22 | setProps({selected: selected})
23 | }
24 |
25 | }
26 |
27 |
28 | render() {
29 |
30 |
31 | return (
32 |
33 |
36 |
37 |
38 | );
39 | }
40 | }
41 |
42 | export default DualList;
43 |
44 |
45 | DualList.propTypes = {
46 |
47 | /**
48 | * The ID of this component, used to identify dash components
49 | * in callbacks. The ID needs to be unique across all of the
50 | * components in an app.
51 | */
52 | id: PropTypes.string,
53 |
54 | /**
55 | * List of selected options, will appear in the right box
56 | */
57 | selected: PropTypes.arrayOf(
58 | PropTypes.oneOfType([PropTypes.string])).isRequired,
59 |
60 | /**
61 | * List of available options, will appear in the left box
62 | */
63 | available: PropTypes.arrayOf(
64 | PropTypes.oneOfType([
65 | PropTypes.shape({
66 | value: PropTypes.any.isRequired,
67 | label: PropTypes.string.isRequired,
68 | }),
69 | PropTypes.shape({
70 | value: PropTypes.any,
71 | options: PropTypes.arrayOf(PropTypes.shape({
72 | value: PropTypes.any.isRequired,
73 | label: PropTypes.string.isRequired
74 | }))
75 | }),
76 | ]),
77 | ).isRequired,
78 |
79 | /**
80 | * A header for the left (available) list
81 | */
82 | leftLabel: PropTypes.string,
83 |
84 | /**
85 | * A header for the right (selected) list
86 | */
87 | rightLabel: PropTypes.string,
88 |
89 | /**
90 | * A false value will hide the search field on the top
91 | */
92 | searchable: PropTypes.bool,
93 |
94 | /**
95 | * A false value will hide the reorder buttons on the right
96 | */
97 | sortable: PropTypes.bool,
98 |
99 | /**
100 | * fontawesome icons or icon of your choice
101 | */
102 | moveLeftIcon: PropTypes.string,
103 |
104 | /**
105 | * fontawesome icons or icon of your choice
106 | */
107 | moveRightIcon: PropTypes.string,
108 |
109 | /**
110 | * fontawesome icons or icon of your choice
111 | */
112 | moveAllLeftIcon: PropTypes.string,
113 |
114 | /**
115 | * fontawesome icons or icon of your choice
116 | */
117 | moveAllRightIcon: PropTypes.string,
118 |
119 | /**
120 | * fontawesome icons or icon of your choice
121 | */
122 | moveUpIcon: PropTypes.string,
123 |
124 | /**
125 | * fontawesome icons or icon of your choice
126 | */
127 | moveTopIcon: PropTypes.string,
128 |
129 | /**
130 | * fontawesome icons or icon of your choice
131 | */
132 | moveDownIcon: PropTypes.string,
133 |
134 | /**
135 | * fontawesome icons or icon of your choice
136 | */
137 | moveBottomIcon: PropTypes.string,
138 |
139 |
140 | setProps: PropTypes.func
141 | }
--------------------------------------------------------------------------------
/src/lib/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/prefer-default-export */
2 | import DualList from './components/DualList.react.js';
3 |
4 | export {
5 | DualList
6 | };
7 |
--------------------------------------------------------------------------------
/tests/IntegrationTests.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 |
3 | import logging
4 | import os
5 | import multiprocessing
6 | import sys
7 | import time
8 | import unittest
9 | import percy
10 | import threading
11 | import platform
12 | import flask
13 | import requests
14 |
15 | from selenium import webdriver
16 | from selenium.webdriver.chrome.options import Options
17 |
18 |
19 | class IntegrationTests(unittest.TestCase):
20 | def percy_snapshot(self, name=''):
21 | if os.environ.get('PERCY_ENABLED', False):
22 | snapshot_name = '{} - {}'.format(name, sys.version_info)
23 | self.percy_runner.snapshot(
24 | name=snapshot_name
25 | )
26 |
27 | @classmethod
28 | def setUpClass(cls):
29 | super(IntegrationTests, cls).setUpClass()
30 |
31 | options = Options()
32 | if 'DASH_TEST_CHROMEPATH' in os.environ:
33 | options.binary_location = os.environ['DASH_TEST_CHROMEPATH']
34 |
35 | cls.driver = webdriver.Chrome(chrome_options=options)
36 |
37 | if os.environ.get('PERCY_ENABLED', False):
38 | loader = percy.ResourceLoader(
39 | webdriver=cls.driver
40 | )
41 | cls.percy_runner = percy.Runner(loader=loader)
42 | cls.percy_runner.initialize_build()
43 |
44 | @classmethod
45 | def tearDownClass(cls):
46 | super(IntegrationTests, cls).tearDownClass()
47 |
48 | cls.driver.quit()
49 | if os.environ.get('PERCY_ENABLED', False):
50 | cls.percy_runner.finalize_build()
51 |
52 | def setUp(self):
53 | pass
54 |
55 | def tearDown(self):
56 | time.sleep(3)
57 | if platform.system() == 'Windows':
58 | requests.get('http://localhost:8050/stop')
59 | else:
60 | self.server_process.terminate()
61 | time.sleep(3)
62 |
63 | def startServer(self, app):
64 | if 'DASH_TEST_PROCESSES' in os.environ:
65 | processes = int(os.environ['DASH_TEST_PROCESSES'])
66 | else:
67 | processes = 4
68 |
69 | def run():
70 | app.scripts.config.serve_locally = True
71 | app.css.config.serve_locally = True
72 | app.run_server(
73 | port=8050,
74 | debug=False,
75 | processes=processes
76 | )
77 |
78 | def run_windows():
79 | app.scripts.config.serve_locally = True
80 | app.css.config.serve_locally = True
81 |
82 | @app.server.route('/stop')
83 | def _stop_server_windows():
84 | stopper = flask.request.environ['werkzeug.server.shutdown']
85 | stopper()
86 | return 'stop'
87 |
88 | app.run_server(
89 | port=8050,
90 | debug=False,
91 | threaded=True
92 | )
93 |
94 | # Run on a separate process so that it doesn't block
95 |
96 | system = platform.system()
97 | if system == 'Windows':
98 | self.server_thread = threading.Thread(target=run_windows)
99 | self.server_thread.start()
100 | else:
101 | self.server_process = multiprocessing.Process(target=run)
102 | self.server_process.start()
103 | logging.getLogger('werkzeug').setLevel(logging.ERROR)
104 | time.sleep(5)
105 |
106 | # Visit the dash page
107 | self.driver.get('http://localhost:8050')
108 | time.sleep(0.5)
109 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vivekvs1/dash-dual-listbox/9067f3d93a022c00155bc68b7bdc55c7ab530097/tests/__init__.py
--------------------------------------------------------------------------------
/tests/requirements.txt:
--------------------------------------------------------------------------------
1 | # Switch into a virtual environment
2 | # pip install -r requirements.txt
3 |
4 | chromedriver-binary
5 | dash
6 | dash-core-components
7 | dash-html-components
8 | dash-renderer
9 | ipdb
10 | percy
11 | selenium
12 | flake8
13 | pylint
14 |
--------------------------------------------------------------------------------
/tests/test_render.py:
--------------------------------------------------------------------------------
1 | from .IntegrationTests import IntegrationTests
2 | import dash
3 | import dash_html_components as html
4 | from selenium.webdriver.common.by import By
5 | from selenium.webdriver.support.ui import WebDriverWait
6 | from selenium.webdriver.support import expected_conditions as EC
7 |
8 | from dash_color_picker import ColorPicker # pylint: disable=no-name-in-module
9 |
10 |
11 | class Tests(IntegrationTests):
12 | def test_render_component(self):
13 | app = dash.Dash(__name__)
14 | app.layout = html.Div([
15 | html.Div(id='waitfor'),
16 | ColorPicker(id='DualList', color='#f22')
17 | ])
18 |
19 | self.startServer(app)
20 |
21 | WebDriverWait(self.driver, 10).until(
22 | EC.presence_of_element_located((By.ID, "waitfor"))
23 | )
24 |
25 | self.percy_snapshot('Simple Render')
26 |
--------------------------------------------------------------------------------
/usage.py:
--------------------------------------------------------------------------------
1 | from dash_dual_listbox import DualList
2 | import dash
3 | from dash.dependencies import Input, Output
4 | import dash_html_components as html
5 |
6 | app = dash.Dash('')
7 |
8 | app.scripts.config.serve_locally = True
9 | app.css.config.serve_locally = True
10 |
11 | app.layout = html.Div([
12 | DualList(id='DualList', available=[{'label': 'sdf', 'value': 'AL'},
13 | {'label': 'Alassdfsdfka', 'value': 'AK'},
14 | {'label': 'Arizona', 'value': 'AZ'},
15 | {'label': 'Arkansas', 'value': 'AR'},
16 | {'label': 'California', 'value': 'CA'},
17 | {'label': 'Colorado', 'value': 'CO'},
18 | {'label': 'sdfg', 'value': 'CT'},
19 | {'label': 'Delaware', 'value': 'DE'},
20 | {'label': 'Florida', 'value': 'FL'},
21 | {'label': 'Georgia', 'value': 'GA'}], selected=['AL']),
22 | ])
23 |
24 |
25 | # @app.callback(Output('display', 'children'),
26 | # [Input('DualList', 'selected'),
27 | # ])
28 | # def display_output(c):
29 | # return c[0]
30 |
31 |
32 | if __name__ == '__main__':
33 | app.run_server(debug=True)
34 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const packagejson = require('./package.json');
3 |
4 | const dashLibraryName = packagejson.name.replace(/-/g, '_');
5 |
6 | module.exports = {
7 | entry: {main: './src/lib/index.js'},
8 | output: {
9 | path: path.resolve(__dirname, dashLibraryName),
10 | filename: 'bundle.js',
11 | library: dashLibraryName,
12 | libraryTarget: 'window',
13 | },
14 | externals: {
15 | react: 'React',
16 | 'react-dom': 'ReactDOM',
17 | 'plotly.js': 'Plotly',
18 | },
19 | module: {
20 | rules: [
21 | {
22 | test: /\.js$/,
23 | exclude: /node_modules/,
24 | use: {
25 | loader: 'babel-loader',
26 | },
27 | },
28 | {
29 | test: /\.css$/,
30 | use: [
31 | {
32 | loader: 'style-loader',
33 | },
34 | {
35 | loader: 'css-loader',
36 | },
37 | ],
38 | },
39 | ],
40 | },
41 | };
42 |
--------------------------------------------------------------------------------
/webpack.serve.config.js:
--------------------------------------------------------------------------------
1 | const config = require('./webpack.config.js');
2 |
3 | config.entry = {main: './src/demo/index.js'};
4 | config.output = {filename: 'output.js'};
5 | config.mode = 'development';
6 | config.externals = undefined; // eslint-disable-line
7 | config.devtool = 'inline-source-map';
8 | module.exports = config;
9 |
--------------------------------------------------------------------------------